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

1.10      albertel    1: # The LearningOnline Network with CAPA
1.1       albertel    2: # a pile of common routines
1.10      albertel    3: #
1.1075.2.161.  .20(raeb    4:-23): # $Id: loncommon.pm,v 1.1075.2.161.2.19 2023/09/11 12:09: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.1075.2.161.  .7(raebu   64:22): use Apache::lonnavmaps();
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.1075.2.25  raeburn    70: use Apache::lonuserutils();
1.1075.2.27  raeburn    71: use Apache::lonuserstate();
1.1075.2.69  raeburn    72: use Apache::courseclassifier();
1.479     albertel   73: use LONCAPA qw(:DEFAULT :match);
1.1075.2.161.  .13(raeb   74:-23): use LONCAPA::map();
1.1075.2.135  raeburn    75: use HTTP::Request;
1.657     raeburn    76: use DateTime::TimeZone;
1.1075.2.102  raeburn    77: use DateTime::Locale;
1.1075.2.94  raeburn    78: use Encode();
1.1075.2.14  raeburn    79: use Authen::Captcha;
                     80: use Captcha::reCAPTCHA;
1.1075.2.107  raeburn    81: use JSON::DWIW;
                     82: use LWP::UserAgent;
1.1075.2.64  raeburn    83: use Crypt::DES;
                     84: use DynaLoader; # for Crypt::DES version
1.1075.2.128  raeburn    85: use File::Copy();
                     86: use File::Path();
1.1075.2.161.  .1(raebu   87:21): use String::CRC32();
                     88:21): use Short::URL();
1.117     www        89: 
1.517     raeburn    90: # ---------------------------------------------- Designs
                     91: use vars qw(%defaultdesign);
                     92: 
1.22      www        93: my $readit;
                     94: 
1.517     raeburn    95: 
1.157     matthew    96: ##
                     97: ## Global Variables
                     98: ##
1.46      matthew    99: 
1.643     foxr      100: 
                    101: # ----------------------------------------------- SSI with retries:
                    102: #
                    103: 
                    104: =pod
                    105: 
1.648     raeburn   106: =head1 Server Side include with retries:
1.643     foxr      107: 
                    108: =over 4
                    109: 
1.648     raeburn   110: =item * &ssi_with_retries(resource,retries form)
1.643     foxr      111: 
                    112: Performs an ssi with some number of retries.  Retries continue either
                    113: until the result is ok or until the retry count supplied by the
                    114: caller is exhausted.  
                    115: 
                    116: Inputs:
1.648     raeburn   117: 
                    118: =over 4
                    119: 
1.643     foxr      120: resource   - Identifies the resource to insert.
1.648     raeburn   121: 
1.643     foxr      122: retries    - Count of the number of retries allowed.
1.648     raeburn   123: 
1.643     foxr      124: form       - Hash that identifies the rendering options.
                    125: 
1.648     raeburn   126: =back
                    127: 
                    128: Returns:
                    129: 
                    130: =over 4
                    131: 
1.643     foxr      132: content    - The content of the response.  If retries were exhausted this is empty.
1.648     raeburn   133: 
1.643     foxr      134: response   - The response from the last attempt (which may or may not have been successful.
                    135: 
1.648     raeburn   136: =back
                    137: 
                    138: =back
                    139: 
1.643     foxr      140: =cut
                    141: 
                    142: sub ssi_with_retries {
                    143:     my ($resource, $retries, %form) = @_;
                    144: 
                    145: 
                    146:     my $ok = 0;			# True if we got a good response.
                    147:     my $content;
                    148:     my $response;
                    149: 
                    150:     # Try to get the ssi done. within the retries count:
                    151: 
                    152:     do {
                    153: 	($content, $response) = &Apache::lonnet::ssi($resource, %form);
                    154: 	$ok      = $response->is_success;
1.650     www       155:         if (!$ok) {
                    156:             &Apache::lonnet::logthis("Failed ssi_with_retries on $resource: ".$response->is_success.', '.$response->code.', '.$response->message);
                    157:         }
1.643     foxr      158: 	$retries--;
                    159:     } while (!$ok && ($retries > 0));
                    160: 
                    161:     if (!$ok) {
                    162: 	$content = '';		# On error return an empty content.
                    163:     }
                    164:     return ($content, $response);
                    165: 
                    166: }
                    167: 
                    168: 
                    169: 
1.20      www       170: # ----------------------------------------------- Filetypes/Languages/Copyright
1.12      harris41  171: my %language;
1.124     www       172: my %supported_language;
1.1048    foxr      173: my %latex_language;		# For choosing hyphenation in <transl..>
                    174: my %latex_language_bykey;	# for choosing hyphenation from metadata
1.12      harris41  175: my %cprtag;
1.192     taceyjo1  176: my %scprtag;
1.351     www       177: my %fe; my %fd; my %fm;
1.41      ng        178: my %category_extensions;
1.12      harris41  179: 
1.46      matthew   180: # ---------------------------------------------- Thesaurus variables
1.144     matthew   181: #
                    182: # %Keywords:
                    183: #      A hash used by &keyword to determine if a word is considered a keyword.
                    184: # $thesaurus_db_file 
                    185: #      Scalar containing the full path to the thesaurus database.
1.46      matthew   186: 
                    187: my %Keywords;
                    188: my $thesaurus_db_file;
                    189: 
1.144     matthew   190: #
                    191: # Initialize values from language.tab, copyright.tab, filetypes.tab,
                    192: # thesaurus.tab, and filecategories.tab.
                    193: #
1.18      www       194: BEGIN {
1.46      matthew   195:     # Variable initialization
                    196:     $thesaurus_db_file = $Apache::lonnet::perlvar{'lonTabDir'}."/thesaurus.db";
                    197:     #
1.22      www       198:     unless ($readit) {
1.12      harris41  199: # ------------------------------------------------------------------- languages
                    200:     {
1.158     raeburn   201:         my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    202:                                    '/language.tab';
1.1075.2.128  raeburn   203:         if ( open(my $fh,'<',$langtabfile) ) {
1.356     albertel  204:             while (my $line = <$fh>) {
                    205:                 next if ($line=~/^\#/);
                    206:                 chomp($line);
1.1048    foxr      207:                 my ($key,$two,$country,$three,$enc,$val,$sup,$latex)=(split(/\t/,$line));
1.158     raeburn   208:                 $language{$key}=$val.' - '.$enc;
                    209:                 if ($sup) {
                    210:                     $supported_language{$key}=$sup;
                    211:                 }
1.1048    foxr      212: 		if ($latex) {
                    213: 		    $latex_language_bykey{$key} = $latex;
                    214: 		    $latex_language{$two} = $latex;
                    215: 		}
1.158     raeburn   216:             }
                    217:             close($fh);
                    218:         }
1.12      harris41  219:     }
                    220: # ------------------------------------------------------------------ copyrights
                    221:     {
1.158     raeburn   222:         my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                    223:                                   '/copyright.tab';
1.1075.2.128  raeburn   224:         if ( open (my $fh,'<',$copyrightfile) ) {
1.356     albertel  225:             while (my $line = <$fh>) {
                    226:                 next if ($line=~/^\#/);
                    227:                 chomp($line);
                    228:                 my ($key,$val)=(split(/\s+/,$line,2));
1.158     raeburn   229:                 $cprtag{$key}=$val;
                    230:             }
                    231:             close($fh);
                    232:         }
1.12      harris41  233:     }
1.351     www       234: # ----------------------------------------------------------- source copyrights
1.192     taceyjo1  235:     {
                    236:         my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                    237:                                   '/source_copyright.tab';
1.1075.2.128  raeburn   238:         if ( open (my $fh,'<',$sourcecopyrightfile) ) {
1.356     albertel  239:             while (my $line = <$fh>) {
                    240:                 next if ($line =~ /^\#/);
                    241:                 chomp($line);
                    242:                 my ($key,$val)=(split(/\s+/,$line,2));
1.192     taceyjo1  243:                 $scprtag{$key}=$val;
                    244:             }
                    245:             close($fh);
                    246:         }
                    247:     }
1.63      www       248: 
1.517     raeburn   249: # -------------------------------------------------------------- default domain designs
1.63      www       250:     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
1.517     raeburn   251:     my $designfile = $designdir.'/default.tab';
1.1075.2.128  raeburn   252:     if ( open (my $fh,'<',$designfile) ) {
1.517     raeburn   253:         while (my $line = <$fh>) {
                    254:             next if ($line =~ /^\#/);
                    255:             chomp($line);
                    256:             my ($key,$val)=(split(/\=/,$line));
                    257:             if ($val) { $defaultdesign{$key}=$val; }
                    258:         }
                    259:         close($fh);
1.63      www       260:     }
                    261: 
1.15      harris41  262: # ------------------------------------------------------------- file categories
                    263:     {
1.158     raeburn   264:         my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    265:                                   '/filecategories.tab';
1.1075.2.128  raeburn   266:         if ( open (my $fh,'<',$categoryfile) ) {
1.356     albertel  267: 	    while (my $line = <$fh>) {
                    268: 		next if ($line =~ /^\#/);
                    269: 		chomp($line);
                    270:                 my ($extension,$category)=(split(/\s+/,$line,2));
1.1075.2.119  raeburn   271:                 push(@{$category_extensions{lc($category)}},$extension);
1.158     raeburn   272:             }
                    273:             close($fh);
                    274:         }
                    275: 
1.15      harris41  276:     }
1.12      harris41  277: # ------------------------------------------------------------------ file types
                    278:     {
1.158     raeburn   279:         my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    280:                '/filetypes.tab';
1.1075.2.128  raeburn   281:         if ( open (my $fh,'<',$typesfile) ) {
1.356     albertel  282:             while (my $line = <$fh>) {
                    283: 		next if ($line =~ /^\#/);
                    284: 		chomp($line);
                    285:                 my ($ending,$emb,$mime,$descr)=split(/\s+/,$line,4);
1.158     raeburn   286:                 if ($descr ne '') {
                    287:                     $fe{$ending}=lc($emb);
                    288:                     $fd{$ending}=$descr;
1.351     www       289:                     if ($mime ne 'unk') { $fm{$ending}=$mime; }
1.158     raeburn   290:                 }
                    291:             }
                    292:             close($fh);
                    293:         }
1.12      harris41  294:     }
1.22      www       295:     &Apache::lonnet::logthis(
1.705     tempelho  296:              "<span style='color:yellow;'>INFO: Read file types</span>");
1.22      www       297:     $readit=1;
1.46      matthew   298:     }  # end of unless($readit) 
1.32      matthew   299:     
                    300: }
1.112     bowersj2  301: 
1.42      matthew   302: ###############################################################
                    303: ##           HTML and Javascript Helper Functions            ##
                    304: ###############################################################
                    305: 
                    306: =pod 
                    307: 
1.112     bowersj2  308: =head1 HTML and Javascript Functions
1.42      matthew   309: 
1.112     bowersj2  310: =over 4
                    311: 
1.648     raeburn   312: =item * &browser_and_searcher_javascript()
1.112     bowersj2  313: 
                    314: X<browsing, javascript>X<searching, javascript>Returns a string
                    315: containing javascript with two functions, C<openbrowser> and
                    316: C<opensearcher>. Returned string does not contain E<lt>scriptE<gt>
                    317: tags.
1.42      matthew   318: 
1.648     raeburn   319: =item * &openbrowser(formname,elementname,only,omit) [javascript]
1.42      matthew   320: 
                    321: inputs: formname, elementname, only, omit
                    322: 
                    323: formname and elementname indicate the name of the html form and name of
                    324: the element that the results of the browsing selection are to be placed in. 
                    325: 
                    326: Specifying 'only' will restrict the browser to displaying only files
1.185     www       327: with the given extension.  Can be a comma separated list.
1.42      matthew   328: 
                    329: Specifying 'omit' will restrict the browser to NOT displaying files
1.185     www       330: with the given extension.  Can be a comma separated list.
1.42      matthew   331: 
1.648     raeburn   332: =item * &opensearcher(formname,elementname) [javascript]
1.42      matthew   333: 
                    334: Inputs: formname, elementname
                    335: 
                    336: formname and elementname specify the name of the html form and the name
                    337: of the element the selection from the search results will be placed in.
1.542     raeburn   338: 
1.42      matthew   339: =cut
                    340: 
                    341: sub browser_and_searcher_javascript {
1.199     albertel  342:     my ($mode)=@_;
                    343:     if (!defined($mode)) { $mode='edit'; }
1.453     albertel  344:     my $resurl=&escape_single(&lastresurl());
1.42      matthew   345:     return <<END;
1.219     albertel  346: // <!-- BEGIN LON-CAPA Internal
1.50      matthew   347:     var editbrowser = null;
1.135     albertel  348:     function openbrowser(formname,elementname,only,omit,titleelement) {
1.170     www       349:         var url = '$resurl/?';
1.42      matthew   350:         if (editbrowser == null) {
                    351:             url += 'launch=1&';
                    352:         }
                    353:         url += 'catalogmode=interactive&';
1.199     albertel  354:         url += 'mode=$mode&';
1.611     albertel  355:         url += 'inhibitmenu=yes&';
1.42      matthew   356:         url += 'form=' + formname + '&';
                    357:         if (only != null) {
                    358:             url += 'only=' + only + '&';
1.217     albertel  359:         } else {
                    360:             url += 'only=&';
                    361: 	}
1.42      matthew   362:         if (omit != null) {
                    363:             url += 'omit=' + omit + '&';
1.217     albertel  364:         } else {
                    365:             url += 'omit=&';
                    366: 	}
1.135     albertel  367:         if (titleelement != null) {
                    368:             url += 'titleelement=' + titleelement + '&';
1.217     albertel  369:         } else {
                    370: 	    url += 'titleelement=&';
                    371: 	}
1.42      matthew   372:         url += 'element=' + elementname + '';
                    373:         var title = 'Browser';
1.435     albertel  374:         var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42      matthew   375:         options += ',width=700,height=600';
                    376:         editbrowser = open(url,title,options,'1');
                    377:         editbrowser.focus();
                    378:     }
                    379:     var editsearcher;
1.135     albertel  380:     function opensearcher(formname,elementname,titleelement) {
1.42      matthew   381:         var url = '/adm/searchcat?';
                    382:         if (editsearcher == null) {
                    383:             url += 'launch=1&';
                    384:         }
                    385:         url += 'catalogmode=interactive&';
1.199     albertel  386:         url += 'mode=$mode&';
1.42      matthew   387:         url += 'form=' + formname + '&';
1.135     albertel  388:         if (titleelement != null) {
                    389:             url += 'titleelement=' + titleelement + '&';
1.217     albertel  390:         } else {
                    391: 	    url += 'titleelement=&';
                    392: 	}
1.42      matthew   393:         url += 'element=' + elementname + '';
                    394:         var title = 'Search';
1.435     albertel  395:         var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42      matthew   396:         options += ',width=700,height=600';
                    397:         editsearcher = open(url,title,options,'1');
                    398:         editsearcher.focus();
                    399:     }
1.219     albertel  400: // END LON-CAPA Internal -->
1.42      matthew   401: END
1.170     www       402: }
                    403: 
                    404: sub lastresurl {
1.258     albertel  405:     if ($env{'environment.lastresurl'}) {
                    406: 	return $env{'environment.lastresurl'}
1.170     www       407:     } else {
                    408: 	return '/res';
                    409:     }
                    410: }
                    411: 
                    412: sub storeresurl {
                    413:     my $resurl=&Apache::lonnet::clutter(shift);
                    414:     unless ($resurl=~/^\/res/) { return 0; }
                    415:     $resurl=~s/\/$//;
                    416:     &Apache::lonnet::put('environment',{'lastresurl' => $resurl});
1.646     raeburn   417:     &Apache::lonnet::appenv({'environment.lastresurl' => $resurl});
1.170     www       418:     return 1;
1.42      matthew   419: }
                    420: 
1.74      www       421: sub studentbrowser_javascript {
1.111     www       422:    unless (
1.258     albertel  423:             (($env{'request.course.id'}) && 
1.302     albertel  424:              (&Apache::lonnet::allowed('srm',$env{'request.course.id'})
                    425: 	      || &Apache::lonnet::allowed('srm',$env{'request.course.id'}.
                    426: 					  '/'.$env{'request.course.sec'})
                    427: 	      ))
1.258     albertel  428:          || ($env{'request.role'}=~/^(au|dc|su)/)
1.111     www       429:           ) { return ''; }  
1.74      www       430:    return (<<'ENDSTDBRW');
1.776     bisitz    431: <script type="text/javascript" language="Javascript">
1.824     bisitz    432: // <![CDATA[
1.74      www       433:     var stdeditbrowser;
1.1075.2.161.  .20(raeb  434:-23):     function openstdbrowser(formname,uname,udom,clicker,roleflag,ignorefilter,courseadv,uident) {
1.74      www       435:         var url = '/adm/pickstudent?';
                    436:         var filter;
1.558     albertel  437: 	if (!ignorefilter) {
                    438: 	    eval('filter=document.'+formname+'.'+uname+'.value;');
                    439: 	}
1.74      www       440:         if (filter != null) {
                    441:            if (filter != '') {
                    442:                url += 'filter='+filter+'&';
                    443: 	   }
                    444:         }
                    445:         url += 'form=' + formname + '&unameelement='+uname+
1.999     www       446:                                     '&udomelement='+udom+
                    447:                                     '&clicker='+clicker;
1.111     www       448: 	if (roleflag) { url+="&roles=1"; }
1.1075.2.143  raeburn   449:         if (courseadv == 'condition') {
                    450:             if (document.getElementById('courseadv')) {
                    451:                 courseadv = document.getElementById('courseadv').value;
                    452:             }
                    453:         }
                    454:         if ((courseadv == 'only') || (courseadv == 'none')) { url+="&courseadv="+courseadv; }
1.1075.2.161.  .20(raeb  455:-23):         if (uident !== '') { url+="&identelement="+uident; }
1.102     www       456:         var title = 'Student_Browser';
1.74      www       457:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    458:         options += ',width=700,height=600';
                    459:         stdeditbrowser = open(url,title,options,'1');
                    460:         stdeditbrowser.focus();
                    461:     }
1.824     bisitz    462: // ]]>
1.74      www       463: </script>
                    464: ENDSTDBRW
                    465: }
1.42      matthew   466: 
1.1003    www       467: sub resourcebrowser_javascript {
                    468:    unless ($env{'request.course.id'}) { return ''; }
1.1004    www       469:    return (<<'ENDRESBRW');
1.1003    www       470: <script type="text/javascript" language="Javascript">
                    471: // <![CDATA[
                    472:     var reseditbrowser;
1.1004    www       473:     function openresbrowser(formname,reslink) {
1.1005    www       474:         var url = '/adm/pickresource?form='+formname+'&reslink='+reslink;
1.1003    www       475:         var title = 'Resource_Browser';
                    476:         var options = 'scrollbars=1,resizable=1,menubar=0';
1.1005    www       477:         options += ',width=700,height=500';
1.1004    www       478:         reseditbrowser = open(url,title,options,'1');
                    479:         reseditbrowser.focus();
1.1003    www       480:     }
                    481: // ]]>
                    482: </script>
1.1004    www       483: ENDRESBRW
1.1003    www       484: }
                    485: 
1.74      www       486: sub selectstudent_link {
1.1075.2.161.  .20(raeb  487:-23):    my ($form,$unameele,$udomele,$courseadv,$clickerid,$identelem)=@_;
1.999     www       488:    my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
                    489:                       &Apache::lonhtmlcommon::entity_encode($unameele)."','".
                    490:                       &Apache::lonhtmlcommon::entity_encode($udomele)."'";
1.258     albertel  491:    if ($env{'request.course.id'}) {  
1.302     albertel  492:        if (!&Apache::lonnet::allowed('srm',$env{'request.course.id'})
                    493: 	   && !&Apache::lonnet::allowed('srm',$env{'request.course.id'}.
                    494: 					'/'.$env{'request.course.sec'})) {
1.111     www       495: 	   return '';
                    496:        }
1.999     www       497:        $callargs.=",'".&Apache::lonhtmlcommon::entity_encode($clickerid)."'";
1.1075.2.143  raeburn   498:        if ($courseadv eq 'only') {
                    499:            $callargs .= ",'',1,'$courseadv'";
                    500:        } elsif ($courseadv eq 'none') {
                    501:            $callargs .= ",'','','$courseadv'";
                    502:        } elsif ($courseadv eq 'condition') {
                    503:            $callargs .= ",'','','$courseadv'";
1.1075.2.161.  .20(raeb  504:-23):        } elsif ($identelem ne '') {
                    505:-23):            $callargs .= ",'','',''";
                    506:-23):        }
                    507:-23):        if ($identelem ne '') {
                    508:-23):            $callargs .= ",'".&Apache::lonhtmlcommon::entity_encode($identelem)."'";
1.793     raeburn   509:        }
                    510:        return '<span class="LC_nobreak">'.
                    511:               '<a href="javascript:openstdbrowser('.$callargs.');">'.
                    512:               &mt('Select User').'</a></span>';
1.74      www       513:    }
1.258     albertel  514:    if ($env{'request.role'}=~/^(au|dc|su)/) {
1.1012    www       515:        $callargs .= ",'',1"; 
1.793     raeburn   516:        return '<span class="LC_nobreak">'.
                    517:               '<a href="javascript:openstdbrowser('.$callargs.');">'.
                    518:               &mt('Select User').'</a></span>';
1.111     www       519:    }
                    520:    return '';
1.91      www       521: }
                    522: 
1.1004    www       523: sub selectresource_link {
                    524:    my ($form,$reslink,$arg)=@_;
                    525:    
                    526:    my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
                    527:                       &Apache::lonhtmlcommon::entity_encode($reslink)."'";
                    528:    unless ($env{'request.course.id'}) { return $arg; }
                    529:    return '<span class="LC_nobreak">'.
                    530:               '<a href="javascript:openresbrowser('.$callargs.');">'.
                    531:               $arg.'</a></span>';
                    532: }
                    533: 
                    534: 
                    535: 
1.653     raeburn   536: sub authorbrowser_javascript {
                    537:     return <<"ENDAUTHORBRW";
1.776     bisitz    538: <script type="text/javascript" language="JavaScript">
1.824     bisitz    539: // <![CDATA[
1.653     raeburn   540: var stdeditbrowser;
                    541: 
                    542: function openauthorbrowser(formname,udom) {
                    543:     var url = '/adm/pickauthor?';
                    544:     url += 'form='+formname+'&roledom='+udom;
                    545:     var title = 'Author_Browser';
                    546:     var options = 'scrollbars=1,resizable=1,menubar=0';
                    547:     options += ',width=700,height=600';
                    548:     stdeditbrowser = open(url,title,options,'1');
                    549:     stdeditbrowser.focus();
                    550: }
                    551: 
1.824     bisitz    552: // ]]>
1.653     raeburn   553: </script>
                    554: ENDAUTHORBRW
                    555: }
                    556: 
1.91      www       557: sub coursebrowser_javascript {
1.1075.2.31  raeburn   558:     my ($domainfilter,$sec_element,$formname,$role_element,$crstype,
1.1075.2.95  raeburn   559:         $credits_element,$instcode) = @_;
1.932     raeburn   560:     my $wintitle = 'Course_Browser';
1.931     raeburn   561:     if ($crstype eq 'Community') {
1.932     raeburn   562:         $wintitle = 'Community_Browser';
1.909     raeburn   563:     }
1.876     raeburn   564:     my $id_functions = &javascript_index_functions();
                    565:     my $output = '
1.776     bisitz    566: <script type="text/javascript" language="JavaScript">
1.824     bisitz    567: // <![CDATA[
1.468     raeburn   568:     var stdeditbrowser;'."\n";
1.876     raeburn   569: 
                    570:     $output .= <<"ENDSTDBRW";
1.909     raeburn   571:     function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,type,type_elem) {
1.91      www       572:         var url = '/adm/pickcourse?';
1.895     raeburn   573:         var formid = getFormIdByName(formname);
1.876     raeburn   574:         var domainfilter = getDomainFromSelectbox(formname,udom);
1.128     albertel  575:         if (domainfilter != null) {
                    576:            if (domainfilter != '') {
                    577:                url += 'domainfilter='+domainfilter+'&';
                    578: 	   }
                    579:         }
1.91      www       580:         url += 'form=' + formname + '&cnumelement='+uname+
1.187     albertel  581: 	                            '&cdomelement='+udom+
                    582:                                     '&cnameelement='+desc;
1.468     raeburn   583:         if (extra_element !=null && extra_element != '') {
1.594     raeburn   584:             if (formname == 'rolechoice' || formname == 'studentform') {
1.468     raeburn   585:                 url += '&roleelement='+extra_element;
                    586:                 if (domainfilter == null || domainfilter == '') {
                    587:                     url += '&domainfilter='+extra_element;
                    588:                 }
1.234     raeburn   589:             }
1.468     raeburn   590:             else {
                    591:                 if (formname == 'portform') {
                    592:                     url += '&setroles='+extra_element;
1.800     raeburn   593:                 } else {
                    594:                     if (formname == 'rules') {
                    595:                         url += '&fixeddom='+extra_element; 
                    596:                     }
1.468     raeburn   597:                 }
                    598:             }     
1.230     raeburn   599:         }
1.909     raeburn   600:         if (type != null && type != '') {
                    601:             url += '&type='+type;
                    602:         }
                    603:         if (type_elem != null && type_elem != '') {
                    604:             url += '&typeelement='+type_elem;
                    605:         }
1.872     raeburn   606:         if (formname == 'ccrs') {
                    607:             var ownername = document.forms[formid].ccuname.value;
                    608:             var ownerdom =  document.forms[formid].ccdomain.options[document.forms[formid].ccdomain.selectedIndex].value;
1.1075.2.101  raeburn   609:             url += '&cloner='+ownername+':'+ownerdom;
                    610:             if (type == 'Course') {
                    611:                 url += '&crscode='+document.forms[formid].crscode.value;
                    612:             }
1.1075.2.95  raeburn   613:         }
                    614:         if (formname == 'requestcrs') {
                    615:             url += '&crsdom=$domainfilter&crscode=$instcode';
1.872     raeburn   616:         }
1.293     raeburn   617:         if (multflag !=null && multflag != '') {
                    618:             url += '&multiple='+multflag;
                    619:         }
1.909     raeburn   620:         var title = '$wintitle';
1.91      www       621:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    622:         options += ',width=700,height=600';
                    623:         stdeditbrowser = open(url,title,options,'1');
                    624:         stdeditbrowser.focus();
                    625:     }
1.876     raeburn   626: $id_functions
                    627: ENDSTDBRW
1.1075.2.31  raeburn   628:     if (($sec_element ne '') || ($role_element ne '') || ($credits_element ne '')) {
                    629:         $output .= &setsec_javascript($sec_element,$formname,$role_element,
                    630:                                       $credits_element);
1.876     raeburn   631:     }
                    632:     $output .= '
                    633: // ]]>
                    634: </script>';
                    635:     return $output;
                    636: }
                    637: 
                    638: sub javascript_index_functions {
                    639:     return <<"ENDJS";
                    640: 
                    641: function getFormIdByName(formname) {
                    642:     for (var i=0;i<document.forms.length;i++) {
                    643:         if (document.forms[i].name == formname) {
                    644:             return i;
                    645:         }
                    646:     }
                    647:     return -1;
                    648: }
                    649: 
                    650: function getIndexByName(formid,item) {
                    651:     for (var i=0;i<document.forms[formid].elements.length;i++) {
                    652:         if (document.forms[formid].elements[i].name == item) {
                    653:             return i;
                    654:         }
                    655:     }
                    656:     return -1;
                    657: }
1.468     raeburn   658: 
1.876     raeburn   659: function getDomainFromSelectbox(formname,udom) {
                    660:     var userdom;
                    661:     var formid = getFormIdByName(formname);
                    662:     if (formid > -1) {
                    663:         var domid = getIndexByName(formid,udom);
                    664:         if (domid > -1) {
                    665:             if (document.forms[formid].elements[domid].type == 'select-one') {
                    666:                 userdom=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
                    667:             }
                    668:             if (document.forms[formid].elements[domid].type == 'hidden') {
                    669:                 userdom=document.forms[formid].elements[domid].value;
1.468     raeburn   670:             }
                    671:         }
                    672:     }
1.876     raeburn   673:     return userdom;
                    674: }
                    675: 
                    676: ENDJS
1.468     raeburn   677: 
1.876     raeburn   678: }
                    679: 
1.1017    raeburn   680: sub javascript_array_indexof {
1.1018    raeburn   681:     return <<ENDJS;
1.1017    raeburn   682: <script type="text/javascript" language="JavaScript">
                    683: // <![CDATA[
                    684: 
                    685: if (!Array.prototype.indexOf) {
                    686:     Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
                    687:         "use strict";
                    688:         if (this === void 0 || this === null) {
                    689:             throw new TypeError();
                    690:         }
                    691:         var t = Object(this);
                    692:         var len = t.length >>> 0;
                    693:         if (len === 0) {
                    694:             return -1;
                    695:         }
                    696:         var n = 0;
                    697:         if (arguments.length > 0) {
                    698:             n = Number(arguments[1]);
                    699:             if (n !== n) { // shortcut for verifying if it's NaN
                    700:                 n = 0;
                    701:             } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
                    702:                 n = (n > 0 || -1) * Math.floor(Math.abs(n));
                    703:             }
                    704:         }
                    705:         if (n >= len) {
                    706:             return -1;
                    707:         }
                    708:         var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
                    709:         for (; k < len; k++) {
                    710:             if (k in t && t[k] === searchElement) {
                    711:                 return k;
                    712:             }
                    713:         }
                    714:         return -1;
                    715:     }
                    716: }
                    717: 
                    718: // ]]>
                    719: </script>
                    720: 
                    721: ENDJS
                    722: 
                    723: }
                    724: 
1.876     raeburn   725: sub userbrowser_javascript {
                    726:     my $id_functions = &javascript_index_functions();
                    727:     return <<"ENDUSERBRW";
                    728: 
1.888     raeburn   729: function openuserbrowser(formname,uname,udom,ulast,ufirst,uemail,hideudom,crsdom,caller) {
1.876     raeburn   730:     var url = '/adm/pickuser?';
                    731:     var userdom = getDomainFromSelectbox(formname,udom);
                    732:     if (userdom != null) {
                    733:        if (userdom != '') {
                    734:            url += 'srchdom='+userdom+'&';
                    735:        }
                    736:     }
                    737:     url += 'form=' + formname + '&unameelement='+uname+
                    738:                                 '&udomelement='+udom+
                    739:                                 '&ulastelement='+ulast+
                    740:                                 '&ufirstelement='+ufirst+
                    741:                                 '&uemailelement='+uemail+
1.881     raeburn   742:                                 '&hideudomelement='+hideudom+
                    743:                                 '&coursedom='+crsdom;
1.888     raeburn   744:     if ((caller != null) && (caller != undefined)) {
                    745:         url += '&caller='+caller;
                    746:     }
1.876     raeburn   747:     var title = 'User_Browser';
                    748:     var options = 'scrollbars=1,resizable=1,menubar=0';
                    749:     options += ',width=700,height=600';
                    750:     var stdeditbrowser = open(url,title,options,'1');
                    751:     stdeditbrowser.focus();
                    752: }
                    753: 
1.888     raeburn   754: function fix_domain (formname,udom,origdom,uname) {
1.876     raeburn   755:     var formid = getFormIdByName(formname);
                    756:     if (formid > -1) {
1.888     raeburn   757:         var unameid = getIndexByName(formid,uname);
1.876     raeburn   758:         var domid = getIndexByName(formid,udom);
                    759:         var hidedomid = getIndexByName(formid,origdom);
                    760:         if (hidedomid > -1) {
                    761:             var fixeddom = document.forms[formid].elements[hidedomid].value;
1.888     raeburn   762:             var unameval = document.forms[formid].elements[unameid].value;
                    763:             if ((fixeddom != '') && (fixeddom != undefined) && (fixeddom != null) && (unameval != '') && (unameval != undefined) && (unameval != null)) {
                    764:                 if (domid > -1) {
                    765:                     var slct = document.forms[formid].elements[domid];
                    766:                     if (slct.type == 'select-one') {
                    767:                         var i;
                    768:                         for (i=0;i<slct.length;i++) {
                    769:                             if (slct.options[i].value==fixeddom) { slct.selectedIndex=i; }
                    770:                         }
                    771:                     }
                    772:                     if (slct.type == 'hidden') {
                    773:                         slct.value = fixeddom;
1.876     raeburn   774:                     }
                    775:                 }
1.468     raeburn   776:             }
                    777:         }
                    778:     }
1.876     raeburn   779:     return;
                    780: }
                    781: 
                    782: $id_functions
                    783: ENDUSERBRW
1.468     raeburn   784: }
                    785: 
                    786: sub setsec_javascript {
1.1075.2.31  raeburn   787:     my ($sec_element,$formname,$role_element,$credits_element) = @_;
1.905     raeburn   788:     my (@courserolenames,@communityrolenames,$rolestr,$courserolestr,
                    789:         $communityrolestr);
                    790:     if ($role_element ne '') {
                    791:         my @allroles = ('st','ta','ep','in','ad');
                    792:         foreach my $crstype ('Course','Community') {
                    793:             if ($crstype eq 'Community') {
                    794:                 foreach my $role (@allroles) {
                    795:                     push(@communityrolenames,&Apache::lonnet::plaintext($role,$crstype));
                    796:                 }
                    797:                 push(@communityrolenames,&Apache::lonnet::plaintext('co'));
                    798:             } else {
                    799:                 foreach my $role (@allroles) {
                    800:                     push(@courserolenames,&Apache::lonnet::plaintext($role,$crstype));
                    801:                 }
                    802:                 push(@courserolenames,&Apache::lonnet::plaintext('cc'));
                    803:             }
                    804:         }
                    805:         $rolestr = '"'.join('","',@allroles).'"';
                    806:         $courserolestr = '"'.join('","',@courserolenames).'"';
                    807:         $communityrolestr = '"'.join('","',@communityrolenames).'"';
                    808:     }
1.468     raeburn   809:     my $setsections = qq|
                    810: function setSect(sectionlist) {
1.629     raeburn   811:     var sectionsArray = new Array();
                    812:     if ((sectionlist != '') && (typeof sectionlist != "undefined")) {
                    813:         sectionsArray = sectionlist.split(",");
                    814:     }
1.468     raeburn   815:     var numSections = sectionsArray.length;
                    816:     document.$formname.$sec_element.length = 0;
                    817:     if (numSections == 0) {
                    818:         document.$formname.$sec_element.multiple=false;
                    819:         document.$formname.$sec_element.size=1;
                    820:         document.$formname.$sec_element.options[0] = new Option('No existing sections','',false,false)
                    821:     } else {
                    822:         if (numSections == 1) {
                    823:             document.$formname.$sec_element.multiple=false;
                    824:             document.$formname.$sec_element.size=1;
                    825:             document.$formname.$sec_element.options[0] = new Option('Select','',true,true);
                    826:             document.$formname.$sec_element.options[1] = new Option('No section','',false,false)
                    827:             document.$formname.$sec_element.options[2] = new Option(sectionsArray[0],sectionsArray[0],false,false);
                    828:         } else {
                    829:             for (var i=0; i<numSections; i++) {
                    830:                 document.$formname.$sec_element.options[i] = new Option(sectionsArray[i],sectionsArray[i],false,false)
                    831:             }
                    832:             document.$formname.$sec_element.multiple=true
                    833:             if (numSections < 3) {
                    834:                 document.$formname.$sec_element.size=numSections;
                    835:             } else {
                    836:                 document.$formname.$sec_element.size=3;
                    837:             }
                    838:             document.$formname.$sec_element.options[0].selected = false
                    839:         }
                    840:     }
1.91      www       841: }
1.905     raeburn   842: 
                    843: function setRole(crstype) {
1.468     raeburn   844: |;
1.905     raeburn   845:     if ($role_element eq '') {
                    846:         $setsections .= '    return;
                    847: }
                    848: ';
                    849:     } else {
                    850:         $setsections .= qq|
                    851:     var elementLength = document.$formname.$role_element.length;
                    852:     var allroles = Array($rolestr);
                    853:     var courserolenames = Array($courserolestr);
                    854:     var communityrolenames = Array($communityrolestr);
                    855:     if (elementLength != undefined) {
                    856:         if (document.$formname.$role_element.options[5].value == 'cc') {
                    857:             if (crstype == 'Course') {
                    858:                 return;
                    859:             } else {
                    860:                 allroles[5] = 'co';
                    861:                 for (var i=0; i<6; i++) {
                    862:                     document.$formname.$role_element.options[i].value = allroles[i];
                    863:                     document.$formname.$role_element.options[i].text = communityrolenames[i];
                    864:                 }
                    865:             }
                    866:         } else {
                    867:             if (crstype == 'Community') {
                    868:                 return;
                    869:             } else {
                    870:                 allroles[5] = 'cc';
                    871:                 for (var i=0; i<6; i++) {
                    872:                     document.$formname.$role_element.options[i].value = allroles[i];
                    873:                     document.$formname.$role_element.options[i].text = courserolenames[i];
                    874:                 }
                    875:             }
                    876:         }
                    877:     }
                    878:     return;
                    879: }
                    880: |;
                    881:     }
1.1075.2.31  raeburn   882:     if ($credits_element) {
                    883:         $setsections .= qq|
                    884: function setCredits(defaultcredits) {
                    885:     document.$formname.$credits_element.value = defaultcredits;
                    886:     return;
                    887: }
                    888: |;
                    889:     }
1.468     raeburn   890:     return $setsections;
                    891: }
                    892: 
1.91      www       893: sub selectcourse_link {
1.909     raeburn   894:    my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype,
                    895:        $typeelement) = @_;
                    896:    my $type = $selecttype;
1.871     raeburn   897:    my $linktext = &mt('Select Course');
                    898:    if ($selecttype eq 'Community') {
1.909     raeburn   899:        $linktext = &mt('Select Community');
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.1075.2.14  raeburn   935:             if (!field[i].disabled) {
                    936:                 field[i].checked = true;
                    937:             }
1.273     raeburn   938:         }
                    939:     } else {
1.1075.2.14  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.1075.2.161.  .10(raeb  960:-22):    my ($name,$selected,$onchange,$includeempty,$id,$disabled)=@_;
                    961:-22):    my $output='<select name="'.$name.'" '.$id.$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.1075.2.115  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.1075.2.102  raeburn   991:     my @languages = &Apache::lonlocal::preferred_languages();
1.687     raeburn   992:     my (@possibles,%locale_names);
1.1075.2.102  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.1075.2.94  raeburn  1014:                 $locale_names{$id} = Encode::encode('UTF-8',$locale_names{$id});
1.1075.2.102  raeburn  1015:                 push(@possibles,$id);
1.687     raeburn  1016:             }
                   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.1075.2.94  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.1075.2.115  raeburn  1035:     my ($name,$selected,$includeempty,$noedit) = @_;
1.792     raeburn  1036:     my %langchoices;
                   1037:     if ($includeempty) {
1.1075.2.32  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.1075.2.32  raeburn  1046:     %langchoices = &Apache::lonlocal::texthash(%langchoices);
1.1075.2.115  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.648     raeburn  1052: =item * &linked_select_forms(...)
1.36      matthew  1053: 
                   1054: linked_select_forms returns a string containing a <script></script> block
                   1055: and html for two <select> menus.  The select menus will be linked in that
                   1056: changing the value of the first menu will result in new values being placed
                   1057: in the second menu.  The values in the select menu will appear in alphabetical
1.609     raeburn  1058: order unless a defined order is provided.
1.36      matthew  1059: 
                   1060: linked_select_forms takes the following ordered inputs:
                   1061: 
                   1062: =over 4
                   1063: 
1.112     bowersj2 1064: =item * $formname, the name of the <form> tag
1.36      matthew  1065: 
1.112     bowersj2 1066: =item * $middletext, the text which appears between the <select> tags
1.36      matthew  1067: 
1.112     bowersj2 1068: =item * $firstdefault, the default value for the first menu
1.36      matthew  1069: 
1.112     bowersj2 1070: =item * $firstselectname, the name of the first <select> tag
1.36      matthew  1071: 
1.112     bowersj2 1072: =item * $secondselectname, the name of the second <select> tag
1.36      matthew  1073: 
1.112     bowersj2 1074: =item * $hashref, a reference to a hash containing the data for the menus.
1.36      matthew  1075: 
1.609     raeburn  1076: =item * $menuorder, the order of values in the first menu
                   1077: 
1.1075.2.31  raeburn  1078: =item * $onchangefirst, additional javascript call to execute for an onchange
                   1079:         event for the first <select> tag
                   1080: 
                   1081: =item * $onchangesecond, additional javascript call to execute for an onchange
                   1082:         event for the second <select> tag
                   1083: 
1.41      ng       1084: =back 
                   1085: 
1.36      matthew  1086: Below is an example of such a hash.  Only the 'text', 'default', and 
                   1087: 'select2' keys must appear as stated.  keys(%menu) are the possible 
                   1088: values for the first select menu.  The text that coincides with the 
1.41      ng       1089: first menu value is given in $menu{$choice1}->{'text'}.  The values 
1.36      matthew  1090: and text for the second menu are given in the hash pointed to by 
                   1091: $menu{$choice1}->{'select2'}.  
                   1092: 
1.112     bowersj2 1093:  my %menu = ( A1 => { text =>"Choice A1" ,
                   1094:                        default => "B3",
                   1095:                        select2 => { 
                   1096:                            B1 => "Choice B1",
                   1097:                            B2 => "Choice B2",
                   1098:                            B3 => "Choice B3",
                   1099:                            B4 => "Choice B4"
1.609     raeburn  1100:                            },
                   1101:                        order => ['B4','B3','B1','B2'],
1.112     bowersj2 1102:                    },
                   1103:                A2 => { text =>"Choice A2" ,
                   1104:                        default => "C2",
                   1105:                        select2 => { 
                   1106:                            C1 => "Choice C1",
                   1107:                            C2 => "Choice C2",
                   1108:                            C3 => "Choice C3"
1.609     raeburn  1109:                            },
                   1110:                        order => ['C2','C1','C3'],
1.112     bowersj2 1111:                    },
                   1112:                A3 => { text =>"Choice A3" ,
                   1113:                        default => "D6",
                   1114:                        select2 => { 
                   1115:                            D1 => "Choice D1",
                   1116:                            D2 => "Choice D2",
                   1117:                            D3 => "Choice D3",
                   1118:                            D4 => "Choice D4",
                   1119:                            D5 => "Choice D5",
                   1120:                            D6 => "Choice D6",
                   1121:                            D7 => "Choice D7"
1.609     raeburn  1122:                            },
                   1123:                        order => ['D4','D3','D2','D1','D7','D6','D5'],
1.112     bowersj2 1124:                    }
                   1125:                );
1.36      matthew  1126: 
                   1127: =cut
                   1128: 
                   1129: sub linked_select_forms {
                   1130:     my ($formname,
                   1131:         $middletext,
                   1132:         $firstdefault,
                   1133:         $firstselectname,
                   1134:         $secondselectname, 
1.609     raeburn  1135:         $hashref,
                   1136:         $menuorder,
1.1075.2.31  raeburn  1137:         $onchangefirst,
                   1138:         $onchangesecond
1.36      matthew  1139:         ) = @_;
                   1140:     my $second = "document.$formname.$secondselectname";
                   1141:     my $first = "document.$formname.$firstselectname";
                   1142:     # output the javascript to do the changing
                   1143:     my $result = '';
1.776     bisitz   1144:     $result.='<script type="text/javascript" language="JavaScript">'."\n";
1.824     bisitz   1145:     $result.="// <![CDATA[\n";
1.36      matthew  1146:     $result.="var select2data = new Object();\n";
                   1147:     $" = '","';
                   1148:     my $debug = '';
                   1149:     foreach my $s1 (sort(keys(%$hashref))) {
                   1150:         $result.="select2data.d_$s1 = new Object();\n";        
                   1151:         $result.="select2data.d_$s1.def = new String('".
                   1152:             $hashref->{$s1}->{'default'}."');\n";
1.609     raeburn  1153:         $result.="select2data.d_$s1.values = new Array(";
1.36      matthew  1154:         my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
1.609     raeburn  1155:         if (ref($hashref->{$s1}->{'order'}) eq 'ARRAY') {
                   1156:             @s2values = @{$hashref->{$s1}->{'order'}};
                   1157:         }
1.36      matthew  1158:         $result.="\"@s2values\");\n";
                   1159:         $result.="select2data.d_$s1.texts = new Array(";        
                   1160:         my @s2texts;
                   1161:         foreach my $value (@s2values) {
1.1075.2.119  raeburn  1162:             push(@s2texts, $hashref->{$s1}->{'select2'}->{$value});
1.36      matthew  1163:         }
                   1164:         $result.="\"@s2texts\");\n";
                   1165:     }
                   1166:     $"=' ';
                   1167:     $result.= <<"END";
                   1168: 
                   1169: function select1_changed() {
                   1170:     // Determine new choice
                   1171:     var newvalue = "d_" + $first.value;
                   1172:     // update select2
                   1173:     var values     = select2data[newvalue].values;
                   1174:     var texts      = select2data[newvalue].texts;
                   1175:     var select2def = select2data[newvalue].def;
                   1176:     var i;
                   1177:     // out with the old
                   1178:     for (i = 0; i < $second.options.length; i++) {
                   1179:         $second.options[i] = null;
                   1180:     }
                   1181:     // in with the nuclear
                   1182:     for (i=0;i<values.length; i++) {
                   1183:         $second.options[i] = new Option(values[i]);
1.143     matthew  1184:         $second.options[i].value = values[i];
1.36      matthew  1185:         $second.options[i].text = texts[i];
                   1186:         if (values[i] == select2def) {
                   1187:             $second.options[i].selected = true;
                   1188:         }
                   1189:     }
                   1190: }
1.824     bisitz   1191: // ]]>
1.36      matthew  1192: </script>
                   1193: END
                   1194:     # output the initial values for the selection lists
1.1075.2.31  raeburn  1195:     $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1_changed();$onchangefirst\">\n";
1.609     raeburn  1196:     my @order = sort(keys(%{$hashref}));
                   1197:     if (ref($menuorder) eq 'ARRAY') {
                   1198:         @order = @{$menuorder};
                   1199:     }
                   1200:     foreach my $value (@order) {
1.36      matthew  1201:         $result.="    <option value=\"$value\" ";
1.253     albertel 1202:         $result.=" selected=\"selected\" " if ($value eq $firstdefault);
1.119     www      1203:         $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
1.36      matthew  1204:     }
                   1205:     $result .= "</select>\n";
                   1206:     my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
                   1207:     $result .= $middletext;
1.1075.2.31  raeburn  1208:     $result .= "<select size=\"1\" name=\"$secondselectname\"";
                   1209:     if ($onchangesecond) {
                   1210:         $result .= ' onchange="'.$onchangesecond.'"';
                   1211:     }
                   1212:     $result .= ">\n";
1.36      matthew  1213:     my $seconddefault = $hashref->{$firstdefault}->{'default'};
1.609     raeburn  1214:     
                   1215:     my @secondorder = sort(keys(%select2));
                   1216:     if (ref($hashref->{$firstdefault}->{'order'}) eq 'ARRAY') {
                   1217:         @secondorder = @{$hashref->{$firstdefault}->{'order'}};
                   1218:     }
                   1219:     foreach my $value (@secondorder) {
1.36      matthew  1220:         $result.="    <option value=\"$value\" ";        
1.253     albertel 1221:         $result.=" selected=\"selected\" " if ($value eq $seconddefault);
1.119     www      1222:         $result.=">".&mt($select2{$value})."</option>\n";
1.36      matthew  1223:     }
                   1224:     $result .= "</select>\n";
                   1225:     #    return $debug;
                   1226:     return $result;
                   1227: }   #  end of sub linked_select_forms {
                   1228: 
1.45      matthew  1229: =pod
1.44      bowersj2 1230: 
1.1075.2.161.  .6(raebu 1231:22): =item * &help_open_topic($topic,$text,$stayOnPage,$width,$height,$imgid,$links_target)
1.44      bowersj2 1232: 
1.112     bowersj2 1233: Returns a string corresponding to an HTML link to the given help
                   1234: $topic, where $topic corresponds to the name of a .tex file in
                   1235: /home/httpd/html/adm/help/tex, with underscores replaced by
                   1236: spaces. 
                   1237: 
                   1238: $text will optionally be linked to the same topic, allowing you to
                   1239: link text in addition to the graphic. If you do not want to link
                   1240: text, but wish to specify one of the later parameters, pass an
                   1241: empty string. 
                   1242: 
                   1243: $stayOnPage is a value that will be interpreted as a boolean. If true,
                   1244: the link will not open a new window. If false, the link will open
                   1245: a new window using Javascript. (Default is false.) 
                   1246: 
                   1247: $width and $height are optional numerical parameters that will
                   1248: override the width and height of the popped up window, which may
1.973     raeburn  1249: be useful for certain help topics with big pictures included.
                   1250: 
                   1251: $imgid is the id of the img tag used for the help icon. This may be
                   1252: used in a javascript call to switch the image src.  See 
                   1253: lonhtmlcommon::htmlareaselectactive() for an example.
1.44      bowersj2 1254: 
1.1075.2.161.  .6(raebu 1255:22): $links_target will optionally be set to a target (_top, _parent or _self).
                   1256:22): 
1.44      bowersj2 1257: =cut
                   1258: 
                   1259: sub help_open_topic {
1.1075.2.161.  .6(raebu 1260:22):     my ($topic, $text, $stayOnPage, $width, $height, $imgid, $links_target) = @_;
1.48      bowersj2 1261:     $text = "" if (not defined $text);
1.44      bowersj2 1262:     $stayOnPage = 0 if (not defined $stayOnPage);
1.1033    www      1263:     $width = 500 if (not defined $width);
1.44      bowersj2 1264:     $height = 400 if (not defined $height);
                   1265:     my $filename = $topic;
                   1266:     $filename =~ s/ /_/g;
                   1267: 
1.48      bowersj2 1268:     my $template = "";
                   1269:     my $link;
1.572     banghart 1270:     
1.159     www      1271:     $topic=~s/\W/\_/g;
1.44      bowersj2 1272: 
1.572     banghart 1273:     if (!$stayOnPage) {
1.1075.2.50  raeburn  1274:         if ($env{'browser.mobile'}) {
                   1275: 	    $link = "javascript:openMyModal('/adm/help/${filename}.hlp',$width,$height,'yes');";
                   1276:         } else {
                   1277:             $link = "javascript:void(open('/adm/help/${filename}.hlp', 'Help_for_$topic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1278:         }
1.1037    www      1279:     } elsif ($stayOnPage eq 'popup') {
                   1280:         $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 1281:     } else {
1.48      bowersj2 1282: 	$link = "/adm/help/${filename}.hlp";
                   1283:     }
                   1284: 
                   1285:     # Add the text
1.1075.2.161.  .6(raebu 1286:22):     my $target = ' target="_top"';
                   1287:22):     if ($links_target) {
                   1288:22):         $target = ' target="'.$links_target.'"';
          .17(raeb 1289:-23):     } elsif ((($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) ||
                   1290:-23):              (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'} eq '_self'))) {
          .6(raebu 1291:22):         $target = '';
                   1292:22):     }
1.755     neumanie 1293:     if ($text ne "") {	
1.763     bisitz   1294: 	$template.='<span class="LC_help_open_topic">'
1.1075.2.161.  .6(raebu 1295:22):                   .'<a'.$target.' href="'.$link.'">'
1.763     bisitz   1296:                   .$text.'</a>';
1.48      bowersj2 1297:     }
                   1298: 
1.763     bisitz   1299:     # (Always) Add the graphic
1.179     matthew  1300:     my $title = &mt('Online Help');
1.667     raeburn  1301:     my $helpicon=&lonhttpdurl("/adm/help/help.png");
1.973     raeburn  1302:     if ($imgid ne '') {
                   1303:         $imgid = ' id="'.$imgid.'"';
                   1304:     }
1.1075.2.161.  .6(raebu 1305:22):     $template.=' <a'.$target.' href="'.$link.'" title="'.$title.'">'
1.763     bisitz   1306:               .'<img src="'.$helpicon.'" border="0"'
                   1307:               .' alt="'.&mt('Help: [_1]',$topic).'"'
1.973     raeburn  1308:               .' title="'.$title.'" style="vertical-align:middle;"'.$imgid 
1.763     bisitz   1309:               .' /></a>';
                   1310:     if ($text ne "") {	
                   1311:         $template.='</span>';
                   1312:     }
1.44      bowersj2 1313:     return $template;
                   1314: 
1.106     bowersj2 1315: }
                   1316: 
                   1317: # This is a quicky function for Latex cheatsheet editing, since it 
                   1318: # appears in at least four places
                   1319: sub helpLatexCheatsheet {
1.1037    www      1320:     my ($topic,$text,$not_author,$stayOnPage) = @_;
1.732     raeburn  1321:     my $out;
1.106     bowersj2 1322:     my $addOther = '';
1.732     raeburn  1323:     if ($topic) {
1.1037    www      1324: 	$addOther = '<span>'.&help_open_topic($topic,&mt($text),$stayOnPage, undef, 600).'</span> ';
1.763     bisitz   1325:     }
                   1326:     $out = '<span>' # Start cheatsheet
                   1327: 	  .$addOther
                   1328:           .'<span>'
1.1037    www      1329: 	  .&help_open_topic('Greek_Symbols',&mt('Greek Symbols'),$stayOnPage,undef,600)
1.763     bisitz   1330: 	  .'</span> <span>'
1.1037    www      1331: 	  .&help_open_topic('Other_Symbols',&mt('Other Symbols'),$stayOnPage,undef,600)
1.763     bisitz   1332: 	  .'</span>';
1.732     raeburn  1333:     unless ($not_author) {
1.763     bisitz   1334:         $out .= ' <span>'
1.1037    www      1335: 	       .&help_open_topic('Authoring_Output_Tags',&mt('Output Tags'),$stayOnPage,undef,600)
1.1075.2.71  raeburn  1336: 	       .'</span> <span>'
1.1075.2.78  raeburn  1337:                .&help_open_topic('Authoring_Multilingual_Problems',&mt('Languages'),$stayOnPage,undef,600)
1.1075.2.71  raeburn  1338:                .'</span>';
1.732     raeburn  1339:     }
1.763     bisitz   1340:     $out .= '</span>'; # End cheatsheet
1.732     raeburn  1341:     return $out;
1.172     www      1342: }
                   1343: 
1.430     albertel 1344: sub general_help {
                   1345:     my $helptopic='Student_Intro';
                   1346:     if ($env{'request.role'}=~/^(ca|au)/) {
                   1347: 	$helptopic='Authoring_Intro';
1.907     raeburn  1348:     } elsif ($env{'request.role'}=~/^(cc|co)/) {
1.430     albertel 1349: 	$helptopic='Course_Coordination_Intro';
1.672     raeburn  1350:     } elsif ($env{'request.role'}=~/^dc/) {
                   1351:         $helptopic='Domain_Coordination_Intro';
1.430     albertel 1352:     }
                   1353:     return $helptopic;
                   1354: }
                   1355: 
                   1356: sub update_help_link {
                   1357:     my ($topic,$component_help,$faq,$bug,$stayOnPage) = @_;
                   1358:     my $origurl = $ENV{'REQUEST_URI'};
                   1359:     $origurl=~s|^/~|/priv/|;
                   1360:     my $timestamp = time;
                   1361:     foreach my $datum (\$topic,\$component_help,\$faq,\$bug,\$origurl) {
                   1362:         $$datum = &escape($$datum);
                   1363:     }
                   1364: 
                   1365:     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";
                   1366:     my $output .= <<"ENDOUTPUT";
                   1367: <script type="text/javascript">
1.824     bisitz   1368: // <![CDATA[
1.430     albertel 1369: banner_link = '$banner_link';
1.824     bisitz   1370: // ]]>
1.430     albertel 1371: </script>
                   1372: ENDOUTPUT
                   1373:     return $output;
                   1374: }
                   1375: 
                   1376: # now just updates the help link and generates a blue icon
1.193     raeburn  1377: sub help_open_menu {
1.1075.2.161.  .6(raebu 1378:22):     my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text,$links_target) 
1.552     banghart 1379: 	= @_;    
1.949     droeschl 1380:     $stayOnPage = 1;
1.430     albertel 1381:     my $output;
                   1382:     if ($component_help) {
                   1383: 	if (!$text) {
                   1384: 	    $output=&help_open_topic($component_help,undef,$stayOnPage,
1.1075.2.161.  .6(raebu 1385:22): 				       $width,$height,'',$links_target);
1.430     albertel 1386: 	} else {
                   1387: 	    my $help_text;
                   1388: 	    $help_text=&unescape($topic);
                   1389: 	    $output='<table><tr><td>'.
                   1390: 		&help_open_topic($component_help,$help_text,$stayOnPage,
1.1075.2.161.  .6(raebu 1391:22): 				 $width,$height,'',$links_target).'</td></tr></table>';
1.430     albertel 1392: 	}
                   1393:     }
                   1394:     my $banner_link = &update_help_link($topic,$component_help,$faq,$bug,$stayOnPage);
                   1395:     return $output.$banner_link;
                   1396: }
                   1397: 
                   1398: sub top_nav_help {
1.1075.2.158  raeburn  1399:     my ($text,$linkattr) = @_;
1.436     albertel 1400:     $text = &mt($text);
1.1075.2.60  raeburn  1401:     my $stay_on_page;
                   1402:     unless ($env{'environment.remote'} eq 'on') {
                   1403:         $stay_on_page = 1;
                   1404:     }
1.1075.2.61  raeburn  1405:     my ($link,$banner_link);
                   1406:     unless ($env{'request.noversionuri'} =~ m{^/adm/helpmenu}) {
                   1407:         $link = ($stay_on_page) ? "javascript:helpMenu('display')"
                   1408: 	                         : "javascript:helpMenu('open')";
                   1409:         $banner_link = &update_help_link(undef,undef,undef,undef,$stay_on_page);
                   1410:     }
1.201     raeburn  1411:     my $title = &mt('Get help');
1.1075.2.61  raeburn  1412:     if ($link) {
                   1413:         return <<"END";
1.436     albertel 1414: $banner_link
1.1075.2.158  raeburn  1415: <a href="$link" title="$title" $linkattr>$text</a>
1.436     albertel 1416: END
1.1075.2.61  raeburn  1417:     } else {
                   1418:         return '&nbsp;'.$text.'&nbsp;';
                   1419:     }
1.436     albertel 1420: }
                   1421: 
                   1422: sub help_menu_js {
1.1075.2.52  raeburn  1423:     my ($httphost) = @_;
1.949     droeschl 1424:     my $stayOnPage = 1;
1.436     albertel 1425:     my $width = 620;
                   1426:     my $height = 600;
1.430     albertel 1427:     my $helptopic=&general_help();
1.1075.2.52  raeburn  1428:     my $details_link = $httphost.'/adm/help/'.$helptopic.'.hlp';
1.261     albertel 1429:     my $nothing=&Apache::lonhtmlcommon::javascript_nothing();
1.331     albertel 1430:     my $start_page =
                   1431:         &Apache::loncommon::start_page('Help Menu', undef,
                   1432: 				       {'frameset'    => 1,
                   1433: 					'js_ready'    => 1,
1.1075.2.136  raeburn  1434:                                         'use_absolute' => $httphost,
1.331     albertel 1435: 					'add_entries' => {
                   1436: 					    'border' => '0',
1.579     raeburn  1437: 					    'rows'   => "110,*",},});
1.331     albertel 1438:     my $end_page =
                   1439:         &Apache::loncommon::end_page({'frameset' => 1,
                   1440: 				      'js_ready' => 1,});
                   1441: 
1.436     albertel 1442:     my $template .= <<"ENDTEMPLATE";
                   1443: <script type="text/javascript">
1.877     bisitz   1444: // <![CDATA[
1.253     albertel 1445: // <!-- BEGIN LON-CAPA Internal
1.430     albertel 1446: var banner_link = '';
1.243     raeburn  1447: function helpMenu(target) {
                   1448:     var caller = this;
                   1449:     if (target == 'open') {
                   1450:         var newWindow = null;
                   1451:         try {
1.262     albertel 1452:             newWindow =  window.open($nothing,"helpmenu","HEIGHT=$height,WIDTH=$width,resizable=yes,scrollbars=yes" )
1.243     raeburn  1453:         }
                   1454:         catch(error) {
                   1455:             writeHelp(caller);
                   1456:             return;
                   1457:         }
                   1458:         if (newWindow) {
                   1459:             caller = newWindow;
                   1460:         }
1.193     raeburn  1461:     }
1.243     raeburn  1462:     writeHelp(caller);
                   1463:     return;
                   1464: }
                   1465: function writeHelp(caller) {
1.1075.2.61  raeburn  1466:     caller.document.writeln('$start_page\\n<frame name="bannerframe" src="'+banner_link+'" marginwidth="0" marginheight="0" frameborder="0">\\n');
                   1467:     caller.document.writeln('<frame name="bodyframe" src="$details_link" marginwidth="0" marginheight="0" frameborder="0">\\n$end_page');
                   1468:     caller.document.close();
                   1469:     caller.focus();
1.193     raeburn  1470: }
1.877     bisitz   1471: // END LON-CAPA Internal -->
1.253     albertel 1472: // ]]>
1.436     albertel 1473: </script>
1.193     raeburn  1474: ENDTEMPLATE
                   1475:     return $template;
                   1476: }
                   1477: 
1.172     www      1478: sub help_open_bug {
                   1479:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel 1480:     unless ($env{'user.adv'}) { return ''; }
1.172     www      1481:     unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; }
                   1482:     $text = "" if (not defined $text);
                   1483: 	$stayOnPage=1;
1.184     albertel 1484:     $width = 600 if (not defined $width);
                   1485:     $height = 600 if (not defined $height);
1.172     www      1486: 
                   1487:     $topic=~s/\W+/\+/g;
                   1488:     my $link='';
                   1489:     my $template='';
1.379     albertel 1490:     my $url=$Apache::lonnet::perlvar{'BugzillaHost'}.'enter_bug.cgi?product=LON-CAPA&amp;bug_file_loc='.
                   1491: 	&escape($ENV{'REQUEST_URI'}).'&amp;component='.$topic;
1.172     www      1492:     if (!$stayOnPage)
                   1493:     {
                   1494: 	$link = "javascript:void(open('$url', 'Bugzilla', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1495:     }
                   1496:     else
                   1497:     {
                   1498: 	$link = $url;
                   1499:     }
1.1075.2.161.  .6(raebu 1500:22): 
                   1501:22):     my $target = '_top';
                   1502:22):     if ((($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) ||
                   1503:22):         (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'} eq '_self'))) {
                   1504:22):         $target = '_blank';
                   1505:22):     }
                   1506:22): 
1.172     www      1507:     # Add the text
                   1508:     if ($text ne "")
                   1509:     {
                   1510: 	$template .= 
                   1511:   "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
1.1075.2.161.  .6(raebu 1512:22):   "<td bgcolor='#FF5555'><a target=\"$target\" href=\"$link\"><span style=\"color:#FFFFFF;font-size:10pt;\">$text</span></a>";
1.172     www      1513:     }
                   1514: 
                   1515:     # Add the graphic
1.179     matthew  1516:     my $title = &mt('Report a Bug');
1.215     albertel 1517:     my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");
1.172     www      1518:     $template .= <<"ENDTEMPLATE";
1.1075.2.161.  .6(raebu 1519:22):  <a target="$target" href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
1.172     www      1520: ENDTEMPLATE
                   1521:     if ($text ne '') { $template.='</td></tr></table>' };
                   1522:     return $template;
                   1523: 
                   1524: }
                   1525: 
                   1526: sub help_open_faq {
                   1527:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel 1528:     unless ($env{'user.adv'}) { return ''; }
1.172     www      1529:     unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; }
                   1530:     $text = "" if (not defined $text);
                   1531: 	$stayOnPage=1;
                   1532:     $width = 350 if (not defined $width);
                   1533:     $height = 400 if (not defined $height);
                   1534: 
                   1535:     $topic=~s/\W+/\+/g;
                   1536:     my $link='';
                   1537:     my $template='';
                   1538:     my $url=$Apache::lonnet::perlvar{'FAQHost'}.'/fom/cache/'.$topic.'.html';
                   1539:     if (!$stayOnPage)
                   1540:     {
                   1541: 	$link = "javascript:void(open('$url', 'FAQ-O-Matic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1542:     }
                   1543:     else
                   1544:     {
                   1545: 	$link = $url;
                   1546:     }
                   1547: 
                   1548:     # Add the text
                   1549:     if ($text ne "")
                   1550:     {
                   1551: 	$template .= 
1.173     www      1552:   "<table bgcolor='#337733' cellspacing='1' cellpadding='1' border='0'><tr>".
1.705     tempelho 1553:   "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF; font-size:10pt;\">$text</span></a>";
1.172     www      1554:     }
                   1555: 
                   1556:     # Add the graphic
1.179     matthew  1557:     my $title = &mt('View the FAQ');
1.215     albertel 1558:     my $faqicon=&lonhttpdurl("/adm/lonMisc/smallFAQ.gif");
1.172     www      1559:     $template .= <<"ENDTEMPLATE";
1.436     albertel 1560:  <a target="_top" href="$link" title="$title"><img src="$faqicon" border="0" alt="(FAQ: $topic)" /></a>
1.172     www      1561: ENDTEMPLATE
                   1562:     if ($text ne '') { $template.='</td></tr></table>' };
                   1563:     return $template;
                   1564: 
1.44      bowersj2 1565: }
1.37      matthew  1566: 
1.180     matthew  1567: ###############################################################
                   1568: ###############################################################
                   1569: 
1.45      matthew  1570: =pod
                   1571: 
1.648     raeburn  1572: =item * &change_content_javascript():
1.256     matthew  1573: 
                   1574: This and the next function allow you to create small sections of an
                   1575: otherwise static HTML page that you can update on the fly with
                   1576: Javascript, even in Netscape 4.
                   1577: 
                   1578: The Javascript fragment returned by this function (no E<lt>scriptE<gt> tag)
                   1579: must be written to the HTML page once. It will prove the Javascript
                   1580: function "change(name, content)". Calling the change function with the
                   1581: name of the section 
                   1582: you want to update, matching the name passed to C<changable_area>, and
                   1583: the new content you want to put in there, will put the content into
                   1584: that area.
                   1585: 
                   1586: B<Note>: Netscape 4 only reserves enough space for the changable area
                   1587: to contain room for the original contents. You need to "make space"
                   1588: for whatever changes you wish to make, and be B<sure> to check your
                   1589: code in Netscape 4. This feature in Netscape 4 is B<not> powerful;
                   1590: it's adequate for updating a one-line status display, but little more.
                   1591: This script will set the space to 100% width, so you only need to
                   1592: worry about height in Netscape 4.
                   1593: 
                   1594: Modern browsers are much less limiting, and if you can commit to the
                   1595: user not using Netscape 4, this feature may be used freely with
                   1596: pretty much any HTML.
                   1597: 
                   1598: =cut
                   1599: 
                   1600: sub change_content_javascript {
                   1601:     # If we're on Netscape 4, we need to use Layer-based code
1.258     albertel 1602:     if ($env{'browser.type'} eq 'netscape' &&
                   1603: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1604: 	return (<<NETSCAPE4);
                   1605: 	function change(name, content) {
                   1606: 	    doc = document.layers[name+"___escape"].layers[0].document;
                   1607: 	    doc.open();
                   1608: 	    doc.write(content);
                   1609: 	    doc.close();
                   1610: 	}
                   1611: NETSCAPE4
                   1612:     } else {
                   1613: 	# Otherwise, we need to use semi-standards-compliant code
                   1614: 	# (technically, "innerHTML" isn't standard but the equivalent
                   1615: 	# is really scary, and every useful browser supports it
                   1616: 	return (<<DOMBASED);
                   1617: 	function change(name, content) {
                   1618: 	    element = document.getElementById(name);
                   1619: 	    element.innerHTML = content;
                   1620: 	}
                   1621: DOMBASED
                   1622:     }
                   1623: }
                   1624: 
                   1625: =pod
                   1626: 
1.648     raeburn  1627: =item * &changable_area($name,$origContent):
1.256     matthew  1628: 
                   1629: This provides a "changable area" that can be modified on the fly via
                   1630: the Javascript code provided in C<change_content_javascript>. $name is
                   1631: the name you will use to reference the area later; do not repeat the
                   1632: same name on a given HTML page more then once. $origContent is what
                   1633: the area will originally contain, which can be left blank.
                   1634: 
                   1635: =cut
                   1636: 
                   1637: sub changable_area {
                   1638:     my ($name, $origContent) = @_;
                   1639: 
1.258     albertel 1640:     if ($env{'browser.type'} eq 'netscape' &&
                   1641: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1642: 	# If this is netscape 4, we need to use the Layer tag
                   1643: 	return "<ilayer width='100%' id='${name}___escape' overflow='none'><layer width='100%' id='$name' overflow='none'>$origContent</layer></ilayer>";
                   1644:     } else {
                   1645: 	return "<span id='$name'>$origContent</span>";
                   1646:     }
                   1647: }
                   1648: 
                   1649: =pod
                   1650: 
1.648     raeburn  1651: =item * &viewport_geometry_js 
1.590     raeburn  1652: 
                   1653: Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser.
                   1654: 
                   1655: =cut
                   1656: 
                   1657: 
                   1658: sub viewport_geometry_js { 
                   1659:     return <<"GEOMETRY";
                   1660: var Geometry = {};
                   1661: function init_geometry() {
                   1662:     if (Geometry.init) { return };
                   1663:     Geometry.init=1;
                   1664:     if (window.innerHeight) {
                   1665:         Geometry.getViewportHeight   = function() { return window.innerHeight; };
                   1666:         Geometry.getViewportWidth   = function() { return window.innerWidth; };
                   1667:         Geometry.getHorizontalScroll = function() { return window.pageXOffset; };
                   1668:         Geometry.getVerticalScroll   = function() { return window.pageYOffset; };
                   1669:     }
                   1670:     else if (document.documentElement && document.documentElement.clientHeight) {
                   1671:         Geometry.getViewportHeight =
                   1672:             function() { return document.documentElement.clientHeight; };
                   1673:         Geometry.getViewportWidth =
                   1674:             function() { return document.documentElement.clientWidth; };
                   1675: 
                   1676:         Geometry.getHorizontalScroll =
                   1677:             function() { return document.documentElement.scrollLeft; };
                   1678:         Geometry.getVerticalScroll =
                   1679:             function() { return document.documentElement.scrollTop; };
                   1680:     }
                   1681:     else if (document.body.clientHeight) {
                   1682:         Geometry.getViewportHeight =
                   1683:             function() { return document.body.clientHeight; };
                   1684:         Geometry.getViewportWidth =
                   1685:             function() { return document.body.clientWidth; };
                   1686:         Geometry.getHorizontalScroll =
                   1687:             function() { return document.body.scrollLeft; };
                   1688:         Geometry.getVerticalScroll =
                   1689:             function() { return document.body.scrollTop; };
                   1690:     }
                   1691: }
                   1692: 
                   1693: GEOMETRY
                   1694: }
                   1695: 
                   1696: =pod
                   1697: 
1.648     raeburn  1698: =item * &viewport_size_js()
1.590     raeburn  1699: 
                   1700: 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. 
                   1701: 
                   1702: =cut
                   1703: 
                   1704: sub viewport_size_js {
                   1705:     my $geometry = &viewport_geometry_js();
                   1706:     return <<"DIMS";
                   1707: 
                   1708: $geometry
                   1709: 
                   1710: function getViewportDims(width,height) {
                   1711:     init_geometry();
                   1712:     width.value = Geometry.getViewportWidth();
                   1713:     height.value = Geometry.getViewportHeight();
                   1714:     return;
                   1715: }
                   1716: 
                   1717: DIMS
                   1718: }
                   1719: 
                   1720: =pod
                   1721: 
1.648     raeburn  1722: =item * &resize_textarea_js()
1.565     albertel 1723: 
                   1724: emits the needed javascript to resize a textarea to be as big as possible
                   1725: 
                   1726: creates a function resize_textrea that takes two IDs first should be
                   1727: the id of the element to resize, second should be the id of a div that
                   1728: surrounds everything that comes after the textarea, this routine needs
                   1729: to be attached to the <body> for the onload and onresize events.
                   1730: 
1.648     raeburn  1731: =back
1.565     albertel 1732: 
                   1733: =cut
                   1734: 
                   1735: sub resize_textarea_js {
1.590     raeburn  1736:     my $geometry = &viewport_geometry_js();
1.565     albertel 1737:     return <<"RESIZE";
                   1738:     <script type="text/javascript">
1.824     bisitz   1739: // <![CDATA[
1.590     raeburn  1740: $geometry
1.565     albertel 1741: 
1.588     albertel 1742: function getX(element) {
                   1743:     var x = 0;
                   1744:     while (element) {
                   1745: 	x += element.offsetLeft;
                   1746: 	element = element.offsetParent;
                   1747:     }
                   1748:     return x;
                   1749: }
                   1750: function getY(element) {
                   1751:     var y = 0;
                   1752:     while (element) {
                   1753: 	y += element.offsetTop;
                   1754: 	element = element.offsetParent;
                   1755:     }
                   1756:     return y;
                   1757: }
                   1758: 
                   1759: 
1.565     albertel 1760: function resize_textarea(textarea_id,bottom_id) {
                   1761:     init_geometry();
                   1762:     var textarea        = document.getElementById(textarea_id);
                   1763:     //alert(textarea);
                   1764: 
1.588     albertel 1765:     var textarea_top    = getY(textarea);
1.565     albertel 1766:     var textarea_height = textarea.offsetHeight;
                   1767:     var bottom          = document.getElementById(bottom_id);
1.588     albertel 1768:     var bottom_top      = getY(bottom);
1.565     albertel 1769:     var bottom_height   = bottom.offsetHeight;
                   1770:     var window_height   = Geometry.getViewportHeight();
1.588     albertel 1771:     var fudge           = 23;
1.565     albertel 1772:     var new_height      = window_height-fudge-textarea_top-bottom_height;
                   1773:     if (new_height < 300) {
                   1774: 	new_height = 300;
                   1775:     }
                   1776:     textarea.style.height=new_height+'px';
                   1777: }
1.824     bisitz   1778: // ]]>
1.565     albertel 1779: </script>
                   1780: RESIZE
                   1781: 
                   1782: }
                   1783: 
1.1075.2.112  raeburn  1784: sub colorfuleditor_js {
                   1785:     return <<"COLORFULEDIT"
                   1786: <script type="text/javascript">
                   1787: // <![CDATA[>
                   1788:     function fold_box(curDepth, lastresource){
                   1789: 
                   1790:     // we need a list because there can be several blocks you need to fold in one tag
                   1791:         var block = document.getElementsByName('foldblock_'+curDepth);
                   1792:     // but there is only one folding button per tag
                   1793:         var foldbutton = document.getElementById('folding_btn_'+curDepth);
                   1794: 
                   1795:         if(block.item(0).style.display == 'none'){
                   1796: 
                   1797:             foldbutton.value = '@{[&mt("Hide")]}';
                   1798:             for (i = 0; i < block.length; i++){
                   1799:                 block.item(i).style.display = '';
                   1800:             }
                   1801:         }else{
                   1802: 
                   1803:             foldbutton.value = '@{[&mt("Show")]}';
                   1804:             for (i = 0; i < block.length; i++){
                   1805:                 // block.item(i).style.visibility = 'collapse';
                   1806:                 block.item(i).style.display = 'none';
                   1807:             }
                   1808:         };
                   1809:         saveState(lastresource);
                   1810:     }
                   1811: 
                   1812:     function saveState (lastresource) {
                   1813: 
                   1814:         var tag_list = getTagList();
                   1815:         if(tag_list != null){
                   1816:             var timestamp = new Date().getTime();
                   1817:             var key = lastresource;
                   1818: 
                   1819:             // the value pattern is: 'time;key1,value1;key2,value2; ... '
                   1820:             // starting with timestamp
                   1821:             var value = timestamp+';';
                   1822: 
                   1823:             // building the list of key-value pairs
                   1824:             for(var i = 0; i < tag_list.length; i++){
                   1825:                 value += tag_list[i]+',';
                   1826:                 value += document.getElementsByName(tag_list[i])[0].style.display+';';
                   1827:             }
                   1828: 
                   1829:             // only iterate whole storage if nothing to override
                   1830:             if(localStorage.getItem(key) == null){
                   1831: 
                   1832:                 // prevent storage from growing large
                   1833:                 if(localStorage.length > 50){
                   1834:                     var regex_getTimestamp = /^(?:\d)+;/;
                   1835:                     var oldest_timestamp = regex_getTimestamp.exec(localStorage.key(0));
                   1836:                     var oldest_key;
                   1837: 
                   1838:                     for(var i = 1; i < localStorage.length; i++){
                   1839:                         if (regex_getTimestamp.exec(localStorage.key(i)) < oldest_timestamp) {
                   1840:                             oldest_key = localStorage.key(i);
                   1841:                             oldest_timestamp = regex_getTimestamp.exec(oldest_key);
                   1842:                         }
                   1843:                     }
                   1844:                     localStorage.removeItem(oldest_key);
                   1845:                 }
                   1846:             }
                   1847:             localStorage.setItem(key,value);
                   1848:         }
                   1849:     }
                   1850: 
                   1851:     // restore folding status of blocks (on page load)
                   1852:     function restoreState (lastresource) {
                   1853:         if(localStorage.getItem(lastresource) != null){
                   1854:             var key = lastresource;
                   1855:             var value = localStorage.getItem(key);
                   1856:             var regex_delTimestamp = /^\d+;/;
                   1857: 
                   1858:             value.replace(regex_delTimestamp, '');
                   1859: 
                   1860:             var valueArr = value.split(';');
                   1861:             var pairs;
                   1862:             var elements;
                   1863:             for (var i = 0; i < valueArr.length; i++){
                   1864:                 pairs = valueArr[i].split(',');
                   1865:                 elements = document.getElementsByName(pairs[0]);
                   1866: 
                   1867:                 for (var j = 0; j < elements.length; j++){
                   1868:                     elements[j].style.display = pairs[1];
                   1869:                     if (pairs[1] == "none"){
                   1870:                         var regex_id = /([_\\d]+)\$/;
                   1871:                         regex_id.exec(pairs[0]);
                   1872:                         document.getElementById("folding_btn"+RegExp.\$1).value = "Show";
                   1873:                     }
                   1874:                 }
                   1875:             }
                   1876:         }
                   1877:     }
                   1878: 
                   1879:     function getTagList () {
                   1880: 
                   1881:         var stringToSearch = document.lonhomework.innerHTML;
                   1882: 
                   1883:         var ret = new Array();
                   1884:         var regex_findBlock = /(foldblock_.*?)"/g;
                   1885:         var tag_list = stringToSearch.match(regex_findBlock);
                   1886: 
                   1887:         if(tag_list != null){
                   1888:             for(var i = 0; i < tag_list.length; i++){
                   1889:                 ret.push(tag_list[i].replace(/"/, ''));
                   1890:             }
                   1891:         }
                   1892:         return ret;
                   1893:     }
                   1894: 
                   1895:     function saveScrollPosition (resource) {
                   1896:         var tag_list = getTagList();
                   1897: 
                   1898:         // we dont always want to jump to the first block
                   1899:         // 170 is roughly above the "Problem Editing" header. we just want to save if the user scrolled down further than this
                   1900:         if(\$(window).scrollTop() > 170){
                   1901:             if(tag_list != null){
                   1902:                 var result;
                   1903:                 for(var i = 0; i < tag_list.length; i++){
                   1904:                     if(isElementInViewport(tag_list[i])){
                   1905:                         result += tag_list[i]+';';
                   1906:                     }
                   1907:                 }
                   1908:                 sessionStorage.setItem('anchor_'+resource, result);
                   1909:             }
                   1910:         } else {
                   1911:             // we dont need to save zero, just delete the item to leave everything tidy
                   1912:             sessionStorage.removeItem('anchor_'+resource);
                   1913:         }
                   1914:     }
                   1915: 
                   1916:     function restoreScrollPosition(resource){
                   1917: 
                   1918:         var elem = sessionStorage.getItem('anchor_'+resource);
                   1919:         if(elem != null){
                   1920:             var tag_list = elem.split(';');
                   1921:             var elem_list;
                   1922: 
                   1923:             for(var i = 0; i < tag_list.length; i++){
                   1924:                 elem_list = document.getElementsByName(tag_list[i]);
                   1925: 
                   1926:                 if(elem_list.length > 0){
                   1927:                     elem = elem_list[0];
                   1928:                     break;
                   1929:                 }
                   1930:             }
                   1931:             elem.scrollIntoView();
                   1932:         }
                   1933:     }
                   1934: 
                   1935:     function isElementInViewport(el) {
                   1936: 
                   1937:         // change to last element instead of first
                   1938:         var elem = document.getElementsByName(el);
                   1939:         var rect = elem[0].getBoundingClientRect();
                   1940: 
                   1941:         return (
                   1942:             rect.top >= 0 &&
                   1943:             rect.left >= 0 &&
                   1944:             rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
                   1945:             rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
                   1946:         );
                   1947:     }
                   1948: 
                   1949:     function autosize(depth){
                   1950:         var cmInst = window['cm'+depth];
                   1951:         var fitsizeButton = document.getElementById('fitsize'+depth);
                   1952: 
                   1953:         // is fixed size, switching to dynamic
                   1954:         if (sessionStorage.getItem("autosized_"+depth) == null) {
                   1955:             cmInst.setSize("","auto");
                   1956:             fitsizeButton.value = "@{[&mt('Fixed size')]}";
                   1957:             sessionStorage.setItem("autosized_"+depth, "yes");
                   1958: 
                   1959:         // is dynamic size, switching to fixed
                   1960:         } else {
                   1961:             cmInst.setSize("","300px");
                   1962:             fitsizeButton.value = "@{[&mt('Dynamic size')]}";
                   1963:             sessionStorage.removeItem("autosized_"+depth);
                   1964:         }
                   1965:     }
                   1966: 
                   1967: 
                   1968: 
                   1969: // ]]>
                   1970: </script>
                   1971: COLORFULEDIT
                   1972: }
                   1973: 
                   1974: sub xmleditor_js {
                   1975:     return <<XMLEDIT
                   1976: <script type="text/javascript" src="/adm/jQuery/addons/jquery-scrolltofixed.js"></script>
                   1977: <script type="text/javascript">
                   1978: // <![CDATA[>
                   1979: 
                   1980:     function saveScrollPosition (resource) {
                   1981: 
                   1982:         var scrollPos = \$(window).scrollTop();
                   1983:         sessionStorage.setItem(resource,scrollPos);
                   1984:     }
                   1985: 
                   1986:     function restoreScrollPosition(resource){
                   1987: 
                   1988:         var scrollPos = sessionStorage.getItem(resource);
                   1989:         \$(window).scrollTop(scrollPos);
                   1990:     }
                   1991: 
                   1992:     // unless internet explorer
                   1993:     if (!(window.navigator.appName == "Microsoft Internet Explorer" && (document.documentMode || document.compatMode))){
                   1994: 
                   1995:         \$(document).ready(function() {
                   1996:              \$(".LC_edit_actionbar").scrollToFixed(\{zIndex: 100\});
                   1997:         });
                   1998:     }
                   1999: 
                   2000:     // inserts text at cursor position into codemirror (xml editor only)
                   2001:     function insertText(text){
                   2002:         cm.focus();
                   2003:         var curPos = cm.getCursor();
                   2004:         cm.replaceRange(text.replace(/ESCAPEDSCRIPT/g,'script'), {line: curPos.line,ch: curPos.ch});
                   2005:     }
                   2006: // ]]>
                   2007: </script>
                   2008: XMLEDIT
                   2009: }
                   2010: 
                   2011: sub insert_folding_button {
                   2012:     my $curDepth = $Apache::lonxml::curdepth;
                   2013:     my $lastresource = $env{'request.ambiguous'};
                   2014: 
                   2015:     return "<input type=\"button\" id=\"folding_btn_$curDepth\"
                   2016:             value=\"".&mt('Hide')."\" onclick=\"fold_box('$curDepth','$lastresource')\">";
                   2017: }
                   2018: 
                   2019: 
1.565     albertel 2020: =pod
                   2021: 
1.256     matthew  2022: =head1 Excel and CSV file utility routines
                   2023: 
                   2024: =cut
                   2025: 
                   2026: ###############################################################
                   2027: ###############################################################
                   2028: 
                   2029: =pod
                   2030: 
1.1075.2.56  raeburn  2031: =over 4
                   2032: 
1.648     raeburn  2033: =item * &csv_translate($text) 
1.37      matthew  2034: 
1.185     www      2035: Translate $text to allow it to be output as a 'comma separated values' 
1.37      matthew  2036: format.
                   2037: 
                   2038: =cut
                   2039: 
1.180     matthew  2040: ###############################################################
                   2041: ###############################################################
1.37      matthew  2042: sub csv_translate {
                   2043:     my $text = shift;
                   2044:     $text =~ s/\"/\"\"/g;
1.209     albertel 2045:     $text =~ s/\n/ /g;
1.37      matthew  2046:     return $text;
                   2047: }
1.180     matthew  2048: 
                   2049: ###############################################################
                   2050: ###############################################################
                   2051: 
                   2052: =pod
                   2053: 
1.648     raeburn  2054: =item * &define_excel_formats()
1.180     matthew  2055: 
                   2056: Define some commonly used Excel cell formats.
                   2057: 
                   2058: Currently supported formats:
                   2059: 
                   2060: =over 4
                   2061: 
                   2062: =item header
                   2063: 
                   2064: =item bold
                   2065: 
                   2066: =item h1
                   2067: 
                   2068: =item h2
                   2069: 
                   2070: =item h3
                   2071: 
1.256     matthew  2072: =item h4
                   2073: 
                   2074: =item i
                   2075: 
1.180     matthew  2076: =item date
                   2077: 
                   2078: =back
                   2079: 
                   2080: Inputs: $workbook
                   2081: 
                   2082: Returns: $format, a hash reference.
                   2083: 
1.1057    foxr     2084: 
1.180     matthew  2085: =cut
                   2086: 
                   2087: ###############################################################
                   2088: ###############################################################
                   2089: sub define_excel_formats {
                   2090:     my ($workbook) = @_;
                   2091:     my $format;
                   2092:     $format->{'header'} = $workbook->add_format(bold      => 1, 
                   2093:                                                 bottom    => 1,
                   2094:                                                 align     => 'center');
                   2095:     $format->{'bold'} = $workbook->add_format(bold=>1);
                   2096:     $format->{'h1'}   = $workbook->add_format(bold=>1, size=>18);
                   2097:     $format->{'h2'}   = $workbook->add_format(bold=>1, size=>16);
                   2098:     $format->{'h3'}   = $workbook->add_format(bold=>1, size=>14);
1.255     matthew  2099:     $format->{'h4'}   = $workbook->add_format(bold=>1, size=>12);
1.246     matthew  2100:     $format->{'i'}    = $workbook->add_format(italic=>1);
1.180     matthew  2101:     $format->{'date'} = $workbook->add_format(num_format=>
1.207     matthew  2102:                                             'mm/dd/yyyy hh:mm:ss');
1.180     matthew  2103:     return $format;
                   2104: }
                   2105: 
                   2106: ###############################################################
                   2107: ###############################################################
1.113     bowersj2 2108: 
                   2109: =pod
                   2110: 
1.648     raeburn  2111: =item * &create_workbook()
1.255     matthew  2112: 
                   2113: Create an Excel worksheet.  If it fails, output message on the
                   2114: request object and return undefs.
                   2115: 
                   2116: Inputs: Apache request object
                   2117: 
                   2118: Returns (undef) on failure, 
                   2119:     Excel worksheet object, scalar with filename, and formats 
                   2120:     from &Apache::loncommon::define_excel_formats on success
                   2121: 
                   2122: =cut
                   2123: 
                   2124: ###############################################################
                   2125: ###############################################################
                   2126: sub create_workbook {
                   2127:     my ($r) = @_;
                   2128:         #
                   2129:     # Create the excel spreadsheet
                   2130:     my $filename = '/prtspool/'.
1.258     albertel 2131:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.255     matthew  2132:         time.'_'.rand(1000000000).'.xls';
                   2133:     my $workbook  = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
                   2134:     if (! defined($workbook)) {
                   2135:         $r->log_error("Error creating excel spreadsheet $filename: $!");
1.928     bisitz   2136:         $r->print(
                   2137:             '<p class="LC_error">'
                   2138:            .&mt('Problems occurred in creating the new Excel file.')
                   2139:            .' '.&mt('This error has been logged.')
                   2140:            .' '.&mt('Please alert your LON-CAPA administrator.')
                   2141:            .'</p>'
                   2142:         );
1.255     matthew  2143:         return (undef);
                   2144:     }
                   2145:     #
1.1014    foxr     2146:     $workbook->set_tempdir(LONCAPA::tempdir());
1.255     matthew  2147:     #
                   2148:     my $format = &Apache::loncommon::define_excel_formats($workbook);
                   2149:     return ($workbook,$filename,$format);
                   2150: }
                   2151: 
                   2152: ###############################################################
                   2153: ###############################################################
                   2154: 
                   2155: =pod
                   2156: 
1.648     raeburn  2157: =item * &create_text_file()
1.113     bowersj2 2158: 
1.542     raeburn  2159: Create a file to write to and eventually make available to the user.
1.256     matthew  2160: If file creation fails, outputs an error message on the request object and 
                   2161: return undefs.
1.113     bowersj2 2162: 
1.256     matthew  2163: Inputs: Apache request object, and file suffix
1.113     bowersj2 2164: 
1.256     matthew  2165: Returns (undef) on failure, 
                   2166:     Filehandle and filename on success.
1.113     bowersj2 2167: 
                   2168: =cut
                   2169: 
1.256     matthew  2170: ###############################################################
                   2171: ###############################################################
                   2172: sub create_text_file {
                   2173:     my ($r,$suffix) = @_;
                   2174:     if (! defined($suffix)) { $suffix = 'txt'; };
                   2175:     my $fh;
                   2176:     my $filename = '/prtspool/'.
1.258     albertel 2177:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.256     matthew  2178:         time.'_'.rand(1000000000).'.'.$suffix;
                   2179:     $fh = Apache::File->new('>/home/httpd'.$filename);
                   2180:     if (! defined($fh)) {
                   2181:         $r->log_error("Couldn't open $filename for output $!");
1.928     bisitz   2182:         $r->print(
                   2183:             '<p class="LC_error">'
                   2184:            .&mt('Problems occurred in creating the output file.')
                   2185:            .' '.&mt('This error has been logged.')
                   2186:            .' '.&mt('Please alert your LON-CAPA administrator.')
                   2187:            .'</p>'
                   2188:         );
1.113     bowersj2 2189:     }
1.256     matthew  2190:     return ($fh,$filename)
1.113     bowersj2 2191: }
                   2192: 
                   2193: 
1.256     matthew  2194: =pod 
1.113     bowersj2 2195: 
                   2196: =back
                   2197: 
                   2198: =cut
1.37      matthew  2199: 
                   2200: ###############################################################
1.33      matthew  2201: ##        Home server <option> list generating code          ##
                   2202: ###############################################################
1.35      matthew  2203: 
1.169     www      2204: # ------------------------------------------
                   2205: 
                   2206: sub domain_select {
                   2207:     my ($name,$value,$multiple)=@_;
                   2208:     my %domains=map { 
1.514     albertel 2209: 	$_ => $_.' '. &Apache::lonnet::domain($_,'description') 
1.512     albertel 2210:     } &Apache::lonnet::all_domains();
1.169     www      2211:     if ($multiple) {
                   2212: 	$domains{''}=&mt('Any domain');
1.550     albertel 2213: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.287     albertel 2214: 	return &multiple_select_form($name,$value,4,\%domains);
1.169     www      2215:     } else {
1.550     albertel 2216: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.970     raeburn  2217: 	return &select_form($name,$value,\%domains);
1.169     www      2218:     }
                   2219: }
                   2220: 
1.282     albertel 2221: #-------------------------------------------
                   2222: 
                   2223: =pod
                   2224: 
1.519     raeburn  2225: =head1 Routines for form select boxes
                   2226: 
                   2227: =over 4
                   2228: 
1.648     raeburn  2229: =item * &multiple_select_form($name,$value,$size,$hash,$order)
1.282     albertel 2230: 
                   2231: Returns a string containing a <select> element int multiple mode
                   2232: 
                   2233: 
                   2234: Args:
                   2235:   $name - name of the <select> element
1.506     raeburn  2236:   $value - scalar or array ref of values that should already be selected
1.282     albertel 2237:   $size - number of rows long the select element is
1.283     albertel 2238:   $hash - the elements should be 'option' => 'shown text'
1.282     albertel 2239:           (shown text should already have been &mt())
1.506     raeburn  2240:   $order - (optional) array ref of the order to show the elements in
1.283     albertel 2241: 
1.282     albertel 2242: =cut
                   2243: 
                   2244: #-------------------------------------------
1.169     www      2245: sub multiple_select_form {
1.284     albertel 2246:     my ($name,$value,$size,$hash,$order)=@_;
1.169     www      2247:     my %selected = map { $_ => 1 } ref($value)?@{$value}:($value);
                   2248:     my $output='';
1.191     matthew  2249:     if (! defined($size)) {
                   2250:         $size = 4;
1.283     albertel 2251:         if (scalar(keys(%$hash))<4) {
                   2252:             $size = scalar(keys(%$hash));
1.191     matthew  2253:         }
                   2254:     }
1.734     bisitz   2255:     $output.="\n".'<select name="'.$name.'" size="'.$size.'" multiple="multiple">';
1.501     banghart 2256:     my @order;
1.506     raeburn  2257:     if (ref($order) eq 'ARRAY')  {
                   2258:         @order = @{$order};
                   2259:     } else {
                   2260:         @order = sort(keys(%$hash));
1.501     banghart 2261:     }
                   2262:     if (exists($$hash{'select_form_order'})) {
                   2263:         @order = @{$$hash{'select_form_order'}};
                   2264:     }
                   2265:         
1.284     albertel 2266:     foreach my $key (@order) {
1.356     albertel 2267:         $output.='<option value="'.&HTML::Entities::encode($key,'"<>&').'" ';
1.284     albertel 2268:         $output.='selected="selected" ' if ($selected{$key});
                   2269:         $output.='>'.$hash->{$key}."</option>\n";
1.169     www      2270:     }
                   2271:     $output.="</select>\n";
                   2272:     return $output;
                   2273: }
                   2274: 
1.88      www      2275: #-------------------------------------------
                   2276: 
                   2277: =pod
                   2278: 
1.1075.2.115  raeburn  2279: =item * &select_form($defdom,$name,$hashref,$onchange,$readonly)
1.88      www      2280: 
                   2281: Returns a string containing a <select name='$name' size='1'> form to 
1.970     raeburn  2282: allow a user to select options from a ref to a hash containing:
                   2283: option_name => displayed text. An optional $onchange can include
1.1075.2.115  raeburn  2284: a javascript onchange item, e.g., onchange="this.form.submit();".
                   2285: An optional arg -- $readonly -- if true will cause the select form
                   2286: to be disabled, e.g., for the case where an instructor has a section-
                   2287: specific role, and is viewing/modifying parameters.  
1.970     raeburn  2288: 
1.88      www      2289: See lonrights.pm for an example invocation and use.
                   2290: 
                   2291: =cut
                   2292: 
                   2293: #-------------------------------------------
                   2294: sub select_form {
1.1075.2.115  raeburn  2295:     my ($def,$name,$hashref,$onchange,$readonly) = @_;
1.970     raeburn  2296:     return unless (ref($hashref) eq 'HASH');
                   2297:     if ($onchange) {
                   2298:         $onchange = ' onchange="'.$onchange.'"';
                   2299:     }
1.1075.2.129  raeburn  2300:     my $disabled;
                   2301:     if ($readonly) {
                   2302:         $disabled = ' disabled="disabled"';
                   2303:     }
                   2304:     my $selectform = "<select name=\"$name\" size=\"1\"$onchange$disabled>\n";
1.128     albertel 2305:     my @keys;
1.970     raeburn  2306:     if (exists($hashref->{'select_form_order'})) {
                   2307: 	@keys=@{$hashref->{'select_form_order'}};
1.128     albertel 2308:     } else {
1.970     raeburn  2309: 	@keys=sort(keys(%{$hashref}));
1.128     albertel 2310:     }
1.356     albertel 2311:     foreach my $key (@keys) {
                   2312:         $selectform.=
                   2313: 	    '<option value="'.&HTML::Entities::encode($key,'"<>&').'" '.
                   2314:             ($key eq $def ? 'selected="selected" ' : '').
1.970     raeburn  2315:                 ">".$hashref->{$key}."</option>\n";
1.88      www      2316:     }
                   2317:     $selectform.="</select>";
                   2318:     return $selectform;
                   2319: }
                   2320: 
1.475     www      2321: # For display filters
                   2322: 
                   2323: sub display_filter {
1.1074    raeburn  2324:     my ($context) = @_;
1.475     www      2325:     if (!$env{'form.show'}) { $env{'form.show'}=10; }
1.477     www      2326:     if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
1.1074    raeburn  2327:     my $phraseinput = 'hidden';
                   2328:     my $includeinput = 'hidden';
                   2329:     my ($checked,$includetypestext);
                   2330:     if ($env{'form.displayfilter'} eq 'containing') {
                   2331:         $phraseinput = 'text'; 
                   2332:         if ($context eq 'parmslog') {
                   2333:             $includeinput = 'checkbox';
                   2334:             if ($env{'form.includetypes'}) {
                   2335:                 $checked = ' checked="checked"';
                   2336:             }
                   2337:             $includetypestext = &mt('Include parameter types');
                   2338:         }
                   2339:     } else {
                   2340:         $includetypestext = '&nbsp;';
                   2341:     }
                   2342:     my ($additional,$secondid,$thirdid);
                   2343:     if ($context eq 'parmslog') {
                   2344:         $additional = 
                   2345:             '<label><input type="'.$includeinput.'" name="includetypes"'. 
                   2346:             $checked.' name="includetypes" value="1" id="includetypes" />'.
                   2347:             '&nbsp;<span id="includetypestext">'.$includetypestext.'</span>'.
                   2348:             '</label>';
                   2349:         $secondid = 'includetypes';
                   2350:         $thirdid = 'includetypestext';
                   2351:     }
                   2352:     my $onchange = "javascript:toggleHistoryOptions(this,'containingphrase','$context',
                   2353:                                                     '$secondid','$thirdid')";
                   2354:     return '<span class="LC_nobreak"><label>'.&mt('Records: [_1]',
1.475     www      2355: 			       &Apache::lonmeta::selectbox('show',$env{'form.show'},undef,
                   2356: 							   (&mt('all'),10,20,50,100,1000,10000))).
1.714     bisitz   2357: 	   '</label></span> <span class="LC_nobreak">'.
1.1074    raeburn  2358:            &mt('Filter: [_1]',
1.477     www      2359: 	   &select_form($env{'form.displayfilter'},
                   2360: 			'displayfilter',
1.970     raeburn  2361: 			{'currentfolder' => 'Current folder/page',
1.477     www      2362: 			 'containing' => 'Containing phrase',
1.1074    raeburn  2363: 			 'none' => 'None'},$onchange)).'&nbsp;'.
                   2364: 			 '<input type="'.$phraseinput.'" name="containingphrase" id="containingphrase" size="30" value="'.
                   2365:                          &HTML::Entities::encode($env{'form.containingphrase'}).
                   2366:                          '" />'.$additional;
                   2367: }
                   2368: 
                   2369: sub display_filter_js {
                   2370:     my $includetext = &mt('Include parameter types');
                   2371:     return <<"ENDJS";
                   2372:   
                   2373: function toggleHistoryOptions(setter,firstid,context,secondid,thirdid) {
                   2374:     var firstType = 'hidden';
                   2375:     if (setter.options[setter.selectedIndex].value == 'containing') {
                   2376:         firstType = 'text';
                   2377:     }
                   2378:     firstObject = document.getElementById(firstid);
                   2379:     if (typeof(firstObject) == 'object') {
                   2380:         if (firstObject.type != firstType) {
                   2381:             changeInputType(firstObject,firstType);
                   2382:         }
                   2383:     }
                   2384:     if (context == 'parmslog') {
                   2385:         var secondType = 'hidden';
                   2386:         if (firstType == 'text') {
                   2387:             secondType = 'checkbox';
                   2388:         }
                   2389:         secondObject = document.getElementById(secondid);  
                   2390:         if (typeof(secondObject) == 'object') {
                   2391:             if (secondObject.type != secondType) {
                   2392:                 changeInputType(secondObject,secondType);
                   2393:             }
                   2394:         }
                   2395:         var textItem = document.getElementById(thirdid);
                   2396:         var currtext = textItem.innerHTML;
                   2397:         var newtext;
                   2398:         if (firstType == 'text') {
                   2399:             newtext = '$includetext';
                   2400:         } else {
                   2401:             newtext = '&nbsp;';
                   2402:         }
                   2403:         if (currtext != newtext) {
                   2404:             textItem.innerHTML = newtext;
                   2405:         }
                   2406:     }
                   2407:     return;
                   2408: }
                   2409: 
                   2410: function changeInputType(oldObject,newType) {
                   2411:     var newObject = document.createElement('input');
                   2412:     newObject.type = newType;
                   2413:     if (oldObject.size) {
                   2414:         newObject.size = oldObject.size;
                   2415:     }
                   2416:     if (oldObject.value) {
                   2417:         newObject.value = oldObject.value;
                   2418:     }
                   2419:     if (oldObject.name) {
                   2420:         newObject.name = oldObject.name;
                   2421:     }
                   2422:     if (oldObject.id) {
                   2423:         newObject.id = oldObject.id;
                   2424:     }
                   2425:     oldObject.parentNode.replaceChild(newObject,oldObject);
                   2426:     return;
                   2427: }
                   2428: 
                   2429: ENDJS
1.475     www      2430: }
                   2431: 
1.167     www      2432: sub gradeleveldescription {
                   2433:     my $gradelevel=shift;
                   2434:     my %gradelevels=(0 => 'Not specified',
                   2435: 		     1 => 'Grade 1',
                   2436: 		     2 => 'Grade 2',
                   2437: 		     3 => 'Grade 3',
                   2438: 		     4 => 'Grade 4',
                   2439: 		     5 => 'Grade 5',
                   2440: 		     6 => 'Grade 6',
                   2441: 		     7 => 'Grade 7',
                   2442: 		     8 => 'Grade 8',
                   2443: 		     9 => 'Grade 9',
                   2444: 		     10 => 'Grade 10',
                   2445: 		     11 => 'Grade 11',
                   2446: 		     12 => 'Grade 12',
                   2447: 		     13 => 'Grade 13',
                   2448: 		     14 => '100 Level',
                   2449: 		     15 => '200 Level',
                   2450: 		     16 => '300 Level',
                   2451: 		     17 => '400 Level',
                   2452: 		     18 => 'Graduate Level');
                   2453:     return &mt($gradelevels{$gradelevel});
                   2454: }
                   2455: 
1.163     www      2456: sub select_level_form {
                   2457:     my ($deflevel,$name)=@_;
                   2458:     unless ($deflevel) { $deflevel=0; }
1.167     www      2459:     my $selectform = "<select name=\"$name\" size=\"1\">\n";
                   2460:     for (my $i=0; $i<=18; $i++) {
                   2461:         $selectform.="<option value=\"$i\" ".
1.253     albertel 2462:             ($i==$deflevel ? 'selected="selected" ' : '').
1.167     www      2463:                 ">".&gradeleveldescription($i)."</option>\n";
                   2464:     }
                   2465:     $selectform.="</select>";
                   2466:     return $selectform;
1.163     www      2467: }
1.167     www      2468: 
1.35      matthew  2469: #-------------------------------------------
                   2470: 
1.45      matthew  2471: =pod
                   2472: 
1.1075.2.115  raeburn  2473: =item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms,$disabled)
1.35      matthew  2474: 
                   2475: Returns a string containing a <select name='$name' size='1'> form to 
                   2476: allow a user to select the domain to preform an operation in.  
                   2477: See loncreateuser.pm for an example invocation and use.
                   2478: 
1.90      www      2479: If the $includeempty flag is set, it also includes an empty choice ("no domain
                   2480: selected");
                   2481: 
1.743     raeburn  2482: If the $showdomdesc flag is set, the domain name is followed by the domain description.
                   2483: 
1.910     raeburn  2484: 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.
                   2485: 
1.1075.2.36  raeburn  2486: The optional $incdoms is a reference to an array of domains which will be the only available options.
                   2487: 
1.1075.2.115  raeburn  2488: The optional $excdoms is a reference to an array of domains which will be excluded from the available options.
                   2489: 
                   2490: The optional $disabled argument, if true, adds the disabled attribute to the select tag. 
1.563     raeburn  2491: 
1.35      matthew  2492: =cut
                   2493: 
                   2494: #-------------------------------------------
1.34      matthew  2495: sub select_dom_form {
1.1075.2.115  raeburn  2496:     my ($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms,$disabled) = @_;
1.872     raeburn  2497:     if ($onchange) {
1.874     raeburn  2498:         $onchange = ' onchange="'.$onchange.'"';
1.743     raeburn  2499:     }
1.1075.2.115  raeburn  2500:     if ($disabled) {
                   2501:         $disabled = ' disabled="disabled"';
                   2502:     }
1.1075.2.36  raeburn  2503:     my (@domains,%exclude);
1.910     raeburn  2504:     if (ref($incdoms) eq 'ARRAY') {
                   2505:         @domains = sort {lc($a) cmp lc($b)} (@{$incdoms});
                   2506:     } else {
                   2507:         @domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
                   2508:     }
1.90      www      2509:     if ($includeempty) { @domains=('',@domains); }
1.1075.2.36  raeburn  2510:     if (ref($excdoms) eq 'ARRAY') {
                   2511:         map { $exclude{$_} = 1; } @{$excdoms};
                   2512:     }
1.1075.2.115  raeburn  2513:     my $selectdomain = "<select name=\"$name\" size=\"1\"$onchange$disabled>\n";
1.356     albertel 2514:     foreach my $dom (@domains) {
1.1075.2.36  raeburn  2515:         next if ($exclude{$dom});
1.356     albertel 2516:         $selectdomain.="<option value=\"$dom\" ".
1.563     raeburn  2517:             ($dom eq $defdom ? 'selected="selected" ' : '').'>'.$dom;
                   2518:         if ($showdomdesc) {
                   2519:             if ($dom ne '') {
                   2520:                 my $domdesc = &Apache::lonnet::domain($dom,'description');
                   2521:                 if ($domdesc ne '') {
                   2522:                     $selectdomain .= ' ('.$domdesc.')';
                   2523:                 }
                   2524:             } 
                   2525:         }
                   2526:         $selectdomain .= "</option>\n";
1.34      matthew  2527:     }
                   2528:     $selectdomain.="</select>";
                   2529:     return $selectdomain;
                   2530: }
                   2531: 
1.35      matthew  2532: #-------------------------------------------
                   2533: 
1.45      matthew  2534: =pod
                   2535: 
1.648     raeburn  2536: =item * &home_server_form_item($domain,$name,$defaultflag)
1.35      matthew  2537: 
1.586     raeburn  2538: input: 4 arguments (two required, two optional) - 
                   2539:     $domain - domain of new user
                   2540:     $name - name of form element
                   2541:     $default - Value of 'default' causes a default item to be first 
                   2542:                             option, and selected by default. 
                   2543:     $hide - Value of 'hide' causes hiding of the name of the server, 
                   2544:                             if 1 server found, or default, if 0 found.
1.594     raeburn  2545: output: returns 2 items: 
1.586     raeburn  2546: (a) form element which contains either:
                   2547:    (i) <select name="$name">
                   2548:         <option value="$hostid1">$hostid $servers{$hostid}</option>
                   2549:         <option value="$hostid2">$hostid $servers{$hostid}</option>       
                   2550:        </select>
                   2551:        form item if there are multiple library servers in $domain, or
                   2552:    (ii) an <input type="hidden" name="$name" value="$hostid" /> form item 
                   2553:        if there is only one library server in $domain.
                   2554: 
                   2555: (b) number of library servers found.
                   2556: 
                   2557: See loncreateuser.pm for example of use.
1.35      matthew  2558: 
                   2559: =cut
                   2560: 
                   2561: #-------------------------------------------
1.586     raeburn  2562: sub home_server_form_item {
                   2563:     my ($domain,$name,$default,$hide) = @_;
1.513     albertel 2564:     my %servers = &Apache::lonnet::get_servers($domain,'library');
1.586     raeburn  2565:     my $result;
                   2566:     my $numlib = keys(%servers);
                   2567:     if ($numlib > 1) {
                   2568:         $result .= '<select name="'.$name.'" />'."\n";
                   2569:         if ($default) {
1.804     bisitz   2570:             $result .= '<option value="default" selected="selected">'.&mt('default').
1.586     raeburn  2571:                        '</option>'."\n";
                   2572:         }
                   2573:         foreach my $hostid (sort(keys(%servers))) {
                   2574:             $result.= '<option value="'.$hostid.'">'.
                   2575: 	              $hostid.' '.$servers{$hostid}."</option>\n";
                   2576:         }
                   2577:         $result .= '</select>'."\n";
                   2578:     } elsif ($numlib == 1) {
                   2579:         my $hostid;
                   2580:         foreach my $item (keys(%servers)) {
                   2581:             $hostid = $item;
                   2582:         }
                   2583:         $result .= '<input type="hidden" name="'.$name.'" value="'.
                   2584:                    $hostid.'" />';
                   2585:                    if (!$hide) {
                   2586:                        $result .= $hostid.' '.$servers{$hostid};
                   2587:                    }
                   2588:                    $result .= "\n";
                   2589:     } elsif ($default) {
                   2590:         $result .= '<input type="hidden" name="'.$name.
                   2591:                    '" value="default" />';
                   2592:                    if (!$hide) {
                   2593:                        $result .= &mt('default');
                   2594:                    }
                   2595:                    $result .= "\n";
1.33      matthew  2596:     }
1.586     raeburn  2597:     return ($result,$numlib);
1.33      matthew  2598: }
1.112     bowersj2 2599: 
                   2600: =pod
                   2601: 
1.534     albertel 2602: =back 
                   2603: 
1.112     bowersj2 2604: =cut
1.87      matthew  2605: 
                   2606: ###############################################################
1.112     bowersj2 2607: ##                  Decoding User Agent                      ##
1.87      matthew  2608: ###############################################################
                   2609: 
                   2610: =pod
                   2611: 
1.112     bowersj2 2612: =head1 Decoding the User Agent
                   2613: 
                   2614: =over 4
                   2615: 
                   2616: =item * &decode_user_agent()
1.87      matthew  2617: 
                   2618: Inputs: $r
                   2619: 
                   2620: Outputs:
                   2621: 
                   2622: =over 4
                   2623: 
1.112     bowersj2 2624: =item * $httpbrowser
1.87      matthew  2625: 
1.112     bowersj2 2626: =item * $clientbrowser
1.87      matthew  2627: 
1.112     bowersj2 2628: =item * $clientversion
1.87      matthew  2629: 
1.112     bowersj2 2630: =item * $clientmathml
1.87      matthew  2631: 
1.112     bowersj2 2632: =item * $clientunicode
1.87      matthew  2633: 
1.112     bowersj2 2634: =item * $clientos
1.87      matthew  2635: 
1.1075.2.42  raeburn  2636: =item * $clientmobile
                   2637: 
                   2638: =item * $clientinfo
                   2639: 
1.1075.2.77  raeburn  2640: =item * $clientosversion
                   2641: 
1.87      matthew  2642: =back
                   2643: 
1.157     matthew  2644: =back 
                   2645: 
1.87      matthew  2646: =cut
                   2647: 
                   2648: ###############################################################
                   2649: ###############################################################
                   2650: sub decode_user_agent {
1.247     albertel 2651:     my ($r)=@_;
1.87      matthew  2652:     my @browsertype=split(/\&/,$Apache::lonnet::perlvar{"lonBrowsDet"});
                   2653:     my %mathcap=split(/\&/,$$Apache::lonnet::perlvar{"lonMathML"});
                   2654:     my $httpbrowser=$ENV{"HTTP_USER_AGENT"};
1.247     albertel 2655:     if (!$httpbrowser && $r) { $httpbrowser=$r->header_in('User-Agent'); }
1.87      matthew  2656:     my $clientbrowser='unknown';
                   2657:     my $clientversion='0';
                   2658:     my $clientmathml='';
                   2659:     my $clientunicode='0';
1.1075.2.42  raeburn  2660:     my $clientmobile=0;
1.1075.2.77  raeburn  2661:     my $clientosversion='';
1.87      matthew  2662:     for (my $i=0;$i<=$#browsertype;$i++) {
1.1075.2.76  raeburn  2663:         my ($bname,$match,$notmatch,$vreg,$minv,$univ)=split(/\%/,$browsertype[$i]);
1.87      matthew  2664: 	if (($httpbrowser=~/$match/i)  && ($httpbrowser!~/$notmatch/i)) {
                   2665: 	    $clientbrowser=$bname;
                   2666:             $httpbrowser=~/$vreg/i;
                   2667: 	    $clientversion=$1;
                   2668:             $clientmathml=($clientversion>=$minv);
                   2669:             $clientunicode=($clientversion>=$univ);
                   2670: 	}
                   2671:     }
                   2672:     my $clientos='unknown';
1.1075.2.42  raeburn  2673:     my $clientinfo;
1.87      matthew  2674:     if (($httpbrowser=~/linux/i) ||
                   2675:         ($httpbrowser=~/unix/i) ||
                   2676:         ($httpbrowser=~/ux/i) ||
                   2677:         ($httpbrowser=~/solaris/i)) { $clientos='unix'; }
                   2678:     if (($httpbrowser=~/vax/i) ||
                   2679:         ($httpbrowser=~/vms/i)) { $clientos='vms'; }
                   2680:     if ($httpbrowser=~/next/i) { $clientos='next'; }
                   2681:     if (($httpbrowser=~/mac/i) ||
                   2682:         ($httpbrowser=~/powerpc/i)) { $clientos='mac'; }
1.1075.2.77  raeburn  2683:     if ($httpbrowser=~/win/i) {
                   2684:         $clientos='win';
                   2685:         if ($httpbrowser =~/Windows\s+NT\s+(\d+\.\d+)/i) {
                   2686:             $clientosversion = $1;
                   2687:         }
                   2688:     }
1.87      matthew  2689:     if ($httpbrowser=~/embed/i) { $clientos='pda'; }
1.1075.2.42  raeburn  2690:     if ($httpbrowser=~/(Android|iPod|iPad|iPhone|webOS|Blackberry|Windows Phone|Opera m(?:ob|in)|Fennec)/i) {
                   2691:         $clientmobile=lc($1);
                   2692:     }
                   2693:     if ($httpbrowser=~ m{Firefox/(\d+\.\d+)}) {
                   2694:         $clientinfo = 'firefox-'.$1;
                   2695:     } elsif ($httpbrowser=~ m{chromeframe/(\d+\.\d+)\.}) {
                   2696:         $clientinfo = 'chromeframe-'.$1;
                   2697:     }
1.87      matthew  2698:     return ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
1.1075.2.77  raeburn  2699:             $clientunicode,$clientos,$clientmobile,$clientinfo,
                   2700:             $clientosversion);
1.87      matthew  2701: }
                   2702: 
1.32      matthew  2703: ###############################################################
                   2704: ##    Authentication changing form generation subroutines    ##
                   2705: ###############################################################
                   2706: ##
                   2707: ## All of the authform_xxxxxxx subroutines take their inputs in a
                   2708: ## hash, and have reasonable default values.
                   2709: ##
                   2710: ##    formname = the name given in the <form> tag.
1.35      matthew  2711: #-------------------------------------------
                   2712: 
1.45      matthew  2713: =pod
                   2714: 
1.112     bowersj2 2715: =head1 Authentication Routines
                   2716: 
                   2717: =over 4
                   2718: 
1.648     raeburn  2719: =item * &authform_xxxxxx()
1.35      matthew  2720: 
                   2721: The authform_xxxxxx subroutines provide javascript and html forms which 
                   2722: handle some of the conveniences required for authentication forms.  
                   2723: This is not an optimal method, but it works.  
                   2724: 
                   2725: =over 4
                   2726: 
1.112     bowersj2 2727: =item * authform_header
1.35      matthew  2728: 
1.112     bowersj2 2729: =item * authform_authorwarning
1.35      matthew  2730: 
1.112     bowersj2 2731: =item * authform_nochange
1.35      matthew  2732: 
1.112     bowersj2 2733: =item * authform_kerberos
1.35      matthew  2734: 
1.112     bowersj2 2735: =item * authform_internal
1.35      matthew  2736: 
1.112     bowersj2 2737: =item * authform_filesystem
1.35      matthew  2738: 
1.1075.2.161.  .17(raeb 2739:-23): =item * authform_lti
                   2740:-23): 
1.35      matthew  2741: =back
                   2742: 
1.648     raeburn  2743: See loncreateuser.pm for invocation and use examples.
1.157     matthew  2744: 
1.35      matthew  2745: =cut
                   2746: 
                   2747: #-------------------------------------------
1.32      matthew  2748: sub authform_header{  
                   2749:     my %in = (
                   2750:         formname => 'cu',
1.80      albertel 2751:         kerb_def_dom => '',
1.32      matthew  2752:         @_,
                   2753:     );
                   2754:     $in{'formname'} = 'document.' . $in{'formname'};
                   2755:     my $result='';
1.80      albertel 2756: 
                   2757: #---------------------------------------------- Code for upper case translation
                   2758:     my $Javascript_toUpperCase;
                   2759:     unless ($in{kerb_def_dom}) {
                   2760:         $Javascript_toUpperCase =<<"END";
                   2761:         switch (choice) {
                   2762:            case 'krb': currentform.elements[choicearg].value =
                   2763:                currentform.elements[choicearg].value.toUpperCase();
                   2764:                break;
                   2765:            default:
                   2766:         }
                   2767: END
                   2768:     } else {
                   2769:         $Javascript_toUpperCase = "";
                   2770:     }
                   2771: 
1.165     raeburn  2772:     my $radioval = "'nochange'";
1.591     raeburn  2773:     if (defined($in{'curr_authtype'})) {
                   2774:         if ($in{'curr_authtype'} ne '') {
                   2775:             $radioval = "'".$in{'curr_authtype'}."arg'";
                   2776:         }
1.174     matthew  2777:     }
1.165     raeburn  2778:     my $argfield = 'null';
1.591     raeburn  2779:     if (defined($in{'mode'})) {
1.165     raeburn  2780:         if ($in{'mode'} eq 'modifycourse')  {
1.591     raeburn  2781:             if (defined($in{'curr_autharg'})) {
                   2782:                 if ($in{'curr_autharg'} ne '') {
1.165     raeburn  2783:                     $argfield = "'$in{'curr_autharg'}'";
                   2784:                 }
                   2785:             }
                   2786:         }
                   2787:     }
                   2788: 
1.32      matthew  2789:     $result.=<<"END";
                   2790: var current = new Object();
1.165     raeburn  2791: current.radiovalue = $radioval;
                   2792: current.argfield = $argfield;
1.32      matthew  2793: 
                   2794: function changed_radio(choice,currentform) {
                   2795:     var choicearg = choice + 'arg';
                   2796:     // If a radio button in changed, we need to change the argfield
                   2797:     if (current.radiovalue != choice) {
                   2798:         current.radiovalue = choice;
                   2799:         if (current.argfield != null) {
                   2800:             currentform.elements[current.argfield].value = '';
                   2801:         }
                   2802:         if (choice == 'nochange') {
                   2803:             current.argfield = null;
                   2804:         } else {
                   2805:             current.argfield = choicearg;
                   2806:             switch(choice) {
                   2807:                 case 'krb': 
                   2808:                     currentform.elements[current.argfield].value = 
                   2809:                         "$in{'kerb_def_dom'}";
                   2810:                 break;
                   2811:               default:
                   2812:                 break;
                   2813:             }
                   2814:         }
                   2815:     }
                   2816:     return;
                   2817: }
1.22      www      2818: 
1.32      matthew  2819: function changed_text(choice,currentform) {
                   2820:     var choicearg = choice + 'arg';
                   2821:     if (currentform.elements[choicearg].value !='') {
1.80      albertel 2822:         $Javascript_toUpperCase
1.32      matthew  2823:         // clear old field
                   2824:         if ((current.argfield != choicearg) && (current.argfield != null)) {
                   2825:             currentform.elements[current.argfield].value = '';
                   2826:         }
                   2827:         current.argfield = choicearg;
                   2828:     }
                   2829:     set_auth_radio_buttons(choice,currentform);
                   2830:     return;
1.20      www      2831: }
1.32      matthew  2832: 
                   2833: function set_auth_radio_buttons(newvalue,currentform) {
1.986     raeburn  2834:     var numauthchoices = currentform.login.length;
                   2835:     if (typeof numauthchoices  == "undefined") {
                   2836:         return;
                   2837:     } 
1.32      matthew  2838:     var i=0;
1.986     raeburn  2839:     while (i < numauthchoices) {
1.32      matthew  2840:         if (currentform.login[i].value == newvalue) { break; }
                   2841:         i++;
                   2842:     }
1.986     raeburn  2843:     if (i == numauthchoices) {
1.32      matthew  2844:         return;
                   2845:     }
                   2846:     current.radiovalue = newvalue;
                   2847:     currentform.login[i].checked = true;
                   2848:     return;
                   2849: }
                   2850: END
                   2851:     return $result;
                   2852: }
                   2853: 
1.1075.2.20  raeburn  2854: sub authform_authorwarning {
1.32      matthew  2855:     my $result='';
1.144     matthew  2856:     $result='<i>'.
                   2857:         &mt('As a general rule, only authors or co-authors should be '.
                   2858:             'filesystem authenticated '.
                   2859:             '(which allows access to the server filesystem).')."</i>\n";
1.32      matthew  2860:     return $result;
                   2861: }
                   2862: 
1.1075.2.20  raeburn  2863: sub authform_nochange {
1.32      matthew  2864:     my %in = (
                   2865:               formname => 'document.cu',
                   2866:               kerb_def_dom => 'MSU.EDU',
                   2867:               @_,
                   2868:           );
1.1075.2.20  raeburn  2869:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'}); 
1.586     raeburn  2870:     my $result;
1.1075.2.20  raeburn  2871:     if (!$authnum) {
                   2872:         $result = &mt('Under your current role you are not permitted to change login settings for this user');
1.586     raeburn  2873:     } else {
                   2874:         $result = '<label>'.&mt('[_1] Do not change login data',
                   2875:                   '<input type="radio" name="login" value="nochange" '.
                   2876:                   'checked="checked" onclick="'.
1.281     albertel 2877:             "javascript:changed_radio('nochange',$in{'formname'});".'" />').
                   2878: 	    '</label>';
1.586     raeburn  2879:     }
1.32      matthew  2880:     return $result;
                   2881: }
                   2882: 
1.591     raeburn  2883: sub authform_kerberos {
1.32      matthew  2884:     my %in = (
                   2885:               formname => 'document.cu',
                   2886:               kerb_def_dom => 'MSU.EDU',
1.80      albertel 2887:               kerb_def_auth => 'krb4',
1.32      matthew  2888:               @_,
                   2889:               );
1.586     raeburn  2890:     my ($check4,$check5,$krbcheck,$krbarg,$krbver,$result,$authtype,
1.1075.2.117  raeburn  2891:         $autharg,$jscall,$disabled);
1.1075.2.20  raeburn  2892:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.80      albertel 2893:     if ($in{'kerb_def_auth'} eq 'krb5') {
1.772     bisitz   2894:        $check5 = ' checked="checked"';
1.80      albertel 2895:     } else {
1.772     bisitz   2896:        $check4 = ' checked="checked"';
1.80      albertel 2897:     }
1.1075.2.117  raeburn  2898:     if ($in{'readonly'}) {
                   2899:         $disabled = ' disabled="disabled"';
                   2900:     }
1.165     raeburn  2901:     $krbarg = $in{'kerb_def_dom'};
1.591     raeburn  2902:     if (defined($in{'curr_authtype'})) {
                   2903:         if ($in{'curr_authtype'} eq 'krb') {
1.772     bisitz   2904:             $krbcheck = ' checked="checked"';
1.623     raeburn  2905:             if (defined($in{'mode'})) {
                   2906:                 if ($in{'mode'} eq 'modifyuser') {
                   2907:                     $krbcheck = '';
                   2908:                 }
                   2909:             }
1.591     raeburn  2910:             if (defined($in{'curr_kerb_ver'})) {
                   2911:                 if ($in{'curr_krb_ver'} eq '5') {
1.772     bisitz   2912:                     $check5 = ' checked="checked"';
1.591     raeburn  2913:                     $check4 = '';
                   2914:                 } else {
1.772     bisitz   2915:                     $check4 = ' checked="checked"';
1.591     raeburn  2916:                     $check5 = '';
                   2917:                 }
1.586     raeburn  2918:             }
1.591     raeburn  2919:             if (defined($in{'curr_autharg'})) {
1.165     raeburn  2920:                 $krbarg = $in{'curr_autharg'};
                   2921:             }
1.586     raeburn  2922:             if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
1.591     raeburn  2923:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2924:                     $result = 
                   2925:     &mt('Currently Kerberos authenticated with domain [_1] Version [_2].',
                   2926:         $in{'curr_autharg'},$krbver);
                   2927:                 } else {
                   2928:                     $result =
                   2929:     &mt('Currently Kerberos authenticated, Version [_1].',$krbver);
                   2930:                 }
                   2931:                 return $result; 
                   2932:             }
                   2933:         }
                   2934:     } else {
                   2935:         if ($authnum == 1) {
1.784     bisitz   2936:             $authtype = '<input type="hidden" name="login" value="krb" />';
1.165     raeburn  2937:         }
                   2938:     }
1.586     raeburn  2939:     if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
                   2940:         return;
1.587     raeburn  2941:     } elsif ($authtype eq '') {
1.591     raeburn  2942:         if (defined($in{'mode'})) {
1.587     raeburn  2943:             if ($in{'mode'} eq 'modifycourse') {
                   2944:                 if ($authnum == 1) {
1.1075.2.117  raeburn  2945:                     $authtype = '<input type="radio" name="login" value="krb"'.$disabled.' />';
1.587     raeburn  2946:                 }
                   2947:             }
                   2948:         }
1.586     raeburn  2949:     }
                   2950:     $jscall = "javascript:changed_radio('krb',$in{'formname'});";
                   2951:     if ($authtype eq '') {
                   2952:         $authtype = '<input type="radio" name="login" value="krb" '.
                   2953:                     'onclick="'.$jscall.'" onchange="'.$jscall.'"'.
1.1075.2.117  raeburn  2954:                     $krbcheck.$disabled.' />';
1.586     raeburn  2955:     }
                   2956:     if (($can_assign{'krb4'} && $can_assign{'krb5'}) ||
1.1075.2.20  raeburn  2957:         ($can_assign{'krb4'} && !$can_assign{'krb5'} &&
1.586     raeburn  2958:          $in{'curr_authtype'} eq 'krb5') ||
1.1075.2.20  raeburn  2959:         (!$can_assign{'krb4'} && $can_assign{'krb5'} &&
1.586     raeburn  2960:          $in{'curr_authtype'} eq 'krb4')) {
                   2961:         $result .= &mt
1.144     matthew  2962:         ('[_1] Kerberos authenticated with domain [_2] '.
1.281     albertel 2963:          '[_3] Version 4 [_4] Version 5 [_5]',
1.586     raeburn  2964:          '<label>'.$authtype,
1.281     albertel 2965:          '</label><input type="text" size="10" name="krbarg" '.
1.165     raeburn  2966:              'value="'.$krbarg.'" '.
1.1075.2.117  raeburn  2967:              'onchange="'.$jscall.'"'.$disabled.' />',
                   2968:          '<label><input type="radio" name="krbver" value="4" '.$check4.$disabled.' />',
                   2969:          '</label><label><input type="radio" name="krbver" value="5" '.$check5.$disabled.' />',
1.281     albertel 2970: 	 '</label>');
1.586     raeburn  2971:     } elsif ($can_assign{'krb4'}) {
                   2972:         $result .= &mt
                   2973:         ('[_1] Kerberos authenticated with domain [_2] '.
                   2974:          '[_3] Version 4 [_4]',
                   2975:          '<label>'.$authtype,
                   2976:          '</label><input type="text" size="10" name="krbarg" '.
                   2977:              'value="'.$krbarg.'" '.
1.1075.2.117  raeburn  2978:              'onchange="'.$jscall.'"'.$disabled.' />',
1.586     raeburn  2979:          '<label><input type="hidden" name="krbver" value="4" />',
                   2980:          '</label>');
                   2981:     } elsif ($can_assign{'krb5'}) {
                   2982:         $result .= &mt
                   2983:         ('[_1] Kerberos authenticated with domain [_2] '.
                   2984:          '[_3] Version 5 [_4]',
                   2985:          '<label>'.$authtype,
                   2986:          '</label><input type="text" size="10" name="krbarg" '.
                   2987:              'value="'.$krbarg.'" '.
1.1075.2.117  raeburn  2988:              'onchange="'.$jscall.'"'.$disabled.' />',
1.586     raeburn  2989:          '<label><input type="hidden" name="krbver" value="5" />',
                   2990:          '</label>');
                   2991:     }
1.32      matthew  2992:     return $result;
                   2993: }
                   2994: 
1.1075.2.20  raeburn  2995: sub authform_internal {
1.586     raeburn  2996:     my %in = (
1.32      matthew  2997:                 formname => 'document.cu',
                   2998:                 kerb_def_dom => 'MSU.EDU',
                   2999:                 @_,
                   3000:                 );
1.1075.2.117  raeburn  3001:     my ($intcheck,$intarg,$result,$authtype,$autharg,$jscall,$disabled);
1.1075.2.20  raeburn  3002:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.1075.2.117  raeburn  3003:     if ($in{'readonly'}) {
                   3004:         $disabled = ' disabled="disabled"';
                   3005:     }
1.591     raeburn  3006:     if (defined($in{'curr_authtype'})) {
                   3007:         if ($in{'curr_authtype'} eq 'int') {
1.586     raeburn  3008:             if ($can_assign{'int'}) {
1.772     bisitz   3009:                 $intcheck = 'checked="checked" ';
1.623     raeburn  3010:                 if (defined($in{'mode'})) {
                   3011:                     if ($in{'mode'} eq 'modifyuser') {
                   3012:                         $intcheck = '';
                   3013:                     }
                   3014:                 }
1.591     raeburn  3015:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  3016:                     $intarg = $in{'curr_autharg'};
                   3017:                 }
                   3018:             } else {
                   3019:                 $result = &mt('Currently internally authenticated.');
                   3020:                 return $result;
1.165     raeburn  3021:             }
                   3022:         }
1.586     raeburn  3023:     } else {
                   3024:         if ($authnum == 1) {
1.784     bisitz   3025:             $authtype = '<input type="hidden" name="login" value="int" />';
1.586     raeburn  3026:         }
                   3027:     }
                   3028:     if (!$can_assign{'int'}) {
                   3029:         return;
1.587     raeburn  3030:     } elsif ($authtype eq '') {
1.591     raeburn  3031:         if (defined($in{'mode'})) {
1.587     raeburn  3032:             if ($in{'mode'} eq 'modifycourse') {
                   3033:                 if ($authnum == 1) {
1.1075.2.117  raeburn  3034:                     $authtype = '<input type="radio" name="login" value="int"'.$disabled.' />';
1.587     raeburn  3035:                 }
                   3036:             }
                   3037:         }
1.165     raeburn  3038:     }
1.586     raeburn  3039:     $jscall = "javascript:changed_radio('int',$in{'formname'});";
                   3040:     if ($authtype eq '') {
                   3041:         $authtype = '<input type="radio" name="login" value="int" '.$intcheck.
1.1075.2.117  raeburn  3042:                     ' onchange="'.$jscall.'" onclick="'.$jscall.'"'.$disabled.' />';
1.586     raeburn  3043:     }
1.605     bisitz   3044:     $autharg = '<input type="password" size="10" name="intarg" value="'.
1.1075.2.117  raeburn  3045:                $intarg.'" onchange="'.$jscall.'"'.$disabled.' />';
1.586     raeburn  3046:     $result = &mt
1.144     matthew  3047:         ('[_1] Internally authenticated (with initial password [_2])',
1.586     raeburn  3048:          '<label>'.$authtype,'</label>'.$autharg);
1.1075.2.118  raeburn  3049:     $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  3050:     return $result;
                   3051: }
                   3052: 
1.1075.2.20  raeburn  3053: sub authform_local {
1.32      matthew  3054:     my %in = (
                   3055:               formname => 'document.cu',
                   3056:               kerb_def_dom => 'MSU.EDU',
                   3057:               @_,
                   3058:               );
1.1075.2.117  raeburn  3059:     my ($loccheck,$locarg,$result,$authtype,$autharg,$jscall,$disabled);
1.1075.2.20  raeburn  3060:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.1075.2.117  raeburn  3061:     if ($in{'readonly'}) {
                   3062:         $disabled = ' disabled="disabled"';
                   3063:     }
1.591     raeburn  3064:     if (defined($in{'curr_authtype'})) {
                   3065:         if ($in{'curr_authtype'} eq 'loc') {
1.586     raeburn  3066:             if ($can_assign{'loc'}) {
1.772     bisitz   3067:                 $loccheck = 'checked="checked" ';
1.623     raeburn  3068:                 if (defined($in{'mode'})) {
                   3069:                     if ($in{'mode'} eq 'modifyuser') {
                   3070:                         $loccheck = '';
                   3071:                     }
                   3072:                 }
1.591     raeburn  3073:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  3074:                     $locarg = $in{'curr_autharg'};
                   3075:                 }
                   3076:             } else {
                   3077:                 $result = &mt('Currently using local (institutional) authentication.');
                   3078:                 return $result;
1.165     raeburn  3079:             }
                   3080:         }
1.586     raeburn  3081:     } else {
                   3082:         if ($authnum == 1) {
1.784     bisitz   3083:             $authtype = '<input type="hidden" name="login" value="loc" />';
1.586     raeburn  3084:         }
                   3085:     }
                   3086:     if (!$can_assign{'loc'}) {
                   3087:         return;
1.587     raeburn  3088:     } elsif ($authtype eq '') {
1.591     raeburn  3089:         if (defined($in{'mode'})) {
1.587     raeburn  3090:             if ($in{'mode'} eq 'modifycourse') {
                   3091:                 if ($authnum == 1) {
1.1075.2.117  raeburn  3092:                     $authtype = '<input type="radio" name="login" value="loc"'.$disabled.' />';
1.587     raeburn  3093:                 }
                   3094:             }
                   3095:         }
1.165     raeburn  3096:     }
1.586     raeburn  3097:     $jscall = "javascript:changed_radio('loc',$in{'formname'});";
                   3098:     if ($authtype eq '') {
                   3099:         $authtype = '<input type="radio" name="login" value="loc" '.
                   3100:                     $loccheck.' onchange="'.$jscall.'" onclick="'.
1.1075.2.117  raeburn  3101:                     $jscall.'"'.$disabled.' />';
1.586     raeburn  3102:     }
                   3103:     $autharg = '<input type="text" size="10" name="locarg" value="'.
1.1075.2.117  raeburn  3104:                $locarg.'" onchange="'.$jscall.'"'.$disabled.' />';
1.586     raeburn  3105:     $result = &mt('[_1] Local Authentication with argument [_2]',
                   3106:                   '<label>'.$authtype,'</label>'.$autharg);
1.32      matthew  3107:     return $result;
                   3108: }
                   3109: 
1.1075.2.20  raeburn  3110: sub authform_filesystem {
1.32      matthew  3111:     my %in = (
                   3112:               formname => 'document.cu',
                   3113:               kerb_def_dom => 'MSU.EDU',
                   3114:               @_,
                   3115:               );
1.1075.2.117  raeburn  3116:     my ($fsyscheck,$result,$authtype,$autharg,$jscall,$disabled);
1.1075.2.20  raeburn  3117:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.1075.2.117  raeburn  3118:     if ($in{'readonly'}) {
                   3119:         $disabled = ' disabled="disabled"';
                   3120:     }
1.591     raeburn  3121:     if (defined($in{'curr_authtype'})) {
                   3122:         if ($in{'curr_authtype'} eq 'fsys') {
1.586     raeburn  3123:             if ($can_assign{'fsys'}) {
1.772     bisitz   3124:                 $fsyscheck = 'checked="checked" ';
1.623     raeburn  3125:                 if (defined($in{'mode'})) {
                   3126:                     if ($in{'mode'} eq 'modifyuser') {
                   3127:                         $fsyscheck = '';
                   3128:                     }
                   3129:                 }
1.586     raeburn  3130:             } else {
                   3131:                 $result = &mt('Currently Filesystem Authenticated.');
                   3132:                 return $result;
                   3133:             }           
                   3134:         }
                   3135:     } else {
                   3136:         if ($authnum == 1) {
1.784     bisitz   3137:             $authtype = '<input type="hidden" name="login" value="fsys" />';
1.586     raeburn  3138:         }
                   3139:     }
                   3140:     if (!$can_assign{'fsys'}) {
                   3141:         return;
1.587     raeburn  3142:     } elsif ($authtype eq '') {
1.591     raeburn  3143:         if (defined($in{'mode'})) {
1.587     raeburn  3144:             if ($in{'mode'} eq 'modifycourse') {
                   3145:                 if ($authnum == 1) {
1.1075.2.117  raeburn  3146:                     $authtype = '<input type="radio" name="login" value="fsys"'.$disabled.' />';
1.587     raeburn  3147:                 }
                   3148:             }
                   3149:         }
1.586     raeburn  3150:     }
                   3151:     $jscall = "javascript:changed_radio('fsys',$in{'formname'});";
                   3152:     if ($authtype eq '') {
                   3153:         $authtype = '<input type="radio" name="login" value="fsys" '.
                   3154:                     $fsyscheck.' onchange="'.$jscall.'" onclick="'.
1.1075.2.117  raeburn  3155:                     $jscall.'"'.$disabled.' />';
1.586     raeburn  3156:     }
1.1075.2.158  raeburn  3157:     $autharg = '<input type="password" size="10" name="fsysarg" value=""'.
1.1075.2.117  raeburn  3158:                ' onchange="'.$jscall.'"'.$disabled.' />';
1.586     raeburn  3159:     $result = &mt
1.144     matthew  3160:         ('[_1] Filesystem Authenticated (with initial password [_2])',
1.1075.2.158  raeburn  3161:          '<label>'.$authtype,'</label>'.$autharg);
1.32      matthew  3162:     return $result;
                   3163: }
                   3164: 
1.1075.2.161.  .17(raeb 3165:-23): sub authform_lti {
                   3166:-23):     my %in = (
                   3167:-23):               formname => 'document.cu',
                   3168:-23):               kerb_def_dom => 'MSU.EDU',
                   3169:-23):               @_,
                   3170:-23):               );
                   3171:-23):     my ($lticheck,$result,$authtype,$autharg,$jscall,$disabled);
                   3172:-23):     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
                   3173:-23):     if ($in{'readonly'}) {
                   3174:-23):         $disabled = ' disabled="disabled"';
                   3175:-23):     }
                   3176:-23):     if (defined($in{'curr_authtype'})) {
                   3177:-23):         if ($in{'curr_authtype'} eq 'lti') {
                   3178:-23):             if ($can_assign{'lti'}) {
                   3179:-23):                 $lticheck = 'checked="checked" ';
                   3180:-23):                 if (defined($in{'mode'})) {
                   3181:-23):                     if ($in{'mode'} eq 'modifyuser') {
                   3182:-23):                         $lticheck = '';
                   3183:-23):                     }
                   3184:-23):                 }
                   3185:-23):             } else {
                   3186:-23):                 $result = &mt('Currently LTI Authenticated.');
                   3187:-23):                 return $result;
                   3188:-23):             }
                   3189:-23):         }
                   3190:-23):     } else {
                   3191:-23):         if ($authnum == 1) {
                   3192:-23):             $authtype = '<input type="hidden" name="login" value="lti" />';
                   3193:-23):         }
                   3194:-23):     }
                   3195:-23):     if (!$can_assign{'lti'}) {
                   3196:-23):         return;
                   3197:-23):     } elsif ($authtype eq '') {
                   3198:-23):         if (defined($in{'mode'})) {
                   3199:-23):             if ($in{'mode'} eq 'modifycourse') {
                   3200:-23):                 if ($authnum == 1) {
                   3201:-23):                     $authtype = '<input type="radio" name="login" value="lti"'.$disabled.' />';
                   3202:-23):                 }
                   3203:-23):             }
                   3204:-23):         }
                   3205:-23):     }
                   3206:-23):     $jscall = "javascript:changed_radio('lti',$in{'formname'});";
                   3207:-23):     if (($authtype eq '') && (($in{'mode'} eq 'modifycourse') || ($in{'curr_authtype'} ne 'lti'))) {
                   3208:-23):         $authtype = '<input type="radio" name="login" value="lti" '.
                   3209:-23):                     $lticheck.' onchange="'.$jscall.'" onclick="'.
                   3210:-23):                     $jscall.'"'.$disabled.' />';
                   3211:-23):     }
                   3212:-23):     $autharg = '<input type="hidden" name="ltiarg" value="" />';
                   3213:-23):     if ($authtype) {
                   3214:-23):         $result = &mt('[_1] LTI Authenticated',
                   3215:-23):                       '<label>'.$authtype.'</label>'.$autharg);
                   3216:-23):     } else {
                   3217:-23):         $result = '<b>'.&mt('LTI Authenticated').'</b>'.
                   3218:-23):                   $autharg;
                   3219:-23):     }
                   3220:-23):     return $result;
                   3221:-23): }
                   3222:-23): 
1.586     raeburn  3223: sub get_assignable_auth {
                   3224:     my ($dom) = @_;
                   3225:     if ($dom eq '') {
                   3226:         $dom = $env{'request.role.domain'};
                   3227:     }
                   3228:     my %can_assign = (
                   3229:                           krb4 => 1,
                   3230:                           krb5 => 1,
                   3231:                           int  => 1,
                   3232:                           loc  => 1,
                   3233:                      );
                   3234:     my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);
                   3235:     if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   3236:         if (ref($domconfig{'usercreation'}{'authtypes'}) eq 'HASH') {
                   3237:             my $authhash = $domconfig{'usercreation'}{'authtypes'};
                   3238:             my $context;
                   3239:             if ($env{'request.role'} =~ /^au/) {
                   3240:                 $context = 'author';
1.1075.2.117  raeburn  3241:             } elsif ($env{'request.role'} =~ /^(dc|dh)/) {
1.586     raeburn  3242:                 $context = 'domain';
                   3243:             } elsif ($env{'request.course.id'}) {
                   3244:                 $context = 'course';
                   3245:             }
                   3246:             if ($context) {
                   3247:                 if (ref($authhash->{$context}) eq 'HASH') {
                   3248:                    %can_assign = %{$authhash->{$context}}; 
                   3249:                 }
                   3250:             }
                   3251:         }
                   3252:     }
                   3253:     my $authnum = 0;
                   3254:     foreach my $key (keys(%can_assign)) {
                   3255:         if ($can_assign{$key}) {
                   3256:             $authnum ++;
                   3257:         }
                   3258:     }
                   3259:     if ($can_assign{'krb4'} && $can_assign{'krb5'}) {
                   3260:         $authnum --;
                   3261:     }
                   3262:     return ($authnum,%can_assign);
                   3263: }
                   3264: 
1.1075.2.137  raeburn  3265: sub check_passwd_rules {
                   3266:     my ($domain,$plainpass) = @_;
                   3267:     my %passwdconf = &Apache::lonnet::get_passwdconf($domain);
                   3268:     my ($min,$max,@chars,@brokerule,$warning);
1.1075.2.138  raeburn  3269:     $min = $Apache::lonnet::passwdmin;
1.1075.2.137  raeburn  3270:     if (ref($passwdconf{'chars'}) eq 'ARRAY') {
                   3271:         if ($passwdconf{'min'} =~ /^\d+$/) {
1.1075.2.138  raeburn  3272:             if ($passwdconf{'min'} > $min) {
                   3273:                 $min = $passwdconf{'min'};
                   3274:             }
1.1075.2.137  raeburn  3275:         }
                   3276:         if ($passwdconf{'max'} =~ /^\d+$/) {
                   3277:             $max = $passwdconf{'max'};
                   3278:         }
                   3279:         @chars = @{$passwdconf{'chars'}};
                   3280:     }
                   3281:     if (($min) && (length($plainpass) < $min)) {
                   3282:         push(@brokerule,'min');
                   3283:     }
                   3284:     if (($max) && (length($plainpass) > $max)) {
                   3285:         push(@brokerule,'max');
                   3286:     }
                   3287:     if (@chars) {
                   3288:         my %rules;
                   3289:         map { $rules{$_} = 1; } @chars;
                   3290:         if ($rules{'uc'}) {
                   3291:             unless ($plainpass =~ /[A-Z]/) {
                   3292:                 push(@brokerule,'uc');
                   3293:             }
                   3294:         }
                   3295:         if ($rules{'lc'}) {
                   3296:             unless ($plainpass =~ /[a-z]/) {
                   3297:                 push(@brokerule,'lc');
                   3298:             }
                   3299:         }
                   3300:         if ($rules{'num'}) {
                   3301:             unless ($plainpass =~ /\d/) {
                   3302:                 push(@brokerule,'num');
                   3303:             }
                   3304:         }
                   3305:         if ($rules{'spec'}) {
                   3306:             unless ($plainpass =~ /[!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~]/) {
                   3307:                 push(@brokerule,'spec');
                   3308:             }
                   3309:         }
                   3310:     }
                   3311:     if (@brokerule) {
                   3312:         my %rulenames = &Apache::lonlocal::texthash(
                   3313:             uc   => 'At least one upper case letter',
                   3314:             lc   => 'At least one lower case letter',
                   3315:             num  => 'At least one number',
                   3316:             spec => 'At least one non-alphanumeric',
                   3317:         );
                   3318:         $rulenames{'uc'} .= ': ABCDEFGHIJKLMNOPQRSTUVWXYZ';
                   3319:         $rulenames{'lc'} .= ': abcdefghijklmnopqrstuvwxyz';
                   3320:         $rulenames{'num'} .= ': 0123456789';
                   3321:         $rulenames{'spec'} .= ': !&quot;\#$%&amp;\'()*+,-./:;&lt;=&gt;?@[\]^_\`{|}~';
                   3322:         $rulenames{'min'} = &mt('Minimum password length: [_1]',$min);
                   3323:         $rulenames{'max'} = &mt('Maximum password length: [_1]',$max);
                   3324:         $warning = &mt('Password did not satisfy the following:').'<ul>';
1.1075.2.143  raeburn  3325:         foreach my $rule ('min','max','uc','lc','num','spec') {
1.1075.2.137  raeburn  3326:             if (grep(/^$rule$/,@brokerule)) {
                   3327:                 $warning .= '<li>'.$rulenames{$rule}.'</li>';
                   3328:             }
                   3329:         }
                   3330:         $warning .= '</ul>';
                   3331:     }
                   3332:     if (wantarray) {
                   3333:         return @brokerule;
                   3334:     }
                   3335:     return $warning;
                   3336: }
                   3337: 
1.1075.2.161.  .5(raebu 3338:22): sub passwd_validation_js {
                   3339:22):     my ($currpasswdval,$domain,$context,$id) = @_;
                   3340:22):     my (%passwdconf,$alertmsg);
                   3341:22):     if ($context eq 'linkprot') {
                   3342:22):         my %domconfig = &Apache::lonnet::get_dom('configuration',['ltisec'],$domain);
                   3343:22):         if (ref($domconfig{'ltisec'}) eq 'HASH') {
                   3344:22):             if (ref($domconfig{'ltisec'}{'rules'}) eq 'HASH') {
                   3345:22):                 %passwdconf = %{$domconfig{'ltisec'}{'rules'}};
                   3346:22):             }
                   3347:22):         }
                   3348:22):         if ($id eq 'add') {
                   3349:22):             $alertmsg = &mt('Secret for added launcher did not satisfy requirement(s):').'\n\n';
                   3350:22):         } elsif ($id =~ /^\d+$/) {
                   3351:22):             my $pos = $id+1;
                   3352:22):             $alertmsg = &mt('Secret for launcher [_1] did not satisfy requirement(s):','#'.$pos).'\n\n';
                   3353:22):         } else {
                   3354:22):             $alertmsg = &mt('A secret did not satisfy requirement(s):').'\n\n';
                   3355:22):         }
                   3356:22):     } else {
                   3357:22):         %passwdconf = &Apache::lonnet::get_passwdconf($domain);
                   3358:22):         $alertmsg = &mt('Initial password did not satisfy requirement(s):').'\n\n';
                   3359:22):     }
                   3360:22):     my ($min,$max,@chars,$numrules,$intargjs,%alert);
                   3361:22):     $numrules = 0;
                   3362:22):     $min = $Apache::lonnet::passwdmin;
                   3363:22):     if (ref($passwdconf{'chars'}) eq 'ARRAY') {
                   3364:22):         if ($passwdconf{'min'} =~ /^\d+$/) {
                   3365:22):             if ($passwdconf{'min'} > $min) {
                   3366:22):                 $min = $passwdconf{'min'};
                   3367:22):             }
                   3368:22):         }
                   3369:22):         if ($passwdconf{'max'} =~ /^\d+$/) {
                   3370:22):             $max = $passwdconf{'max'};
                   3371:22):             $numrules ++;
                   3372:22):         }
                   3373:22):         @chars = @{$passwdconf{'chars'}};
                   3374:22):         if (@chars) {
                   3375:22):             $numrules ++;
                   3376:22):         }
                   3377:22):     }
                   3378:22):     if ($min > 0) {
                   3379:22):         $numrules ++;
                   3380:22):     }
                   3381:22):     if (($min > 0) || ($max ne '') || (@chars > 0)) {
                   3382:22):         if ($min) {
                   3383:22):             $alert{'min'} = &mt('minimum [quant,_1,character]',$min).'\n';
                   3384:22):         }
                   3385:22):         if ($max) {
                   3386:22):             $alert{'max'} = &mt('maximum [quant,_1,character]',$max).'\n';
                   3387:22):         }
                   3388:22):         my (@charalerts,@charrules);
                   3389:22):         if (@chars) {
                   3390:22):             if (grep(/^uc$/,@chars)) {
                   3391:22):                 push(@charalerts,&mt('contain at least one upper case letter'));
                   3392:22):                 push(@charrules,'uc');
                   3393:22):             }
                   3394:22):             if (grep(/^lc$/,@chars)) {
                   3395:22):                 push(@charalerts,&mt('contain at least one lower case letter'));
                   3396:22):                 push(@charrules,'lc');
                   3397:22):             }
                   3398:22):             if (grep(/^num$/,@chars)) {
                   3399:22):                 push(@charalerts,&mt('contain at least one number'));
                   3400:22):                 push(@charrules,'num');
                   3401:22):             }
                   3402:22):             if (grep(/^spec$/,@chars)) {
                   3403:22):                 push(@charalerts,&mt('contain at least one non-alphanumeric'));
                   3404:22):                 push(@charrules,'spec');
                   3405:22):             }
                   3406:22):         }
                   3407:22):         $intargjs = qq|            var rulesmsg = '';\n|.
                   3408:22):                     qq|            var currpwval = $currpasswdval;\n|;
                   3409:22):             if ($min) {
                   3410:22):                 $intargjs .= qq|
                   3411:22):             if (currpwval.length < $min) {
                   3412:22):                 rulesmsg += ' - $alert{min}';
                   3413:22):             }
                   3414:22): |;
                   3415:22):             }
                   3416:22):             if ($max) {
                   3417:22):                 $intargjs .= qq|
                   3418:22):             if (currpwval.length > $max) {
                   3419:22):                 rulesmsg += ' - $alert{max}';
                   3420:22):             }
                   3421:22): |;
                   3422:22):             }
                   3423:22):             if (@chars > 0) {
                   3424:22):                 my $charrulestr = '"'.join('","',@charrules).'"';
                   3425:22):                 my $charalertstr = '"'.join('","',@charalerts).'"';
                   3426:22):                 $intargjs .= qq|            var brokerules = new Array();\n|.
                   3427:22):                              qq|            var charrules = new Array($charrulestr);\n|.
                   3428:22):                              qq|            var charalerts = new Array($charalertstr);\n|;
                   3429:22):                 my %rules;
                   3430:22):                 map { $rules{$_} = 1; } @chars;
                   3431:22):                 if ($rules{'uc'}) {
                   3432:22):                     $intargjs .= qq|
                   3433:22):             var ucRegExp = /[A-Z]/;
                   3434:22):             if (!ucRegExp.test(currpwval)) {
                   3435:22):                 brokerules.push('uc');
                   3436:22):             }
                   3437:22): |;
                   3438:22):                 }
                   3439:22):                 if ($rules{'lc'}) {
                   3440:22):                     $intargjs .= qq|
                   3441:22):             var lcRegExp = /[a-z]/;
                   3442:22):             if (!lcRegExp.test(currpwval)) {
                   3443:22):                 brokerules.push('lc');
                   3444:22):             }
                   3445:22): |;
                   3446:22):                 }
                   3447:22):                 if ($rules{'num'}) {
                   3448:22):                      $intargjs .= qq|
                   3449:22):             var numRegExp = /[0-9]/;
                   3450:22):             if (!numRegExp.test(currpwval)) {
                   3451:22):                 brokerules.push('num');
                   3452:22):             }
                   3453:22): |;
                   3454:22):                 }
                   3455:22):                 if ($rules{'spec'}) {
                   3456:22):                      $intargjs .= q|
                   3457:22):             var specRegExp = /[!"#$%&'()*+,\-.\/:;<=>?@[\\^\]_`{\|}~]/;
                   3458:22):             if (!specRegExp.test(currpwval)) {
                   3459:22):                 brokerules.push('spec');
                   3460:22):             }
                   3461:22): |;
                   3462:22):                 }
                   3463:22):                 $intargjs .= qq|
                   3464:22):             if (brokerules.length > 0) {
                   3465:22):                 for (var i=0; i<brokerules.length; i++) {
                   3466:22):                     for (var j=0; j<charrules.length; j++) {
                   3467:22):                         if (brokerules[i] == charrules[j]) {
                   3468:22):                             rulesmsg += ' - '+charalerts[j]+'\\n';
                   3469:22):                             break;
                   3470:22):                         }
                   3471:22):                     }
                   3472:22):                 }
                   3473:22):             }
                   3474:22): |;
                   3475:22):             }
                   3476:22):             $intargjs .= qq|
                   3477:22):             if (rulesmsg != '') {
                   3478:22):                 rulesmsg = '$alertmsg'+rulesmsg;
                   3479:22):                 alert(rulesmsg);
                   3480:22):                 return false;
                   3481:22):             }
                   3482:22): |;
                   3483:22):     }
                   3484:22):     return ($numrules,$intargjs);
                   3485:22): }
                   3486:22): 
1.80      albertel 3487: ###############################################################
                   3488: ##    Get Kerberos Defaults for Domain                 ##
                   3489: ###############################################################
                   3490: ##
                   3491: ## Returns default kerberos version and an associated argument
                   3492: ## as listed in file domain.tab. If not listed, provides
                   3493: ## appropriate default domain and kerberos version.
                   3494: ##
                   3495: #-------------------------------------------
                   3496: 
                   3497: =pod
                   3498: 
1.648     raeburn  3499: =item * &get_kerberos_defaults()
1.80      albertel 3500: 
                   3501: get_kerberos_defaults($target_domain) returns the default kerberos
1.641     raeburn  3502: version and domain. If not found, it defaults to version 4 and the 
                   3503: domain of the server.
1.80      albertel 3504: 
1.648     raeburn  3505: =over 4
                   3506: 
1.80      albertel 3507: ($def_version, $def_krb_domain) = &get_kerberos_defaults($target_domain);
                   3508: 
1.648     raeburn  3509: =back
                   3510: 
                   3511: =back
                   3512: 
1.80      albertel 3513: =cut
                   3514: 
                   3515: #-------------------------------------------
                   3516: sub get_kerberos_defaults {
                   3517:     my $domain=shift;
1.641     raeburn  3518:     my ($krbdef,$krbdefdom);
                   3519:     my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
                   3520:     if (($domdefaults{'auth_def'} =~/^krb(4|5)$/) && ($domdefaults{'auth_arg_def'} ne '')) {
                   3521:         $krbdef = $domdefaults{'auth_def'};
                   3522:         $krbdefdom = $domdefaults{'auth_arg_def'};
                   3523:     } else {
1.80      albertel 3524:         $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/;
                   3525:         my $krbdefdom=$1;
                   3526:         $krbdefdom=~tr/a-z/A-Z/;
                   3527:         $krbdef = "krb4";
                   3528:     }
                   3529:     return ($krbdef,$krbdefdom);
                   3530: }
1.112     bowersj2 3531: 
1.32      matthew  3532: 
1.46      matthew  3533: ###############################################################
                   3534: ##                Thesaurus Functions                        ##
                   3535: ###############################################################
1.20      www      3536: 
1.46      matthew  3537: =pod
1.20      www      3538: 
1.112     bowersj2 3539: =head1 Thesaurus Functions
                   3540: 
                   3541: =over 4
                   3542: 
1.648     raeburn  3543: =item * &initialize_keywords()
1.46      matthew  3544: 
                   3545: Initializes the package variable %Keywords if it is empty.  Uses the
                   3546: package variable $thesaurus_db_file.
                   3547: 
                   3548: =cut
                   3549: 
                   3550: ###################################################
                   3551: 
                   3552: sub initialize_keywords {
                   3553:     return 1 if (scalar keys(%Keywords));
                   3554:     # If we are here, %Keywords is empty, so fill it up
                   3555:     #   Make sure the file we need exists...
                   3556:     if (! -e $thesaurus_db_file) {
                   3557:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file".
                   3558:                                  " failed because it does not exist");
                   3559:         return 0;
                   3560:     }
                   3561:     #   Set up the hash as a database
                   3562:     my %thesaurus_db;
                   3563:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 3564:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  3565:         &Apache::lonnet::logthis("Could not tie \%thesaurus_db to ".
                   3566:                                  $thesaurus_db_file);
                   3567:         return 0;
                   3568:     } 
                   3569:     #  Get the average number of appearances of a word.
                   3570:     my $avecount = $thesaurus_db{'average.count'};
                   3571:     #  Put keywords (those that appear > average) into %Keywords
                   3572:     while (my ($word,$data)=each (%thesaurus_db)) {
                   3573:         my ($count,undef) = split /:/,$data;
                   3574:         $Keywords{$word}++ if ($count > $avecount);
                   3575:     }
                   3576:     untie %thesaurus_db;
                   3577:     # Remove special values from %Keywords.
1.356     albertel 3578:     foreach my $value ('total.count','average.count') {
                   3579:         delete($Keywords{$value}) if (exists($Keywords{$value}));
1.586     raeburn  3580:   }
1.46      matthew  3581:     return 1;
                   3582: }
                   3583: 
                   3584: ###################################################
                   3585: 
                   3586: =pod
                   3587: 
1.648     raeburn  3588: =item * &keyword($word)
1.46      matthew  3589: 
                   3590: Returns true if $word is a keyword.  A keyword is a word that appears more 
                   3591: than the average number of times in the thesaurus database.  Calls 
                   3592: &initialize_keywords
                   3593: 
                   3594: =cut
                   3595: 
                   3596: ###################################################
1.20      www      3597: 
                   3598: sub keyword {
1.46      matthew  3599:     return if (!&initialize_keywords());
                   3600:     my $word=lc(shift());
                   3601:     $word=~s/\W//g;
                   3602:     return exists($Keywords{$word});
1.20      www      3603: }
1.46      matthew  3604: 
                   3605: ###############################################################
                   3606: 
                   3607: =pod 
1.20      www      3608: 
1.648     raeburn  3609: =item * &get_related_words()
1.46      matthew  3610: 
1.160     matthew  3611: Look up a word in the thesaurus.  Takes a scalar argument and returns
1.46      matthew  3612: an array of words.  If the keyword is not in the thesaurus, an empty array
                   3613: will be returned.  The order of the words returned is determined by the
                   3614: database which holds them.
                   3615: 
                   3616: Uses global $thesaurus_db_file.
                   3617: 
1.1057    foxr     3618: 
1.46      matthew  3619: =cut
                   3620: 
                   3621: ###############################################################
                   3622: sub get_related_words {
                   3623:     my $keyword = shift;
                   3624:     my %thesaurus_db;
                   3625:     if (! -e $thesaurus_db_file) {
                   3626:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file ".
                   3627:                                  "failed because the file does not exist");
                   3628:         return ();
                   3629:     }
                   3630:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 3631:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  3632:         return ();
                   3633:     } 
                   3634:     my @Words=();
1.429     www      3635:     my $count=0;
1.46      matthew  3636:     if (exists($thesaurus_db{$keyword})) {
1.356     albertel 3637: 	# The first element is the number of times
                   3638: 	# the word appears.  We do not need it now.
1.429     www      3639: 	my (undef,@RelatedWords) = (split(/:/,$thesaurus_db{$keyword}));
                   3640: 	my (undef,$mostfrequentcount)=split(/\,/,$RelatedWords[0]);
                   3641: 	my $threshold=$mostfrequentcount/10;
                   3642:         foreach my $possibleword (@RelatedWords) {
                   3643:             my ($word,$wordcount)=split(/\,/,$possibleword);
                   3644:             if ($wordcount>$threshold) {
                   3645: 		push(@Words,$word);
                   3646:                 $count++;
                   3647:                 if ($count>10) { last; }
                   3648: 	    }
1.20      www      3649:         }
                   3650:     }
1.46      matthew  3651:     untie %thesaurus_db;
                   3652:     return @Words;
1.14      harris41 3653: }
1.46      matthew  3654: 
1.112     bowersj2 3655: =pod
                   3656: 
                   3657: =back
                   3658: 
                   3659: =cut
1.61      www      3660: 
                   3661: # -------------------------------------------------------------- Plaintext name
1.81      albertel 3662: =pod
                   3663: 
1.112     bowersj2 3664: =head1 User Name Functions
                   3665: 
                   3666: =over 4
                   3667: 
1.648     raeburn  3668: =item * &plainname($uname,$udom,$first)
1.81      albertel 3669: 
1.112     bowersj2 3670: Takes a users logon name and returns it as a string in
1.226     albertel 3671: "first middle last generation" form 
                   3672: if $first is set to 'lastname' then it returns it as
                   3673: 'lastname generation, firstname middlename' if their is a lastname
1.81      albertel 3674: 
                   3675: =cut
1.61      www      3676: 
1.295     www      3677: 
1.81      albertel 3678: ###############################################################
1.61      www      3679: sub plainname {
1.226     albertel 3680:     my ($uname,$udom,$first)=@_;
1.537     albertel 3681:     return if (!defined($uname) || !defined($udom));
1.295     www      3682:     my %names=&getnames($uname,$udom);
1.226     albertel 3683:     my $name=&Apache::lonnet::format_name($names{'firstname'},
                   3684: 					  $names{'middlename'},
                   3685: 					  $names{'lastname'},
                   3686: 					  $names{'generation'},$first);
                   3687:     $name=~s/^\s+//;
1.62      www      3688:     $name=~s/\s+$//;
                   3689:     $name=~s/\s+/ /g;
1.353     albertel 3690:     if ($name !~ /\S/) { $name=$uname.':'.$udom; }
1.62      www      3691:     return $name;
1.61      www      3692: }
1.66      www      3693: 
                   3694: # -------------------------------------------------------------------- Nickname
1.81      albertel 3695: =pod
                   3696: 
1.648     raeburn  3697: =item * &nickname($uname,$udom)
1.81      albertel 3698: 
                   3699: Gets a users name and returns it as a string as
                   3700: 
                   3701: "&quot;nickname&quot;"
1.66      www      3702: 
1.81      albertel 3703: if the user has a nickname or
                   3704: 
                   3705: "first middle last generation"
                   3706: 
                   3707: if the user does not
                   3708: 
                   3709: =cut
1.66      www      3710: 
                   3711: sub nickname {
                   3712:     my ($uname,$udom)=@_;
1.537     albertel 3713:     return if (!defined($uname) || !defined($udom));
1.295     www      3714:     my %names=&getnames($uname,$udom);
1.68      albertel 3715:     my $name=$names{'nickname'};
1.66      www      3716:     if ($name) {
                   3717:        $name='&quot;'.$name.'&quot;'; 
                   3718:     } else {
                   3719:        $name=$names{'firstname'}.' '.$names{'middlename'}.' '.
                   3720: 	     $names{'lastname'}.' '.$names{'generation'};
                   3721:        $name=~s/\s+$//;
                   3722:        $name=~s/\s+/ /g;
                   3723:     }
                   3724:     return $name;
                   3725: }
                   3726: 
1.295     www      3727: sub getnames {
                   3728:     my ($uname,$udom)=@_;
1.537     albertel 3729:     return if (!defined($uname) || !defined($udom));
1.433     albertel 3730:     if ($udom eq 'public' && $uname eq 'public') {
                   3731: 	return ('lastname' => &mt('Public'));
                   3732:     }
1.295     www      3733:     my $id=$uname.':'.$udom;
                   3734:     my ($names,$cached)=&Apache::lonnet::is_cached_new('namescache',$id);
                   3735:     if ($cached) {
                   3736: 	return %{$names};
                   3737:     } else {
                   3738: 	my %loadnames=&Apache::lonnet::get('environment',
                   3739:                     ['firstname','middlename','lastname','generation','nickname'],
                   3740: 					 $udom,$uname);
                   3741: 	&Apache::lonnet::do_cache_new('namescache',$id,\%loadnames);
                   3742: 	return %loadnames;
                   3743:     }
                   3744: }
1.61      www      3745: 
1.542     raeburn  3746: # -------------------------------------------------------------------- getemails
1.648     raeburn  3747: 
1.542     raeburn  3748: =pod
                   3749: 
1.648     raeburn  3750: =item * &getemails($uname,$udom)
1.542     raeburn  3751: 
                   3752: Gets a user's email information and returns it as a hash with keys:
                   3753: notification, critnotification, permanentemail
                   3754: 
                   3755: For notification and critnotification, values are comma-separated lists 
1.648     raeburn  3756: of e-mail addresses; for permanentemail, value is a single e-mail address.
1.542     raeburn  3757:  
1.648     raeburn  3758: 
1.542     raeburn  3759: =cut
                   3760: 
1.648     raeburn  3761: 
1.466     albertel 3762: sub getemails {
                   3763:     my ($uname,$udom)=@_;
                   3764:     if ($udom eq 'public' && $uname eq 'public') {
                   3765: 	return;
                   3766:     }
1.467     www      3767:     if (!$udom) { $udom=$env{'user.domain'}; }
                   3768:     if (!$uname) { $uname=$env{'user.name'}; }
1.466     albertel 3769:     my $id=$uname.':'.$udom;
                   3770:     my ($names,$cached)=&Apache::lonnet::is_cached_new('emailscache',$id);
                   3771:     if ($cached) {
                   3772: 	return %{$names};
                   3773:     } else {
                   3774: 	my %loadnames=&Apache::lonnet::get('environment',
                   3775:                     			   ['notification','critnotification',
                   3776: 					    'permanentemail'],
                   3777: 					   $udom,$uname);
                   3778: 	&Apache::lonnet::do_cache_new('emailscache',$id,\%loadnames);
                   3779: 	return %loadnames;
                   3780:     }
                   3781: }
                   3782: 
1.551     albertel 3783: sub flush_email_cache {
                   3784:     my ($uname,$udom)=@_;
                   3785:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   3786:     if (!$uname) { $uname=$env{'user.name'};   }
                   3787:     return if ($udom eq 'public' && $uname eq 'public');
                   3788:     my $id=$uname.':'.$udom;
                   3789:     &Apache::lonnet::devalidate_cache_new('emailscache',$id);
                   3790: }
                   3791: 
1.728     raeburn  3792: # -------------------------------------------------------------------- getlangs
                   3793: 
                   3794: =pod
                   3795: 
                   3796: =item * &getlangs($uname,$udom)
                   3797: 
                   3798: Gets a user's language preference and returns it as a hash with key:
                   3799: language.
                   3800: 
                   3801: =cut
                   3802: 
                   3803: 
                   3804: sub getlangs {
                   3805:     my ($uname,$udom) = @_;
                   3806:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   3807:     if (!$uname) { $uname=$env{'user.name'};   }
                   3808:     my $id=$uname.':'.$udom;
                   3809:     my ($langs,$cached)=&Apache::lonnet::is_cached_new('userlangs',$id);
                   3810:     if ($cached) {
                   3811:         return %{$langs};
                   3812:     } else {
                   3813:         my %loadlangs=&Apache::lonnet::get('environment',['languages'],
                   3814:                                            $udom,$uname);
                   3815:         &Apache::lonnet::do_cache_new('userlangs',$id,\%loadlangs);
                   3816:         return %loadlangs;
                   3817:     }
                   3818: }
                   3819: 
                   3820: sub flush_langs_cache {
                   3821:     my ($uname,$udom)=@_;
                   3822:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   3823:     if (!$uname) { $uname=$env{'user.name'};   }
                   3824:     return if ($udom eq 'public' && $uname eq 'public');
                   3825:     my $id=$uname.':'.$udom;
                   3826:     &Apache::lonnet::devalidate_cache_new('userlangs',$id);
                   3827: }
                   3828: 
1.61      www      3829: # ------------------------------------------------------------------ Screenname
1.81      albertel 3830: 
                   3831: =pod
                   3832: 
1.648     raeburn  3833: =item * &screenname($uname,$udom)
1.81      albertel 3834: 
                   3835: Gets a users screenname and returns it as a string
                   3836: 
                   3837: =cut
1.61      www      3838: 
                   3839: sub screenname {
                   3840:     my ($uname,$udom)=@_;
1.258     albertel 3841:     if ($uname eq $env{'user.name'} &&
                   3842: 	$udom eq $env{'user.domain'}) {return $env{'environment.screenname'};}
1.212     albertel 3843:     my %names=&Apache::lonnet::get('environment',['screenname'],$udom,$uname);
1.68      albertel 3844:     return $names{'screenname'};
1.62      www      3845: }
                   3846: 
1.212     albertel 3847: 
1.802     bisitz   3848: # ------------------------------------------------------------- Confirm Wrapper
                   3849: =pod
                   3850: 
1.1075.2.42  raeburn  3851: =item * &confirmwrapper($message)
1.802     bisitz   3852: 
                   3853: Wrap messages about completion of operation in box
                   3854: 
                   3855: =cut
                   3856: 
                   3857: sub confirmwrapper {
                   3858:     my ($message)=@_;
                   3859:     if ($message) {
                   3860:         return "\n".'<div class="LC_confirm_box">'."\n"
                   3861:                .$message."\n"
                   3862:                .'</div>'."\n";
                   3863:     } else {
                   3864:         return $message;
                   3865:     }
                   3866: }
                   3867: 
1.62      www      3868: # ------------------------------------------------------------- Message Wrapper
                   3869: 
                   3870: sub messagewrapper {
1.369     www      3871:     my ($link,$username,$domain,$subject,$text)=@_;
1.62      www      3872:     return 
1.441     albertel 3873:         '<a href="/adm/email?compose=individual&amp;'.
                   3874:         'recname='.$username.'&amp;recdom='.$domain.
                   3875: 	'&amp;subject='.&escape($subject).'&amp;text='.&escape($text).'" '.
1.200     matthew  3876:         'title="'.&mt('Send message').'">'.$link.'</a>';
1.74      www      3877: }
1.802     bisitz   3878: 
1.74      www      3879: # --------------------------------------------------------------- Notes Wrapper
                   3880: 
                   3881: sub noteswrapper {
                   3882:     my ($link,$un,$do)=@_;
                   3883:     return 
1.896     amueller 3884: "<a href='/adm/email?recordftf=retrieve&amp;recname=$un&amp;recdom=$do'>$link</a>";
1.62      www      3885: }
1.802     bisitz   3886: 
1.62      www      3887: # ------------------------------------------------------------- Aboutme Wrapper
                   3888: 
                   3889: sub aboutmewrapper {
1.1070    raeburn  3890:     my ($link,$username,$domain,$target,$class)=@_;
1.447     raeburn  3891:     if (!defined($username)  && !defined($domain)) {
                   3892:         return;
                   3893:     }
1.1075.2.15  raeburn  3894:     return '<a href="/adm/'.$domain.'/'.$username.'/aboutme"'.
1.1070    raeburn  3895: 	($target?' target="'.$target.'"':'').($class?' class="'.$class.'"':'').' title="'.&mt("View this user's personal information page").'">'.$link.'</a>';
1.62      www      3896: }
                   3897: 
                   3898: # ------------------------------------------------------------ Syllabus Wrapper
                   3899: 
                   3900: sub syllabuswrapper {
1.707     bisitz   3901:     my ($linktext,$coursedir,$domain)=@_;
1.208     matthew  3902:     return qq{<a href="/public/$domain/$coursedir/syllabus">$linktext</a>};
1.61      www      3903: }
1.14      harris41 3904: 
1.1075.2.161.  .11(raeb 3905:-22): sub aboutme_on {
                   3906:-22):     my ($uname,$udom)=@_;
                   3907:-22):     unless ($uname) { $uname=$env{'user.name'}; }
                   3908:-22):     unless ($udom)  { $udom=$env{'user.domain'}; }
                   3909:-22):     return if ($udom eq 'public' && $uname eq 'public');
                   3910:-22):     my $hashkey=$uname.':'.$udom;
                   3911:-22):     my ($aboutme,$cached)=&Apache::lonnet::is_cached_new('aboutme',$hashkey);
                   3912:-22):     if ($cached) {
                   3913:-22):         return $aboutme;
                   3914:-22):     }
                   3915:-22):     $aboutme = &Apache::lonnet::usertools_access($uname,$udom,'aboutme');
                   3916:-22):     &Apache::lonnet::do_cache_new('aboutme',$hashkey,$aboutme,3600);
                   3917:-22):     return $aboutme;
                   3918:-22): }
                   3919:-22): 
                   3920:-22): sub devalidate_aboutme_cache {
                   3921:-22):     my ($uname,$udom)=@_;
                   3922:-22):     if (!$udom)  { $udom =$env{'user.domain'}; }
                   3923:-22):     if (!$uname) { $uname=$env{'user.name'};   }
                   3924:-22):     return if ($udom eq 'public' && $uname eq 'public');
                   3925:-22):     my $id=$uname.':'.$udom;
                   3926:-22):     &Apache::lonnet::devalidate_cache_new('aboutme',$id);
                   3927:-22): }
                   3928:-22): 
1.802     bisitz   3929: # -----------------------------------------------------------------------------
                   3930: 
1.208     matthew  3931: sub track_student_link {
1.887     raeburn  3932:     my ($linktext,$sname,$sdom,$target,$start,$only_body) = @_;
1.268     albertel 3933:     my $link ="/adm/trackstudent?";
1.208     matthew  3934:     my $title = 'View recent activity';
                   3935:     if (defined($sname) && $sname !~ /^\s*$/ &&
                   3936:         defined($sdom)  && $sdom  !~ /^\s*$/) {
1.268     albertel 3937:         $link .= "selected_student=$sname:$sdom";
1.208     matthew  3938:         $title .= ' of this student';
1.268     albertel 3939:     } 
1.208     matthew  3940:     if (defined($target) && $target !~ /^\s*$/) {
                   3941:         $target = qq{target="$target"};
                   3942:     } else {
                   3943:         $target = '';
                   3944:     }
1.268     albertel 3945:     if ($start) { $link.='&amp;start='.$start; }
1.887     raeburn  3946:     if ($only_body) { $link .= '&amp;only_body=1'; }
1.554     albertel 3947:     $title = &mt($title);
                   3948:     $linktext = &mt($linktext);
1.448     albertel 3949:     return qq{<a href="$link" title="$title" $target>$linktext</a>}.
                   3950: 	&help_open_topic('View_recent_activity');
1.208     matthew  3951: }
                   3952: 
1.781     raeburn  3953: sub slot_reservations_link {
                   3954:     my ($linktext,$sname,$sdom,$target) = @_;
                   3955:     my $link ="/adm/slotrequest?command=showresv&amp;origin=aboutme";
                   3956:     my $title = 'View slot reservation history';
                   3957:     if (defined($sname) && $sname !~ /^\s*$/ &&
                   3958:         defined($sdom)  && $sdom  !~ /^\s*$/) {
                   3959:         $link .= "&amp;uname=$sname&amp;udom=$sdom";
                   3960:         $title .= ' of this student';
                   3961:     }
                   3962:     if (defined($target) && $target !~ /^\s*$/) {
                   3963:         $target = qq{target="$target"};
                   3964:     } else {
                   3965:         $target = '';
                   3966:     }
                   3967:     $title = &mt($title);
                   3968:     $linktext = &mt($linktext);
                   3969:     return qq{<a href="$link" title="$title" $target>$linktext</a>};
                   3970: # FIXME uncomment when help item created: &help_open_topic('Slot_Reservation_History');
                   3971: 
                   3972: }
                   3973: 
1.508     www      3974: # ===================================================== Display a student photo
                   3975: 
                   3976: 
1.509     albertel 3977: sub student_image_tag {
1.508     www      3978:     my ($domain,$user)=@_;
                   3979:     my $imgsrc=&Apache::lonnet::studentphoto($domain,$user,'jpg');
                   3980:     if (($imgsrc) && ($imgsrc ne '/adm/lonKaputt/lonlogo_broken.gif')) {
                   3981: 	return '<img src="'.$imgsrc.'" align="right" />';
                   3982:     } else {
                   3983: 	return '';
                   3984:     }
                   3985: }
                   3986: 
1.112     bowersj2 3987: =pod
                   3988: 
                   3989: =back
                   3990: 
                   3991: =head1 Access .tab File Data
                   3992: 
                   3993: =over 4
                   3994: 
1.648     raeburn  3995: =item * &languageids() 
1.112     bowersj2 3996: 
                   3997: returns list of all language ids
                   3998: 
                   3999: =cut
                   4000: 
1.14      harris41 4001: sub languageids {
1.16      harris41 4002:     return sort(keys(%language));
1.14      harris41 4003: }
                   4004: 
1.112     bowersj2 4005: =pod
                   4006: 
1.648     raeburn  4007: =item * &languagedescription() 
1.112     bowersj2 4008: 
                   4009: returns description of a specified language id
                   4010: 
                   4011: =cut
                   4012: 
1.14      harris41 4013: sub languagedescription {
1.125     www      4014:     my $code=shift;
                   4015:     return  ($supported_language{$code}?'* ':'').
                   4016:             $language{$code}.
1.126     www      4017: 	    ($supported_language{$code}?' ('.&mt('interface available').')':'');
1.145     www      4018: }
                   4019: 
1.1048    foxr     4020: =pod
                   4021: 
                   4022: =item * &plainlanguagedescription
                   4023: 
                   4024: Returns both the plain language description (e.g. 'Creoles and Pidgins, English-based (Other)')
                   4025: and the language character encoding (e.g. ISO) separated by a ' - ' string.
                   4026: 
                   4027: =cut
                   4028: 
1.145     www      4029: sub plainlanguagedescription {
                   4030:     my $code=shift;
                   4031:     return $language{$code};
                   4032: }
                   4033: 
1.1048    foxr     4034: =pod
                   4035: 
                   4036: =item * &supportedlanguagecode
                   4037: 
                   4038: Returns the supported language code (e.g. sptutf maps to pt) given a language
                   4039: code.
                   4040: 
                   4041: =cut
                   4042: 
1.145     www      4043: sub supportedlanguagecode {
                   4044:     my $code=shift;
                   4045:     return $supported_language{$code};
1.97      www      4046: }
                   4047: 
1.112     bowersj2 4048: =pod
                   4049: 
1.1048    foxr     4050: =item * &latexlanguage()
                   4051: 
                   4052: Given a language key code returns the correspondnig language to use
                   4053: to select the correct hyphenation on LaTeX printouts.  This is undef if there
                   4054: is no supported hyphenation for the language code.
                   4055: 
                   4056: =cut
                   4057: 
                   4058: sub latexlanguage {
                   4059:     my $code = shift;
                   4060:     return $latex_language{$code};
                   4061: }
                   4062: 
                   4063: =pod
                   4064: 
                   4065: =item * &latexhyphenation()
                   4066: 
                   4067: Same as above but what's supplied is the language as it might be stored
                   4068: in the metadata.
                   4069: 
                   4070: =cut
                   4071: 
                   4072: sub latexhyphenation {
                   4073:     my $key = shift;
                   4074:     return $latex_language_bykey{$key};
                   4075: }
                   4076: 
                   4077: =pod
                   4078: 
1.648     raeburn  4079: =item * &copyrightids() 
1.112     bowersj2 4080: 
                   4081: returns list of all copyrights
                   4082: 
                   4083: =cut
                   4084: 
                   4085: sub copyrightids {
                   4086:     return sort(keys(%cprtag));
                   4087: }
                   4088: 
                   4089: =pod
                   4090: 
1.648     raeburn  4091: =item * &copyrightdescription() 
1.112     bowersj2 4092: 
                   4093: returns description of a specified copyright id
                   4094: 
                   4095: =cut
                   4096: 
                   4097: sub copyrightdescription {
1.166     www      4098:     return &mt($cprtag{shift(@_)});
1.112     bowersj2 4099: }
1.197     matthew  4100: 
                   4101: =pod
                   4102: 
1.648     raeburn  4103: =item * &source_copyrightids() 
1.192     taceyjo1 4104: 
                   4105: returns list of all source copyrights
                   4106: 
                   4107: =cut
                   4108: 
                   4109: sub source_copyrightids {
                   4110:     return sort(keys(%scprtag));
                   4111: }
                   4112: 
                   4113: =pod
                   4114: 
1.648     raeburn  4115: =item * &source_copyrightdescription() 
1.192     taceyjo1 4116: 
                   4117: returns description of a specified source copyright id
                   4118: 
                   4119: =cut
                   4120: 
                   4121: sub source_copyrightdescription {
                   4122:     return &mt($scprtag{shift(@_)});
                   4123: }
1.112     bowersj2 4124: 
                   4125: =pod
                   4126: 
1.648     raeburn  4127: =item * &filecategories() 
1.112     bowersj2 4128: 
                   4129: returns list of all file categories
                   4130: 
                   4131: =cut
                   4132: 
                   4133: sub filecategories {
                   4134:     return sort(keys(%category_extensions));
                   4135: }
                   4136: 
                   4137: =pod
                   4138: 
1.648     raeburn  4139: =item * &filecategorytypes() 
1.112     bowersj2 4140: 
                   4141: returns list of file types belonging to a given file
                   4142: category
                   4143: 
                   4144: =cut
                   4145: 
                   4146: sub filecategorytypes {
1.356     albertel 4147:     my ($cat) = @_;
                   4148:     return @{$category_extensions{lc($cat)}};
1.112     bowersj2 4149: }
                   4150: 
                   4151: =pod
                   4152: 
1.648     raeburn  4153: =item * &fileembstyle() 
1.112     bowersj2 4154: 
                   4155: returns embedding style for a specified file type
                   4156: 
                   4157: =cut
                   4158: 
                   4159: sub fileembstyle {
                   4160:     return $fe{lc(shift(@_))};
1.169     www      4161: }
                   4162: 
1.351     www      4163: sub filemimetype {
                   4164:     return $fm{lc(shift(@_))};
                   4165: }
                   4166: 
1.169     www      4167: 
                   4168: sub filecategoryselect {
                   4169:     my ($name,$value)=@_;
1.189     matthew  4170:     return &select_form($value,$name,
1.970     raeburn  4171:                         {'' => &mt('Any category'), map { $_,$_ } sort(keys(%category_extensions))});
1.112     bowersj2 4172: }
                   4173: 
                   4174: =pod
                   4175: 
1.648     raeburn  4176: =item * &filedescription() 
1.112     bowersj2 4177: 
                   4178: returns description for a specified file type
                   4179: 
                   4180: =cut
                   4181: 
                   4182: sub filedescription {
1.188     matthew  4183:     my $file_description = $fd{lc(shift())};
                   4184:     $file_description =~ s:([\[\]]):~$1:g;
                   4185:     return &mt($file_description);
1.112     bowersj2 4186: }
                   4187: 
                   4188: =pod
                   4189: 
1.648     raeburn  4190: =item * &filedescriptionex() 
1.112     bowersj2 4191: 
                   4192: returns description for a specified file type with
                   4193: extra formatting
                   4194: 
                   4195: =cut
                   4196: 
                   4197: sub filedescriptionex {
                   4198:     my $ex=shift;
1.188     matthew  4199:     my $file_description = $fd{lc($ex)};
                   4200:     $file_description =~ s:([\[\]]):~$1:g;
                   4201:     return '.'.$ex.' '.&mt($file_description);
1.112     bowersj2 4202: }
                   4203: 
                   4204: # End of .tab access
                   4205: =pod
                   4206: 
                   4207: =back
                   4208: 
                   4209: =cut
                   4210: 
                   4211: # ------------------------------------------------------------------ File Types
                   4212: sub fileextensions {
                   4213:     return sort(keys(%fe));
                   4214: }
                   4215: 
1.97      www      4216: # ----------------------------------------------------------- Display Languages
                   4217: # returns a hash with all desired display languages
                   4218: #
                   4219: 
                   4220: sub display_languages {
                   4221:     my %languages=();
1.695     raeburn  4222:     foreach my $lang (&Apache::lonlocal::preferred_languages()) {
1.356     albertel 4223: 	$languages{$lang}=1;
1.97      www      4224:     }
                   4225:     &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']);
1.258     albertel 4226:     if ($env{'form.displaylanguage'}) {
1.356     albertel 4227: 	foreach my $lang (split(/\s*(\,|\;|\:)\s*/,$env{'form.displaylanguage'})) {
                   4228: 	    $languages{$lang}=1;
1.97      www      4229:         }
                   4230:     }
                   4231:     return %languages;
1.14      harris41 4232: }
                   4233: 
1.582     albertel 4234: sub languages {
                   4235:     my ($possible_langs) = @_;
1.695     raeburn  4236:     my @preferred_langs = &Apache::lonlocal::preferred_languages();
1.582     albertel 4237:     if (!ref($possible_langs)) {
                   4238: 	if( wantarray ) {
                   4239: 	    return @preferred_langs;
                   4240: 	} else {
                   4241: 	    return $preferred_langs[0];
                   4242: 	}
                   4243:     }
                   4244:     my %possibilities = map { $_ => 1 } (@$possible_langs);
                   4245:     my @preferred_possibilities;
                   4246:     foreach my $preferred_lang (@preferred_langs) {
                   4247: 	if (exists($possibilities{$preferred_lang})) {
                   4248: 	    push(@preferred_possibilities, $preferred_lang);
                   4249: 	}
                   4250:     }
                   4251:     if( wantarray ) {
                   4252: 	return @preferred_possibilities;
                   4253:     }
                   4254:     return $preferred_possibilities[0];
                   4255: }
                   4256: 
1.742     raeburn  4257: sub user_lang {
                   4258:     my ($touname,$toudom,$fromcid) = @_;
                   4259:     my @userlangs;
                   4260:     if (($fromcid ne '') && ($env{'course.'.$fromcid.'.languages'} ne '')) {
                   4261:         @userlangs=(@userlangs,split(/\s*(\,|\;|\:)\s*/,
                   4262:                     $env{'course.'.$fromcid.'.languages'}));
                   4263:     } else {
                   4264:         my %langhash = &getlangs($touname,$toudom);
                   4265:         if ($langhash{'languages'} ne '') {
                   4266:             @userlangs = split(/\s*(\,|\;|\:)\s*/,$langhash{'languages'});
                   4267:         } else {
                   4268:             my %domdefs = &Apache::lonnet::get_domain_defaults($toudom);
                   4269:             if ($domdefs{'lang_def'} ne '') {
                   4270:                 @userlangs = ($domdefs{'lang_def'});
                   4271:             }
                   4272:         }
                   4273:     }
                   4274:     my @languages=&Apache::lonlocal::get_genlanguages(@userlangs);
                   4275:     my $user_lh = Apache::localize->get_handle(@languages);
                   4276:     return $user_lh;
                   4277: }
                   4278: 
                   4279: 
1.112     bowersj2 4280: ###############################################################
                   4281: ##               Student Answer Attempts                     ##
                   4282: ###############################################################
                   4283: 
                   4284: =pod
                   4285: 
                   4286: =head1 Alternate Problem Views
                   4287: 
                   4288: =over 4
                   4289: 
1.648     raeburn  4290: =item * &get_previous_attempt($symb, $username, $domain, $course,
1.1075.2.86  raeburn  4291:     $getattempt, $regexp, $gradesub, $usec, $identifier)
1.112     bowersj2 4292: 
                   4293: Return string with previous attempt on problem. Arguments:
                   4294: 
                   4295: =over 4
                   4296: 
                   4297: =item * $symb: Problem, including path
                   4298: 
                   4299: =item * $username: username of the desired student
                   4300: 
                   4301: =item * $domain: domain of the desired student
1.14      harris41 4302: 
1.112     bowersj2 4303: =item * $course: Course ID
1.14      harris41 4304: 
1.112     bowersj2 4305: =item * $getattempt: Leave blank for all attempts, otherwise put
                   4306:     something
1.14      harris41 4307: 
1.112     bowersj2 4308: =item * $regexp: if string matches this regexp, the string will be
                   4309:     sent to $gradesub
1.14      harris41 4310: 
1.112     bowersj2 4311: =item * $gradesub: routine that processes the string if it matches $regexp
1.14      harris41 4312: 
1.1075.2.86  raeburn  4313: =item * $usec: section of the desired student
                   4314: 
                   4315: =item * $identifier: counter for student (multiple students one problem) or
                   4316:     problem (one student; whole sequence).
                   4317: 
1.112     bowersj2 4318: =back
1.14      harris41 4319: 
1.112     bowersj2 4320: The output string is a table containing all desired attempts, if any.
1.16      harris41 4321: 
1.112     bowersj2 4322: =cut
1.1       albertel 4323: 
                   4324: sub get_previous_attempt {
1.1075.2.86  raeburn  4325:   my ($symb,$username,$domain,$course,$getattempt,$regexp,$gradesub,$usec,$identifier)=@_;
1.1       albertel 4326:   my $prevattempts='';
1.43      ng       4327:   no strict 'refs';
1.1       albertel 4328:   if ($symb) {
1.3       albertel 4329:     my (%returnhash)=
                   4330:       &Apache::lonnet::restore($symb,$course,$domain,$username);
1.1       albertel 4331:     if ($returnhash{'version'}) {
                   4332:       my %lasthash=();
                   4333:       my $version;
                   4334:       for ($version=1;$version<=$returnhash{'version'};$version++) {
1.1075.2.91  raeburn  4335:         foreach my $key (reverse(sort(split(/\:/,$returnhash{$version.':keys'})))) {
                   4336:             if ($key =~ /\.rawrndseed$/) {
                   4337:                 my ($id) = ($key =~ /^(.+)\.rawrndseed$/);
                   4338:                 $lasthash{$id.'.rndseed'} = $returnhash{$version.':'.$key};
                   4339:             } else {
                   4340:                 $lasthash{$key}=$returnhash{$version.':'.$key};
                   4341:             }
1.19      harris41 4342:         }
1.1       albertel 4343:       }
1.596     albertel 4344:       $prevattempts=&start_data_table().&start_data_table_header_row();
                   4345:       $prevattempts.='<th>'.&mt('History').'</th>';
1.1075.2.86  raeburn  4346:       my (%typeparts,%lasthidden,%regraded,%hidestatus);
1.945     raeburn  4347:       my $showsurv=&Apache::lonnet::allowed('vas',$env{'request.course.id'});
1.356     albertel 4348:       foreach my $key (sort(keys(%lasthash))) {
                   4349: 	my ($ign,@parts) = split(/\./,$key);
1.41      ng       4350: 	if ($#parts > 0) {
1.31      albertel 4351: 	  my $data=$parts[-1];
1.989     raeburn  4352:           next if ($data eq 'foilorder');
1.31      albertel 4353: 	  pop(@parts);
1.1010    www      4354:           $prevattempts.='<th>'.&mt('Part ').join('.',@parts).'<br />'.$data.'&nbsp;</th>';
1.945     raeburn  4355:           if ($data eq 'type') {
                   4356:               unless ($showsurv) {
                   4357:                   my $id = join(',',@parts);
                   4358:                   $typeparts{$ign.'.'.$id} = $lasthash{$key};
1.978     raeburn  4359:                   if (($lasthash{$key} eq 'anonsurvey') || ($lasthash{$key} eq 'anonsurveycred')) {
                   4360:                       $lasthidden{$ign.'.'.$id} = 1;
                   4361:                   }
1.945     raeburn  4362:               }
1.1075.2.86  raeburn  4363:               if ($identifier ne '') {
                   4364:                   my $id = join(',',@parts);
                   4365:                   if (&Apache::lonnet::EXT("resource.$id.problemstatus",$symb,
                   4366:                                                $domain,$username,$usec,undef,$course) =~ /^no/) {
                   4367:                       $hidestatus{$ign.'.'.$id} = 1;
                   4368:                   }
                   4369:               }
                   4370:           } elsif ($data eq 'regrader') {
                   4371:               if (($identifier ne '') && (@parts)) {
                   4372:                   my $id = join(',',@parts);
                   4373:                   $regraded{$ign.'.'.$id} = 1;
                   4374:               }
1.1010    www      4375:           } 
1.31      albertel 4376: 	} else {
1.41      ng       4377: 	  if ($#parts == 0) {
                   4378: 	    $prevattempts.='<th>'.$parts[0].'</th>';
                   4379: 	  } else {
                   4380: 	    $prevattempts.='<th>'.$ign.'</th>';
                   4381: 	  }
1.31      albertel 4382: 	}
1.16      harris41 4383:       }
1.596     albertel 4384:       $prevattempts.=&end_data_table_header_row();
1.40      ng       4385:       if ($getattempt eq '') {
1.1075.2.86  raeburn  4386:         my (%solved,%resets,%probstatus);
                   4387:         if (($identifier ne '') && (keys(%regraded) > 0)) {
                   4388:             for ($version=1;$version<=$returnhash{'version'};$version++) {
                   4389:                 foreach my $id (keys(%regraded)) {
                   4390:                     if (($returnhash{$version.':'.$id.'.regrader'}) &&
                   4391:                         ($returnhash{$version.':'.$id.'.tries'} eq '') &&
                   4392:                         ($returnhash{$version.':'.$id.'.award'} eq '')) {
                   4393:                         push(@{$resets{$id}},$version);
                   4394:                     }
                   4395:                 }
                   4396:             }
                   4397:         }
1.40      ng       4398: 	for ($version=1;$version<=$returnhash{'version'};$version++) {
1.1075.2.86  raeburn  4399:             my (@hidden,@unsolved);
1.945     raeburn  4400:             if (%typeparts) {
                   4401:                 foreach my $id (keys(%typeparts)) {
1.1075.2.86  raeburn  4402:                     if (($returnhash{$version.':'.$id.'.type'} eq 'anonsurvey') ||
                   4403:                         ($returnhash{$version.':'.$id.'.type'} eq 'anonsurveycred')) {
1.945     raeburn  4404:                         push(@hidden,$id);
1.1075.2.86  raeburn  4405:                     } elsif ($identifier ne '') {
                   4406:                         unless (($returnhash{$version.':'.$id.'.type'} eq 'survey') ||
                   4407:                                 ($returnhash{$version.':'.$id.'.type'} eq 'surveycred') ||
                   4408:                                 ($hidestatus{$id})) {
                   4409:                             next if ((ref($resets{$id}) eq 'ARRAY') && grep(/^\Q$version\E$/,@{$resets{$id}}));
                   4410:                             if ($returnhash{$version.':'.$id.'.solved'} eq 'correct_by_student') {
                   4411:                                 push(@{$solved{$id}},$version);
                   4412:                             } elsif (($returnhash{$version.':'.$id.'.solved'} ne '') &&
                   4413:                                      (ref($solved{$id}) eq 'ARRAY')) {
                   4414:                                 my $skip;
                   4415:                                 if (ref($resets{$id}) eq 'ARRAY') {
                   4416:                                     foreach my $reset (@{$resets{$id}}) {
                   4417:                                         if ($reset > $solved{$id}[-1]) {
                   4418:                                             $skip=1;
                   4419:                                             last;
                   4420:                                         }
                   4421:                                     }
                   4422:                                 }
                   4423:                                 unless ($skip) {
                   4424:                                     my ($ign,$partslist) = split(/\./,$id,2);
                   4425:                                     push(@unsolved,$partslist);
                   4426:                                 }
                   4427:                             }
                   4428:                         }
1.945     raeburn  4429:                     }
                   4430:                 }
                   4431:             }
                   4432:             $prevattempts.=&start_data_table_row().
1.1075.2.86  raeburn  4433:                            '<td>'.&mt('Transaction [_1]',$version);
                   4434:             if (@unsolved) {
                   4435:                 $prevattempts .= '<span class="LC_nobreak"><label>'.
                   4436:                                  '<input type="checkbox" name="HIDE'.$identifier.'" value="'.$version.':'.join('_',@unsolved).'" />'.
                   4437:                                  &mt('Hide').'</label></span>';
                   4438:             }
                   4439:             $prevattempts .= '</td>';
1.945     raeburn  4440:             if (@hidden) {
                   4441:                 foreach my $key (sort(keys(%lasthash))) {
1.989     raeburn  4442:                     next if ($key =~ /\.foilorder$/);
1.945     raeburn  4443:                     my $hide;
                   4444:                     foreach my $id (@hidden) {
                   4445:                         if ($key =~ /^\Q$id\E/) {
                   4446:                             $hide = 1;
                   4447:                             last;
                   4448:                         }
                   4449:                     }
                   4450:                     if ($hide) {
                   4451:                         my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
                   4452:                         if (($data eq 'award') || ($data eq 'awarddetail')) {
                   4453:                             my $value = &format_previous_attempt_value($key,
                   4454:                                              $returnhash{$version.':'.$key});
                   4455:                             $prevattempts.='<td>'.$value.'&nbsp;</td>';
                   4456:                         } else {
                   4457:                             $prevattempts.='<td>&nbsp;</td>';
                   4458:                         }
                   4459:                     } else {
                   4460:                         if ($key =~ /\./) {
1.1075.2.91  raeburn  4461:                             my $value = $returnhash{$version.':'.$key};
                   4462:                             if ($key =~ /\.rndseed$/) {
                   4463:                                 my ($id) = ($key =~ /^(.+)\.rndseed$/);
                   4464:                                 if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) {
                   4465:                                     $value = $returnhash{$version.':'.$id.'.rawrndseed'};
                   4466:                                 }
                   4467:                             }
                   4468:                             $prevattempts.='<td>'.&format_previous_attempt_value($key,$value).
                   4469:                                            '&nbsp;</td>';
1.945     raeburn  4470:                         } else {
                   4471:                             $prevattempts.='<td>&nbsp;</td>';
                   4472:                         }
                   4473:                     }
                   4474:                 }
                   4475:             } else {
                   4476: 	        foreach my $key (sort(keys(%lasthash))) {
1.989     raeburn  4477:                     next if ($key =~ /\.foilorder$/);
1.1075.2.91  raeburn  4478:                     my $value = $returnhash{$version.':'.$key};
                   4479:                     if ($key =~ /\.rndseed$/) {
                   4480:                         my ($id) = ($key =~ /^(.+)\.rndseed$/);
                   4481:                         if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) {
                   4482:                             $value = $returnhash{$version.':'.$id.'.rawrndseed'};
                   4483:                         }
                   4484:                     }
                   4485:                     $prevattempts.='<td>'.&format_previous_attempt_value($key,$value).
                   4486:                                    '&nbsp;</td>';
1.945     raeburn  4487: 	        }
                   4488:             }
                   4489: 	    $prevattempts.=&end_data_table_row();
1.40      ng       4490: 	 }
1.1       albertel 4491:       }
1.945     raeburn  4492:       my @currhidden = keys(%lasthidden);
1.596     albertel 4493:       $prevattempts.=&start_data_table_row().'<td>'.&mt('Current').'</td>';
1.356     albertel 4494:       foreach my $key (sort(keys(%lasthash))) {
1.989     raeburn  4495:           next if ($key =~ /\.foilorder$/);
1.945     raeburn  4496:           if (%typeparts) {
                   4497:               my $hidden;
                   4498:               foreach my $id (@currhidden) {
                   4499:                   if ($key =~ /^\Q$id\E/) {
                   4500:                       $hidden = 1;
                   4501:                       last;
                   4502:                   }
                   4503:               }
                   4504:               if ($hidden) {
                   4505:                   my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
                   4506:                   if (($data eq 'award') || ($data eq 'awarddetail')) {
                   4507:                       my $value = &format_previous_attempt_value($key,$lasthash{$key});
                   4508:                       if ($key =~/$regexp$/ && (defined &$gradesub)) {
                   4509:                           $value = &$gradesub($value);
                   4510:                       }
                   4511:                       $prevattempts.='<td>'.$value.'&nbsp;</td>';
                   4512:                   } else {
                   4513:                       $prevattempts.='<td>&nbsp;</td>';
                   4514:                   }
                   4515:               } else {
                   4516:                   my $value = &format_previous_attempt_value($key,$lasthash{$key});
                   4517:                   if ($key =~/$regexp$/ && (defined &$gradesub)) {
                   4518:                       $value = &$gradesub($value);
                   4519:                   }
                   4520:                   $prevattempts.='<td>'.$value.'&nbsp;</td>';
                   4521:               }
                   4522:           } else {
                   4523: 	      my $value = &format_previous_attempt_value($key,$lasthash{$key});
                   4524: 	      if ($key =~/$regexp$/ && (defined &$gradesub)) {
                   4525:                   $value = &$gradesub($value);
                   4526:               }
                   4527: 	      $prevattempts.='<td>'.$value.'&nbsp;</td>';
                   4528:           }
1.16      harris41 4529:       }
1.596     albertel 4530:       $prevattempts.= &end_data_table_row().&end_data_table();
1.1       albertel 4531:     } else {
1.1075.2.161.  .17(raeb 4532:-23):       my $msg;
                   4533:-23):       if ($symb =~ /ext\.tool$/) {
                   4534:-23):           $msg = &mt('No grade passed back.');
                   4535:-23):       } else {
                   4536:-23):           $msg = &mt('Nothing submitted - no attempts.');
                   4537:-23):       }
1.596     albertel 4538:       $prevattempts=
                   4539: 	  &start_data_table().&start_data_table_row().
1.1075.2.161.  .17(raeb 4540:-23): 	  '<td>'.$msg.'</td>'.
1.596     albertel 4541: 	  &end_data_table_row().&end_data_table();
1.1       albertel 4542:     }
                   4543:   } else {
1.596     albertel 4544:     $prevattempts=
                   4545: 	  &start_data_table().&start_data_table_row().
                   4546: 	  '<td>'.&mt('No data.').'</td>'.
                   4547: 	  &end_data_table_row().&end_data_table();
1.1       albertel 4548:   }
1.10      albertel 4549: }
                   4550: 
1.581     albertel 4551: sub format_previous_attempt_value {
                   4552:     my ($key,$value) = @_;
1.1011    www      4553:     if (($key =~ /timestamp/) || ($key=~/duedate/)) {
1.581     albertel 4554: 	$value = &Apache::lonlocal::locallocaltime($value);
                   4555:     } elsif (ref($value) eq 'ARRAY') {
                   4556: 	$value = '('.join(', ', @{ $value }).')';
1.988     raeburn  4557:     } elsif ($key =~ /answerstring$/) {
                   4558:         my %answers = &Apache::lonnet::str2hash($value);
                   4559:         my @anskeys = sort(keys(%answers));
                   4560:         if (@anskeys == 1) {
                   4561:             my $answer = $answers{$anskeys[0]};
1.1001    raeburn  4562:             if ($answer =~ m{\0}) {
                   4563:                 $answer =~ s{\0}{,}g;
1.988     raeburn  4564:             }
                   4565:             my $tag_internal_answer_name = 'INTERNAL';
                   4566:             if ($anskeys[0] eq $tag_internal_answer_name) {
                   4567:                 $value = $answer; 
                   4568:             } else {
                   4569:                 $value = $anskeys[0].'='.$answer;
                   4570:             }
                   4571:         } else {
                   4572:             foreach my $ans (@anskeys) {
                   4573:                 my $answer = $answers{$ans};
1.1001    raeburn  4574:                 if ($answer =~ m{\0}) {
                   4575:                     $answer =~ s{\0}{,}g;
1.988     raeburn  4576:                 }
                   4577:                 $value .=  $ans.'='.$answer.'<br />';;
                   4578:             } 
                   4579:         }
1.581     albertel 4580:     } else {
                   4581: 	$value = &unescape($value);
                   4582:     }
                   4583:     return $value;
                   4584: }
                   4585: 
                   4586: 
1.107     albertel 4587: sub relative_to_absolute {
                   4588:     my ($url,$output)=@_;
                   4589:     my $parser=HTML::TokeParser->new(\$output);
                   4590:     my $token;
                   4591:     my $thisdir=$url;
                   4592:     my @rlinks=();
                   4593:     while ($token=$parser->get_token) {
                   4594: 	if ($token->[0] eq 'S') {
                   4595: 	    if ($token->[1] eq 'a') {
                   4596: 		if ($token->[2]->{'href'}) {
                   4597: 		    $rlinks[$#rlinks+1]=$token->[2]->{'href'};
                   4598: 		}
                   4599: 	    } elsif ($token->[1] eq 'img' || $token->[1] eq 'embed' ) {
                   4600: 		$rlinks[$#rlinks+1]=$token->[2]->{'src'};
                   4601: 	    } elsif ($token->[1] eq 'base') {
                   4602: 		$thisdir=$token->[2]->{'href'};
                   4603: 	    }
                   4604: 	}
                   4605:     }
                   4606:     $thisdir=~s-/[^/]*$--;
1.356     albertel 4607:     foreach my $link (@rlinks) {
1.726     raeburn  4608: 	unless (($link=~/^https?\:\/\//i) ||
1.356     albertel 4609: 		($link=~/^\//) ||
                   4610: 		($link=~/^javascript:/i) ||
                   4611: 		($link=~/^mailto:/i) ||
                   4612: 		($link=~/^\#/)) {
                   4613: 	    my $newlocation=&Apache::lonnet::hreflocation($thisdir,$link);
                   4614: 	    $output=~s/(\"|\'|\=\s*)\Q$link\E(\"|\'|\s|\>)/$1$newlocation$2/;
1.107     albertel 4615: 	}
                   4616:     }
                   4617: # -------------------------------------------------- Deal with Applet codebases
                   4618:     $output=~s/(\<applet[^\>]+)(codebase\=[^\S\>]+)*([^\>]*)\>/$1.($2?$2:' codebase="'.$thisdir.'"').$3.'>'/gei;
                   4619:     return $output;
                   4620: }
                   4621: 
1.112     bowersj2 4622: =pod
                   4623: 
1.648     raeburn  4624: =item * &get_student_view()
1.112     bowersj2 4625: 
                   4626: show a snapshot of what student was looking at
                   4627: 
                   4628: =cut
                   4629: 
1.10      albertel 4630: sub get_student_view {
1.186     albertel 4631:   my ($symb,$username,$domain,$courseid,$target,$moreenv) = @_;
1.114     www      4632:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 4633:   my (%form);
1.10      albertel 4634:   my @elements=('symb','courseid','domain','username');
                   4635:   foreach my $element (@elements) {
1.186     albertel 4636:       $form{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 4637:   }
1.186     albertel 4638:   if (defined($moreenv)) {
                   4639:       %form=(%form,%{$moreenv});
                   4640:   }
1.236     albertel 4641:   if (defined($target)) { $form{'grade_target'} = $target; }
1.107     albertel 4642:   $feedurl=&Apache::lonnet::clutter($feedurl);
1.650     www      4643:   my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form);
1.11      albertel 4644:   $userview=~s/\<body[^\>]*\>//gi;
                   4645:   $userview=~s/\<\/body\>//gi;
                   4646:   $userview=~s/\<html\>//gi;
                   4647:   $userview=~s/\<\/html\>//gi;
                   4648:   $userview=~s/\<head\>//gi;
                   4649:   $userview=~s/\<\/head\>//gi;
                   4650:   $userview=~s/action\s*\=/would_be_action\=/gi;
1.107     albertel 4651:   $userview=&relative_to_absolute($feedurl,$userview);
1.650     www      4652:   if (wantarray) {
                   4653:      return ($userview,$response);
                   4654:   } else {
                   4655:      return $userview;
                   4656:   }
                   4657: }
                   4658: 
                   4659: sub get_student_view_with_retries {
                   4660:   my ($symb,$retries,$username,$domain,$courseid,$target,$moreenv) = @_;
                   4661: 
                   4662:     my $ok = 0;                 # True if we got a good response.
                   4663:     my $content;
                   4664:     my $response;
                   4665: 
                   4666:     # Try to get the student_view done. within the retries count:
                   4667:     
                   4668:     do {
                   4669:          ($content, $response) = &get_student_view($symb,$username,$domain,$courseid,$target,$moreenv);
                   4670:          $ok      = $response->is_success;
                   4671:          if (!$ok) {
                   4672:             &Apache::lonnet::logthis("Failed get_student_view_with_retries on $symb: ".$response->is_success.', '.$response->code.', '.$response->message);
                   4673:          }
                   4674:          $retries--;
                   4675:     } while (!$ok && ($retries > 0));
                   4676:     
                   4677:     if (!$ok) {
                   4678:        $content = '';          # On error return an empty content.
                   4679:     }
1.651     www      4680:     if (wantarray) {
                   4681:        return ($content, $response);
                   4682:     } else {
                   4683:        return $content;
                   4684:     }
1.11      albertel 4685: }
                   4686: 
1.1075.2.149  raeburn  4687: sub css_links {
                   4688:     my ($currsymb,$level) = @_;
                   4689:     my ($links,@symbs,%cssrefs,%httpref);
                   4690:     if ($level eq 'map') {
                   4691:         my $navmap = Apache::lonnavmaps::navmap->new();
                   4692:         if (ref($navmap)) {
                   4693:             my ($map,undef,$url)=&Apache::lonnet::decode_symb($currsymb);
                   4694:             my @resources = $navmap->retrieveResources($map,sub { $_[0]->is_problem() },0,0);
                   4695:             foreach my $res (@resources) {
                   4696:                 if (ref($res) && $res->symb()) {
                   4697:                     push(@symbs,$res->symb());
                   4698:                 }
                   4699:             }
                   4700:         }
                   4701:     } else {
                   4702:         @symbs = ($currsymb);
                   4703:     }
                   4704:     foreach my $symb (@symbs) {
                   4705:         my $css_href = &Apache::lonnet::EXT('resource.0.cssfile',$symb);
                   4706:         if ($css_href =~ /\S/) {
                   4707:             unless ($css_href =~ m{https?://}) {
                   4708:                 my $url = (&Apache::lonnet::decode_symb($symb))[-1];
                   4709:                 my $proburl =  &Apache::lonnet::clutter($url);
                   4710:                 my ($probdir) = ($proburl =~ m{(.+)/[^/]+$});
                   4711:                 unless ($css_href =~ m{^/}) {
                   4712:                     $css_href = &Apache::lonnet::hreflocation($probdir,$css_href);
                   4713:                 }
                   4714:                 if ($css_href =~ m{^/(res|uploaded)/}) {
                   4715:                     unless (($httpref{'httpref.'.$css_href}) ||
                   4716:                             (&Apache::lonnet::is_on_map($css_href))) {
                   4717:                         my $thisurl = $proburl;
                   4718:                         if ($env{'httpref.'.$proburl}) {
                   4719:                             $thisurl = $env{'httpref.'.$proburl};
                   4720:                         }
                   4721:                         $httpref{'httpref.'.$css_href} = $thisurl;
                   4722:                     }
                   4723:                 }
                   4724:             }
                   4725:             $cssrefs{$css_href} = 1;
                   4726:         }
                   4727:     }
                   4728:     if (keys(%httpref)) {
                   4729:         &Apache::lonnet::appenv(\%httpref);
                   4730:     }
                   4731:     if (keys(%cssrefs)) {
                   4732:         foreach my $css_href (keys(%cssrefs)) {
                   4733:             next unless ($css_href =~ m{^(/res/|/uploaded/|https?://)});
                   4734:             $links .= '<link rel="stylesheet" type="text/css" href="'.$css_href.'" />'."\n";
                   4735:         }
                   4736:     }
                   4737:     return $links;
                   4738: }
                   4739: 
1.112     bowersj2 4740: =pod
                   4741: 
1.648     raeburn  4742: =item * &get_student_answers() 
1.112     bowersj2 4743: 
                   4744: show a snapshot of how student was answering problem
                   4745: 
                   4746: =cut
                   4747: 
1.11      albertel 4748: sub get_student_answers {
1.100     sakharuk 4749:   my ($symb,$username,$domain,$courseid,%form) = @_;
1.114     www      4750:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 4751:   my (%moreenv);
1.11      albertel 4752:   my @elements=('symb','courseid','domain','username');
                   4753:   foreach my $element (@elements) {
1.186     albertel 4754:     $moreenv{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 4755:   }
1.186     albertel 4756:   $moreenv{'grade_target'}='answer';
                   4757:   %moreenv=(%form,%moreenv);
1.497     raeburn  4758:   $feedurl = &Apache::lonnet::clutter($feedurl);
                   4759:   my $userview=&Apache::lonnet::ssi($feedurl,%moreenv);
1.10      albertel 4760:   return $userview;
1.1       albertel 4761: }
1.116     albertel 4762: 
                   4763: =pod
                   4764: 
                   4765: =item * &submlink()
                   4766: 
1.242     albertel 4767: Inputs: $text $uname $udom $symb $target
1.116     albertel 4768: 
                   4769: Returns: A link to grades.pm such as to see the SUBM view of a student
                   4770: 
                   4771: =cut
                   4772: 
                   4773: ###############################################
                   4774: sub submlink {
1.242     albertel 4775:     my ($text,$uname,$udom,$symb,$target)=@_;
1.116     albertel 4776:     if (!($uname && $udom)) {
                   4777: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 4778: 	    &Apache::lonnet::whichuser($symb);
1.116     albertel 4779: 	if (!$symb) { $symb=$cursymb; }
                   4780:     }
1.254     matthew  4781:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      4782:     $symb=&escape($symb);
1.960     bisitz   4783:     if ($target) { $target=" target=\"$target\""; }
                   4784:     return
                   4785:         '<a href="/adm/grades?command=submission'.
                   4786:         '&amp;symb='.$symb.
                   4787:         '&amp;student='.$uname.
                   4788:         '&amp;userdom='.$udom.'"'.
                   4789:         $target.'>'.$text.'</a>';
1.242     albertel 4790: }
                   4791: ##############################################
                   4792: 
                   4793: =pod
                   4794: 
                   4795: =item * &pgrdlink()
                   4796: 
                   4797: Inputs: $text $uname $udom $symb $target
                   4798: 
                   4799: Returns: A link to grades.pm such as to see the PGRD view of a student
                   4800: 
                   4801: =cut
                   4802: 
                   4803: ###############################################
                   4804: sub pgrdlink {
                   4805:     my $link=&submlink(@_);
                   4806:     $link=~s/(&command=submission)/$1&showgrading=yes/;
                   4807:     return $link;
                   4808: }
                   4809: ##############################################
                   4810: 
                   4811: =pod
                   4812: 
                   4813: =item * &pprmlink()
                   4814: 
                   4815: Inputs: $text $uname $udom $symb $target
                   4816: 
                   4817: Returns: A link to parmset.pm such as to see the PPRM view of a
1.283     albertel 4818: student and a specific resource
1.242     albertel 4819: 
                   4820: =cut
                   4821: 
                   4822: ###############################################
                   4823: sub pprmlink {
                   4824:     my ($text,$uname,$udom,$symb,$target)=@_;
                   4825:     if (!($uname && $udom)) {
                   4826: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 4827: 	    &Apache::lonnet::whichuser($symb);
1.242     albertel 4828: 	if (!$symb) { $symb=$cursymb; }
                   4829:     }
1.254     matthew  4830:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      4831:     $symb=&escape($symb);
1.242     albertel 4832:     if ($target) { $target="target=\"$target\""; }
1.595     albertel 4833:     return '<a href="/adm/parmset?command=set&amp;'.
                   4834: 	'symb='.$symb.'&amp;uname='.$uname.
                   4835: 	'&amp;udom='.$udom.'" '.$target.'>'.$text.'</a>';
1.116     albertel 4836: }
                   4837: ##############################################
1.37      matthew  4838: 
1.112     bowersj2 4839: =pod
                   4840: 
                   4841: =back
                   4842: 
                   4843: =cut
                   4844: 
1.37      matthew  4845: ###############################################
1.51      www      4846: 
                   4847: 
                   4848: sub timehash {
1.687     raeburn  4849:     my ($thistime) = @_;
                   4850:     my $timezone = &Apache::lonlocal::gettimezone();
                   4851:     my $dt = DateTime->from_epoch(epoch => $thistime)
                   4852:                      ->set_time_zone($timezone);
                   4853:     my $wday = $dt->day_of_week();
                   4854:     if ($wday == 7) { $wday = 0; }
                   4855:     return ( 'second' => $dt->second(),
                   4856:              'minute' => $dt->minute(),
                   4857:              'hour'   => $dt->hour(),
                   4858:              'day'     => $dt->day_of_month(),
                   4859:              'month'   => $dt->month(),
                   4860:              'year'    => $dt->year(),
                   4861:              'weekday' => $wday,
                   4862:              'dayyear' => $dt->day_of_year(),
                   4863:              'dlsav'   => $dt->is_dst() );
1.51      www      4864: }
                   4865: 
1.370     www      4866: sub utc_string {
                   4867:     my ($date)=@_;
1.371     www      4868:     return strftime("%Y%m%dT%H%M%SZ",gmtime($date));
1.370     www      4869: }
                   4870: 
1.51      www      4871: sub maketime {
                   4872:     my %th=@_;
1.687     raeburn  4873:     my ($epoch_time,$timezone,$dt);
                   4874:     $timezone = &Apache::lonlocal::gettimezone();
                   4875:     eval {
                   4876:         $dt = DateTime->new( year   => $th{'year'},
                   4877:                              month  => $th{'month'},
                   4878:                              day    => $th{'day'},
                   4879:                              hour   => $th{'hour'},
                   4880:                              minute => $th{'minute'},
                   4881:                              second => $th{'second'},
                   4882:                              time_zone => $timezone,
                   4883:                          );
                   4884:     };
                   4885:     if (!$@) {
                   4886:         $epoch_time = $dt->epoch;
                   4887:         if ($epoch_time) {
                   4888:             return $epoch_time;
                   4889:         }
                   4890:     }
1.51      www      4891:     return POSIX::mktime(
                   4892:         ($th{'seconds'},$th{'minutes'},$th{'hours'},
1.210     www      4893:          $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1));
1.70      www      4894: }
                   4895: 
                   4896: #########################################
1.51      www      4897: 
                   4898: sub findallcourses {
1.482     raeburn  4899:     my ($roles,$uname,$udom) = @_;
1.355     albertel 4900:     my %roles;
                   4901:     if (ref($roles)) { %roles = map { $_ => 1 } @{$roles}; }
1.348     albertel 4902:     my %courses;
1.51      www      4903:     my $now=time;
1.482     raeburn  4904:     if (!defined($uname)) {
                   4905:         $uname = $env{'user.name'};
                   4906:     }
                   4907:     if (!defined($udom)) {
                   4908:         $udom = $env{'user.domain'};
                   4909:     }
                   4910:     if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
1.1073    raeburn  4911:         my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname);
1.482     raeburn  4912:         if (!%roles) {
                   4913:             %roles = (
                   4914:                        cc => 1,
1.907     raeburn  4915:                        co => 1,
1.482     raeburn  4916:                        in => 1,
                   4917:                        ep => 1,
                   4918:                        ta => 1,
                   4919:                        cr => 1,
                   4920:                        st => 1,
                   4921:              );
                   4922:         }
                   4923:         foreach my $entry (keys(%roleshash)) {
                   4924:             my ($trole,$tend,$tstart) = split(/_/,$roleshash{$entry});
                   4925:             if ($trole =~ /^cr/) { 
                   4926:                 next if (!exists($roles{$trole}) && !exists($roles{'cr'}));
                   4927:             } else {
                   4928:                 next if (!exists($roles{$trole}));
                   4929:             }
                   4930:             if ($tend) {
                   4931:                 next if ($tend < $now);
                   4932:             }
                   4933:             if ($tstart) {
                   4934:                 next if ($tstart > $now);
                   4935:             }
1.1058    raeburn  4936:             my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role);
1.482     raeburn  4937:             (undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
1.1058    raeburn  4938:             my $value = $trole.'/'.$cdom.'/';
1.482     raeburn  4939:             if ($secpart eq '') {
                   4940:                 ($cnum,$role) = split(/_/,$cnumpart); 
                   4941:                 $sec = 'none';
1.1058    raeburn  4942:                 $value .= $cnum.'/';
1.482     raeburn  4943:             } else {
                   4944:                 $cnum = $cnumpart;
                   4945:                 ($sec,$role) = split(/_/,$secpart);
1.1058    raeburn  4946:                 $value .= $cnum.'/'.$sec;
                   4947:             }
                   4948:             if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
                   4949:                 unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
                   4950:                     push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
                   4951:                 }
                   4952:             } else {
                   4953:                 @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
1.490     raeburn  4954:             }
1.482     raeburn  4955:         }
                   4956:     } else {
                   4957:         foreach my $key (keys(%env)) {
1.483     albertel 4958: 	    if ( $key=~m{^user\.role\.(\w+)\./($match_domain)/($match_courseid)/?(\w*)$} ||
                   4959:                  $key=~m{^user\.role\.(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_courseid)/?(\w*)$}) {
1.482     raeburn  4960: 	        my ($role,$cdom,$cnum,$sec) = ($1,$2,$3,$4);
                   4961: 	        next if ($role eq 'ca' || $role eq 'aa');
                   4962: 	        next if (%roles && !exists($roles{$role}));
                   4963: 	        my ($starttime,$endtime)=split(/\./,$env{$key});
                   4964:                 my $active=1;
                   4965:                 if ($starttime) {
                   4966: 		    if ($now<$starttime) { $active=0; }
                   4967:                 }
                   4968:                 if ($endtime) {
                   4969:                     if ($now>$endtime) { $active=0; }
                   4970:                 }
                   4971:                 if ($active) {
1.1058    raeburn  4972:                     my $value = $role.'/'.$cdom.'/'.$cnum.'/';
1.482     raeburn  4973:                     if ($sec eq '') {
                   4974:                         $sec = 'none';
1.1058    raeburn  4975:                     } else {
                   4976:                         $value .= $sec;
                   4977:                     }
                   4978:                     if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
                   4979:                         unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
                   4980:                             push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
                   4981:                         }
                   4982:                     } else {
                   4983:                         @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
1.482     raeburn  4984:                     }
1.474     raeburn  4985:                 }
                   4986:             }
1.51      www      4987:         }
                   4988:     }
1.474     raeburn  4989:     return %courses;
1.51      www      4990: }
1.37      matthew  4991: 
1.54      www      4992: ###############################################
1.474     raeburn  4993: 
                   4994: sub blockcheck {
1.1075.2.158  raeburn  4995:     my ($setters,$activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller) = @_;
1.490     raeburn  4996: 
1.1075.2.161.  .4(raebu 4997:22):     unless (($activity eq 'docs') || ($activity eq 'reinit') || ($activity eq 'alert')) {
1.1075.2.158  raeburn  4998:         my ($has_evb,$check_ipaccess);
                   4999:         my $dom = $env{'user.domain'};
                   5000:         if ($env{'request.course.id'}) {
                   5001:             my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   5002:             my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
                   5003:             my $checkrole = "cm./$cdom/$cnum";
                   5004:             my $sec = $env{'request.course.sec'};
                   5005:             if ($sec ne '') {
                   5006:                 $checkrole .= "/$sec";
                   5007:             }
                   5008:             if ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
                   5009:                 ($env{'request.role'} !~ /^st/)) {
                   5010:                 $has_evb = 1;
                   5011:             }
                   5012:             unless ($has_evb) {
                   5013:                 if (($activity eq 'printout') || ($activity eq 'grades') || ($activity eq 'search') ||
                   5014:                     ($activity eq 'boards') || ($activity eq 'groups') || ($activity eq 'chat')) {
                   5015:                     if ($udom eq $cdom) {
                   5016:                         $check_ipaccess = 1;
                   5017:                     }
                   5018:                 }
                   5019:             }
1.1075.2.161.  .3(raebu 5020:22):         } elsif (($activity eq 'com') || ($activity eq 'port') || ($activity eq 'blogs') ||
                   5021:22):                 ($activity eq 'about') || ($activity eq 'wishlist') || ($activity eq 'passwd')) {
                   5022:22):             my $checkrole;
                   5023:22):             if ($env{'request.role.domain'} eq '') {
                   5024:22):                 $checkrole = "cm./$env{'user.domain'}/";
                   5025:22):             } else {
                   5026:22):                 $checkrole = "cm./$env{'request.role.domain'}/";
                   5027:22):             }
                   5028:22):             if (($checkrole) && (&Apache::lonnet::allowed('evb',undef,undef,$checkrole))) {
                   5029:22):                 $has_evb = 1;
                   5030:22):             }
1.1075.2.158  raeburn  5031:         }
                   5032:         unless ($has_evb || $check_ipaccess) {
                   5033:             my @machinedoms = &Apache::lonnet::current_machine_domains();
                   5034:             if (($dom eq 'public') && ($activity eq 'port')) {
                   5035:                 $dom = $udom;
                   5036:             }
                   5037:             if (($dom ne '') && (grep(/^\Q$dom\E$/,@machinedoms))) {
                   5038:                 $check_ipaccess = 1;
                   5039:             } else {
                   5040:                 my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
                   5041:                 my $internet_names = &Apache::lonnet::get_internet_names($lonhost);
                   5042:                 my $prim = &Apache::lonnet::domain($dom,'primary');
                   5043:                 my $intdom = &Apache::lonnet::internet_dom($prim);
                   5044:                 if (($intdom ne '') && (ref($internet_names) eq 'ARRAY')) {
                   5045:                     if (grep(/^\Q$intdom\E$/,@{$internet_names})) {
                   5046:                         $check_ipaccess = 1;
                   5047:                     }
                   5048:                 }
                   5049:             }
                   5050:         }
                   5051:         if ($check_ipaccess) {
                   5052:             my ($ipaccessref,$cached)=&Apache::lonnet::is_cached_new('ipaccess',$dom);
                   5053:             unless (defined($cached)) {
                   5054:                 my %domconfig =
                   5055:                     &Apache::lonnet::get_dom('configuration',['ipaccess'],$dom);
                   5056:                 $ipaccessref = &Apache::lonnet::do_cache_new('ipaccess',$dom,$domconfig{'ipaccess'},1800);
                   5057:             }
                   5058:             if ((ref($ipaccessref) eq 'HASH') && ($clientip)) {
                   5059:                 foreach my $id (keys(%{$ipaccessref})) {
                   5060:                     if (ref($ipaccessref->{$id}) eq 'HASH') {
                   5061:                         my $range = $ipaccessref->{$id}->{'ip'};
                   5062:                         if ($range) {
                   5063:                             if (&Apache::lonnet::ip_match($clientip,$range)) {
                   5064:                                 if (ref($ipaccessref->{$id}->{'commblocks'}) eq 'HASH') {
                   5065:                                     if ($ipaccessref->{$id}->{'commblocks'}->{$activity} eq 'on') {
                   5066:                                         return ('','','',$id,$dom);
                   5067:                                         last;
                   5068:                                     }
                   5069:                                 }
                   5070:                             }
                   5071:                         }
                   5072:                     }
                   5073:                 }
                   5074:             }
                   5075:         }
1.1075.2.161.  .4(raebu 5076:22):         if (($activity eq 'wishlist') || ($activity eq 'annotate')) {
                   5077:22):             return ();
                   5078:22):         }
1.1075.2.158  raeburn  5079:     }
1.1075.2.73  raeburn  5080:     if (defined($udom) && defined($uname)) {
                   5081:         # If uname and udom are for a course, check for blocks in the course.
                   5082:         if (($is_course) || (&Apache::lonnet::is_course($udom,$uname))) {
                   5083:             my ($startblock,$endblock,$triggerblock) =
1.1075.2.147  raeburn  5084:                 &get_blocks($setters,$activity,$udom,$uname,$url,$symb,$caller);
1.1075.2.73  raeburn  5085:             return ($startblock,$endblock,$triggerblock);
                   5086:         }
                   5087:     } else {
1.490     raeburn  5088:         $udom = $env{'user.domain'};
                   5089:         $uname = $env{'user.name'};
                   5090:     }
                   5091: 
1.502     raeburn  5092:     my $startblock = 0;
                   5093:     my $endblock = 0;
1.1062    raeburn  5094:     my $triggerblock = '';
1.1075.2.160  raeburn  5095:     my %live_courses;
                   5096:     unless (($activity eq 'wishlist') || ($activity eq 'annotate')) {
                   5097:         %live_courses = &findallcourses(undef,$uname,$udom);
                   5098:     }
1.474     raeburn  5099: 
1.490     raeburn  5100:     # If uname is for a user, and activity is course-specific, i.e.,
                   5101:     # boards, chat or groups, check for blocking in current course only.
1.474     raeburn  5102: 
1.490     raeburn  5103:     if (($activity eq 'boards' || $activity eq 'chat' ||
1.1075.2.161.  .1(raebu 5104:21):          $activity eq 'groups' || $activity eq 'printout' ||
                   5105:21):          $activity eq 'search' || $activity eq 'reinit' ||
                   5106:21):          $activity eq 'alert') && ($env{'request.course.id'})) {
1.490     raeburn  5107:         foreach my $key (keys(%live_courses)) {
                   5108:             if ($key ne $env{'request.course.id'}) {
                   5109:                 delete($live_courses{$key});
                   5110:             }
                   5111:         }
                   5112:     }
                   5113: 
                   5114:     my $otheruser = 0;
                   5115:     my %own_courses;
                   5116:     if ((($uname ne $env{'user.name'})) || ($udom ne $env{'user.domain'})) {
                   5117:         # Resource belongs to user other than current user.
                   5118:         $otheruser = 1;
                   5119:         # Gather courses for current user
                   5120:         %own_courses = 
                   5121:             &findallcourses(undef,$env{'user.name'},$env{'user.domain'});
                   5122:     }
                   5123: 
                   5124:     # Gather active course roles - course coordinator, instructor, 
                   5125:     # exam proctor, ta, student, or custom role.
1.474     raeburn  5126: 
                   5127:     foreach my $course (keys(%live_courses)) {
1.482     raeburn  5128:         my ($cdom,$cnum);
                   5129:         if ((defined($env{'course.'.$course.'.domain'})) && (defined($env{'course.'.$course.'.num'}))) {
                   5130:             $cdom = $env{'course.'.$course.'.domain'};
                   5131:             $cnum = $env{'course.'.$course.'.num'};
                   5132:         } else {
1.490     raeburn  5133:             ($cdom,$cnum) = split(/_/,$course); 
1.482     raeburn  5134:         }
                   5135:         my $no_ownblock = 0;
                   5136:         my $no_userblock = 0;
1.533     raeburn  5137:         if ($otheruser && $activity ne 'com') {
1.490     raeburn  5138:             # Check if current user has 'evb' priv for this
                   5139:             if (defined($own_courses{$course})) {
                   5140:                 foreach my $sec (keys(%{$own_courses{$course}})) {
                   5141:                     my $checkrole = 'cm./'.$cdom.'/'.$cnum;
                   5142:                     if ($sec ne 'none') {
                   5143:                         $checkrole .= '/'.$sec;
                   5144:                     }
                   5145:                     if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   5146:                         $no_ownblock = 1;
                   5147:                         last;
                   5148:                     }
                   5149:                 }
                   5150:             }
                   5151:             # if they have 'evb' priv and are currently not playing student
                   5152:             next if (($no_ownblock) &&
                   5153:                  ($env{'request.role'} !~ m{^st\./$cdom/$cnum}));
                   5154:         }
1.474     raeburn  5155:         foreach my $sec (keys(%{$live_courses{$course}})) {
1.482     raeburn  5156:             my $checkrole = 'cm./'.$cdom.'/'.$cnum;
1.474     raeburn  5157:             if ($sec ne 'none') {
1.482     raeburn  5158:                 $checkrole .= '/'.$sec;
1.474     raeburn  5159:             }
1.490     raeburn  5160:             if ($otheruser) {
                   5161:                 # Resource belongs to user other than current user.
                   5162:                 # Assemble privs for that user, and check for 'evb' priv.
1.1058    raeburn  5163:                 my (%allroles,%userroles);
                   5164:                 if (ref($live_courses{$course}{$sec}) eq 'ARRAY') {
                   5165:                     foreach my $entry (@{$live_courses{$course}{$sec}}) { 
                   5166:                         my ($trole,$tdom,$tnum,$tsec);
                   5167:                         if ($entry =~ /^cr/) {
                   5168:                             ($trole,$tdom,$tnum,$tsec) = 
                   5169:                                 ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
                   5170:                         } else {
                   5171:                            ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
                   5172:                         }
                   5173:                         my ($spec,$area,$trest);
                   5174:                         $area = '/'.$tdom.'/'.$tnum;
                   5175:                         $trest = $tnum;
                   5176:                         if ($tsec ne '') {
                   5177:                             $area .= '/'.$tsec;
                   5178:                             $trest .= '/'.$tsec;
                   5179:                         }
                   5180:                         $spec = $trole.'.'.$area;
                   5181:                         if ($trole =~ /^cr/) {
                   5182:                             &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
                   5183:                                                               $tdom,$spec,$trest,$area);
                   5184:                         } else {
                   5185:                             &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
                   5186:                                                                 $tdom,$spec,$trest,$area);
                   5187:                         }
                   5188:                     }
1.1075.2.124  raeburn  5189:                     my ($author,$adv,$rar) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
1.1058    raeburn  5190:                     if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
                   5191:                         if ($1) {
                   5192:                             $no_userblock = 1;
                   5193:                             last;
                   5194:                         }
1.486     raeburn  5195:                     }
                   5196:                 }
1.490     raeburn  5197:             } else {
                   5198:                 # Resource belongs to current user
                   5199:                 # Check for 'evb' priv via lonnet::allowed().
1.482     raeburn  5200:                 if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   5201:                     $no_ownblock = 1;
                   5202:                     last;
                   5203:                 }
1.474     raeburn  5204:             }
                   5205:         }
                   5206:         # if they have the evb priv and are currently not playing student
1.482     raeburn  5207:         next if (($no_ownblock) &&
1.491     albertel 5208:                  ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E}));
1.482     raeburn  5209:         next if ($no_userblock);
1.474     raeburn  5210: 
1.1075.2.128  raeburn  5211:         # Retrieve blocking times and identity of blocker for course
1.490     raeburn  5212:         # of specified user, unless user has 'evb' privilege.
1.502     raeburn  5213:         
1.1062    raeburn  5214:         my ($start,$end,$trigger) = 
1.1075.2.147  raeburn  5215:             &get_blocks($setters,$activity,$cdom,$cnum,$url,$symb,$caller);
1.502     raeburn  5216:         if (($start != 0) && 
                   5217:             (($startblock == 0) || ($startblock > $start))) {
                   5218:             $startblock = $start;
1.1062    raeburn  5219:             if ($trigger ne '') {
                   5220:                 $triggerblock = $trigger;
                   5221:             }
1.502     raeburn  5222:         }
                   5223:         if (($end != 0)  &&
                   5224:             (($endblock == 0) || ($endblock < $end))) {
                   5225:             $endblock = $end;
1.1062    raeburn  5226:             if ($trigger ne '') {
                   5227:                 $triggerblock = $trigger;
                   5228:             }
1.502     raeburn  5229:         }
1.490     raeburn  5230:     }
1.1062    raeburn  5231:     return ($startblock,$endblock,$triggerblock);
1.490     raeburn  5232: }
                   5233: 
                   5234: sub get_blocks {
1.1075.2.147  raeburn  5235:     my ($setters,$activity,$cdom,$cnum,$url,$symb,$caller) = @_;
1.490     raeburn  5236:     my $startblock = 0;
                   5237:     my $endblock = 0;
1.1062    raeburn  5238:     my $triggerblock = '';
1.490     raeburn  5239:     my $course = $cdom.'_'.$cnum;
                   5240:     $setters->{$course} = {};
                   5241:     $setters->{$course}{'staff'} = [];
                   5242:     $setters->{$course}{'times'} = [];
1.1062    raeburn  5243:     $setters->{$course}{'triggers'} = [];
                   5244:     my (@blockers,%triggered);
                   5245:     my $now = time;
                   5246:     my %commblocks = &Apache::lonnet::get_comm_blocks($cdom,$cnum);
                   5247:     if ($activity eq 'docs') {
1.1075.2.148  raeburn  5248:         my ($blocked,$nosymbcache,$noenccheck);
1.1075.2.147  raeburn  5249:         if (($caller eq 'blockedaccess') || ($caller eq 'blockingstatus')) {
                   5250:             $blocked = 1;
                   5251:             $nosymbcache = 1;
1.1075.2.148  raeburn  5252:             $noenccheck = 1;
1.1075.2.147  raeburn  5253:         }
1.1075.2.148  raeburn  5254:         @blockers = &Apache::lonnet::has_comm_blocking('bre',$symb,$url,$nosymbcache,$noenccheck,$blocked,\%commblocks);
1.1062    raeburn  5255:         foreach my $block (@blockers) {
                   5256:             if ($block =~ /^firstaccess____(.+)$/) {
                   5257:                 my $item = $1;
                   5258:                 my $type = 'map';
                   5259:                 my $timersymb = $item;
                   5260:                 if ($item eq 'course') {
                   5261:                     $type = 'course';
                   5262:                 } elsif ($item =~ /___\d+___/) {
                   5263:                     $type = 'resource';
                   5264:                 } else {
                   5265:                     $timersymb = &Apache::lonnet::symbread($item);
                   5266:                 }
                   5267:                 my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
                   5268:                 my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb};
                   5269:                 $triggered{$block} = {
                   5270:                                        start => $start,
                   5271:                                        end   => $end,
                   5272:                                        type  => $type,
                   5273:                                      };
                   5274:             }
                   5275:         }
                   5276:     } else {
                   5277:         foreach my $block (keys(%commblocks)) {
                   5278:             if ($block =~ m/^(\d+)____(\d+)$/) { 
                   5279:                 my ($start,$end) = ($1,$2);
                   5280:                 if ($start <= time && $end >= time) {
                   5281:                     if (ref($commblocks{$block}) eq 'HASH') {
                   5282:                         if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
                   5283:                             if ($commblocks{$block}{'blocks'}{$activity} eq 'on') {
                   5284:                                 unless(grep(/^\Q$block\E$/,@blockers)) {
                   5285:                                     push(@blockers,$block);
                   5286:                                 }
                   5287:                             }
                   5288:                         }
                   5289:                     }
                   5290:                 }
                   5291:             } elsif ($block =~ /^firstaccess____(.+)$/) {
                   5292:                 my $item = $1;
                   5293:                 my $timersymb = $item; 
                   5294:                 my $type = 'map';
                   5295:                 if ($item eq 'course') {
                   5296:                     $type = 'course';
                   5297:                 } elsif ($item =~ /___\d+___/) {
                   5298:                     $type = 'resource';
                   5299:                 } else {
                   5300:                     $timersymb = &Apache::lonnet::symbread($item);
                   5301:                 }
                   5302:                 my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
                   5303:                 my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb}; 
                   5304:                 if ($start && $end) {
                   5305:                     if (($start <= time) && ($end >= time)) {
1.1075.2.158  raeburn  5306:                         if (ref($commblocks{$block}) eq 'HASH') {
                   5307:                             if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
                   5308:                                 if ($commblocks{$block}{'blocks'}{$activity} eq 'on') {
                   5309:                                     unless(grep(/^\Q$block\E$/,@blockers)) {
                   5310:                                         push(@blockers,$block);
                   5311:                                         $triggered{$block} = {
                   5312:                                                                start => $start,
                   5313:                                                                end   => $end,
                   5314:                                                                type  => $type,
                   5315:                                                              };
                   5316:                                     }
                   5317:                                 }
                   5318:                             }
1.1062    raeburn  5319:                         }
                   5320:                     }
1.490     raeburn  5321:                 }
1.1062    raeburn  5322:             }
                   5323:         }
                   5324:     }
                   5325:     foreach my $blocker (@blockers) {
                   5326:         my ($staff_name,$staff_dom,$title,$blocks) =
                   5327:             &parse_block_record($commblocks{$blocker});
                   5328:         push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
                   5329:         my ($start,$end,$triggertype);
                   5330:         if ($blocker =~ m/^(\d+)____(\d+)$/) {
                   5331:             ($start,$end) = ($1,$2);
                   5332:         } elsif (ref($triggered{$blocker}) eq 'HASH') {
                   5333:             $start = $triggered{$blocker}{'start'};
                   5334:             $end = $triggered{$blocker}{'end'};
                   5335:             $triggertype = $triggered{$blocker}{'type'};
                   5336:         }
                   5337:         if ($start) {
                   5338:             push(@{$$setters{$course}{'times'}}, [$start,$end]);
                   5339:             if ($triggertype) {
                   5340:                 push(@{$$setters{$course}{'triggers'}},$triggertype);
                   5341:             } else {
                   5342:                 push(@{$$setters{$course}{'triggers'}},0);
                   5343:             }
                   5344:             if ( ($startblock == 0) || ($startblock > $start) ) {
                   5345:                 $startblock = $start;
                   5346:                 if ($triggertype) {
                   5347:                     $triggerblock = $blocker;
1.474     raeburn  5348:                 }
                   5349:             }
1.1062    raeburn  5350:             if ( ($endblock == 0) || ($endblock < $end) ) {
                   5351:                $endblock = $end;
                   5352:                if ($triggertype) {
                   5353:                    $triggerblock = $blocker;
                   5354:                }
                   5355:             }
1.474     raeburn  5356:         }
                   5357:     }
1.1062    raeburn  5358:     return ($startblock,$endblock,$triggerblock);
1.474     raeburn  5359: }
                   5360: 
                   5361: sub parse_block_record {
                   5362:     my ($record) = @_;
                   5363:     my ($setuname,$setudom,$title,$blocks);
                   5364:     if (ref($record) eq 'HASH') {
                   5365:         ($setuname,$setudom) = split(/:/,$record->{'setter'});
                   5366:         $title = &unescape($record->{'event'});
                   5367:         $blocks = $record->{'blocks'};
                   5368:     } else {
                   5369:         my @data = split(/:/,$record,3);
                   5370:         if (scalar(@data) eq 2) {
                   5371:             $title = $data[1];
                   5372:             ($setuname,$setudom) = split(/@/,$data[0]);
                   5373:         } else {
                   5374:             ($setuname,$setudom,$title) = @data;
                   5375:         }
                   5376:         $blocks = { 'com' => 'on' };
                   5377:     }
                   5378:     return ($setuname,$setudom,$title,$blocks);
                   5379: }
                   5380: 
1.854     kalberla 5381: sub blocking_status {
1.1075.2.158  raeburn  5382:     my ($activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller) = @_;
1.1061    raeburn  5383:     my %setters;
1.890     droeschl 5384: 
1.1061    raeburn  5385: # check for active blocking
1.1075.2.158  raeburn  5386:     if ($clientip eq '') {
                   5387:         $clientip = &Apache::lonnet::get_requestor_ip();
                   5388:     }
                   5389:     my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) = 
                   5390:         &blockcheck(\%setters,$activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller);
1.1062    raeburn  5391:     my $blocked = 0;
1.1075.2.158  raeburn  5392:     if (($startblock && $endblock) || ($by_ip)) {
1.1062    raeburn  5393:         $blocked = 1;
                   5394:     }
1.890     droeschl 5395: 
1.1061    raeburn  5396: # caller just wants to know whether a block is active
                   5397:     if (!wantarray) { return $blocked; }
                   5398: 
                   5399: # build a link to a popup window containing the details
                   5400:     my $querystring  = "?activity=$activity";
1.1075.2.158  raeburn  5401: # $uname and $udom decide whose portfolio (or information page) the user is trying to look at
                   5402:     if (($activity eq 'port') || ($activity eq 'about') || ($activity eq 'passwd')) {
1.1075.2.97  raeburn  5403:         $querystring .= "&amp;udom=$udom"      if ($udom =~ /^$match_domain$/);
                   5404:         $querystring .= "&amp;uname=$uname"    if ($uname =~ /^$match_username$/);
1.1062    raeburn  5405:     } elsif ($activity eq 'docs') {
1.1075.2.147  raeburn  5406:         my $showurl = &Apache::lonenc::check_encrypt($url);
                   5407:         $querystring .= '&amp;url='.&HTML::Entities::encode($showurl,'\'&"<>');
                   5408:         if ($symb) {
                   5409:             my $showsymb = &Apache::lonenc::check_encrypt($symb);
                   5410:             $querystring .= '&amp;symb='.&HTML::Entities::encode($showsymb,'\'&"<>');
                   5411:         }
1.1062    raeburn  5412:     }
1.1061    raeburn  5413: 
                   5414:     my $output .= <<'END_MYBLOCK';
                   5415: function openWindow(url, wdwName, w, h, toolbar,scrollbar) {
                   5416:     var options = "width=" + w + ",height=" + h + ",";
                   5417:     options += "resizable=yes,scrollbars="+scrollbar+",status=no,";
                   5418:     options += "menubar=no,toolbar="+toolbar+",location=no,directories=no";
                   5419:     var newWin = window.open(url, wdwName, options);
                   5420:     newWin.focus();
                   5421: }
1.890     droeschl 5422: END_MYBLOCK
1.854     kalberla 5423: 
1.1061    raeburn  5424:     $output = Apache::lonhtmlcommon::scripttag($output);
1.890     droeschl 5425:   
1.1061    raeburn  5426:     my $popupUrl = "/adm/blockingstatus/$querystring";
1.1062    raeburn  5427:     my $text = &mt('Communication Blocked');
1.1075.2.93  raeburn  5428:     my $class = 'LC_comblock';
1.1062    raeburn  5429:     if ($activity eq 'docs') {
                   5430:         $text = &mt('Content Access Blocked');
1.1075.2.93  raeburn  5431:         $class = '';
1.1063    raeburn  5432:     } elsif ($activity eq 'printout') {
                   5433:         $text = &mt('Printing Blocked');
1.1075.2.97  raeburn  5434:     } elsif ($activity eq 'passwd') {
                   5435:         $text = &mt('Password Changing Blocked');
1.1075.2.158  raeburn  5436:     } elsif ($activity eq 'grades') {
                   5437:         $text = &mt('Gradebook Blocked');
                   5438:     } elsif ($activity eq 'search') {
                   5439:         $text = &mt('Search Blocked');
1.1075.2.161.  .1(raebu 5440:21):     } elsif ($activity eq 'alert') {
                   5441:21):         $text = &mt('Checking Critical Messages Blocked');
                   5442:21):     } elsif ($activity eq 'reinit') {
                   5443:21):         $text = &mt('Checking Course Update Blocked');
1.1075.2.158  raeburn  5444:     } elsif ($activity eq 'about') {
                   5445:         $text = &mt('Access to User Information Pages Blocked');
1.1075.2.160  raeburn  5446:     } elsif ($activity eq 'wishlist') {
                   5447:         $text = &mt('Access to Stored Links Blocked');
                   5448:     } elsif ($activity eq 'annotate') {
                   5449:         $text = &mt('Access to Annotations Blocked');
1.1062    raeburn  5450:     }
1.1061    raeburn  5451:     $output .= <<"END_BLOCK";
1.1075.2.93  raeburn  5452: <div class='$class'>
1.869     kalberla 5453:   <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring'
1.890     droeschl 5454:   title='$text'>
                   5455:   <img class='LC_noBorder LC_middle' title='$text' src='/res/adm/pages/comblock.png' alt='$text'/></a>
1.869     kalberla 5456:   <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring' 
1.890     droeschl 5457:   title='$text'>$text</a>
1.867     kalberla 5458: </div>
                   5459: 
                   5460: END_BLOCK
1.474     raeburn  5461: 
1.1061    raeburn  5462:     return ($blocked, $output);
1.854     kalberla 5463: }
1.490     raeburn  5464: 
1.60      matthew  5465: ###############################################
                   5466: 
1.682     raeburn  5467: sub check_ip_acc {
1.1075.2.105  raeburn  5468:     my ($acc,$clientip)=@_;
1.682     raeburn  5469:     &Apache::lonxml::debug("acc is $acc");
                   5470:     if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) {
                   5471:         return 1;
                   5472:     }
                   5473:     my $allowed=0;
1.1075.2.144  raeburn  5474:     my $ip;
                   5475:     if (($ENV{'REMOTE_ADDR'} eq '127.0.0.1') ||
                   5476:         ($ENV{'REMOTE_ADDR'} eq &Apache::lonnet::get_host_ip($Apache::lonnet::perlvar{'lonHostID'}))) {
                   5477:         $ip = $env{'request.host'} || $ENV{'REMOTE_ADDR'} || $clientip;
                   5478:     } else {
1.1075.2.150  raeburn  5479:         my $remote_ip = &Apache::lonnet::get_requestor_ip();
                   5480:         $ip = $remote_ip || $env{'request.host'} || $clientip;
1.1075.2.144  raeburn  5481:     }
1.682     raeburn  5482: 
                   5483:     my $name;
1.1075.2.161.  .1(raebu 5484:21):     my %access = (
                   5485:21):                      allowfrom => 1,
                   5486:21):                      denyfrom  => 0,
                   5487:21):                  );
                   5488:21):     my @allows;
                   5489:21):     my @denies;
                   5490:21):     foreach my $item (split(',',$acc)) {
                   5491:21):         $item =~ s/^\s*//;
                   5492:21):         $item =~ s/\s*$//;
                   5493:21):         if ($item =~ /^\!(.+)$/) {
                   5494:21):             push(@denies,$1);
                   5495:21):         } else {
                   5496:21):             push(@allows,$item);
                   5497:21):         }
                   5498:21):     }
                   5499:21):     my $numdenies = scalar(@denies);
                   5500:21):     my $numallows = scalar(@allows);
                   5501:21):     my $count = 0;
                   5502:21):     foreach my $pattern (@denies,@allows) {
                   5503:21):         $count ++;
                   5504:21):         my $acctype = 'allowfrom';
                   5505:21):         if ($count <= $numdenies) {
                   5506:21):             $acctype = 'denyfrom';
                   5507:21):         }
1.682     raeburn  5508:         if ($pattern =~ /\*$/) {
                   5509:             #35.8.*
                   5510:             $pattern=~s/\*//;
1.1075.2.161.  .1(raebu 5511:21):             if ($ip =~ /^\Q$pattern\E/) { $allowed=$access{$acctype}; }
1.682     raeburn  5512:         } elsif ($pattern =~ /(\d+\.\d+\.\d+)\.\[(\d+)-(\d+)\]$/) {
                   5513:             #35.8.3.[34-56]
                   5514:             my $low=$2;
                   5515:             my $high=$3;
                   5516:             $pattern=$1;
                   5517:             if ($ip =~ /^\Q$pattern\E/) {
                   5518:                 my $last=(split(/\./,$ip))[3];
1.1075.2.161.  .1(raebu 5519:21):                 if ($last <=$high && $last >=$low) { $allowed=$access{$acctype}; }
1.682     raeburn  5520:             }
                   5521:         } elsif ($pattern =~ /^\*/) {
                   5522:             #*.msu.edu
                   5523:             $pattern=~s/\*//;
                   5524:             if (!defined($name)) {
                   5525:                 use Socket;
                   5526:                 my $netaddr=inet_aton($ip);
                   5527:                 ($name)=gethostbyaddr($netaddr,AF_INET);
                   5528:             }
1.1075.2.161.  .1(raebu 5529:21):             if ($name =~ /\Q$pattern\E$/i) { $allowed=$access{$acctype}; }
1.682     raeburn  5530:         } elsif ($pattern =~ /\d+\.\d+\.\d+\.\d+/) {
                   5531:             #127.0.0.1
1.1075.2.161.  .1(raebu 5532:21):             if ($ip =~ /^\Q$pattern\E/) { $allowed=$access{$acctype}; }
1.682     raeburn  5533:         } else {
                   5534:             #some.name.com
                   5535:             if (!defined($name)) {
                   5536:                 use Socket;
                   5537:                 my $netaddr=inet_aton($ip);
                   5538:                 ($name)=gethostbyaddr($netaddr,AF_INET);
                   5539:             }
1.1075.2.161.  .1(raebu 5540:21):             if ($name =~ /\Q$pattern\E$/i) { $allowed=$access{$acctype}; }
                   5541:21):         }
                   5542:21):         if ($allowed =~ /^(0|1)$/) { last; }
                   5543:21):     }
                   5544:21):     if ($allowed eq '') {
                   5545:21):         if ($numdenies && !$numallows) {
                   5546:21):             $allowed = 1;
                   5547:21):         } else {
                   5548:21):             $allowed = 0;
1.682     raeburn  5549:         }
                   5550:     }
                   5551:     return $allowed;
                   5552: }
                   5553: 
                   5554: ###############################################
                   5555: 
1.60      matthew  5556: =pod
                   5557: 
1.112     bowersj2 5558: =head1 Domain Template Functions
                   5559: 
                   5560: =over 4
                   5561: 
                   5562: =item * &determinedomain()
1.60      matthew  5563: 
                   5564: Inputs: $domain (usually will be undef)
                   5565: 
1.63      www      5566: Returns: Determines which domain should be used for designs
1.60      matthew  5567: 
                   5568: =cut
1.54      www      5569: 
1.60      matthew  5570: ###############################################
1.63      www      5571: sub determinedomain {
                   5572:     my $domain=shift;
1.531     albertel 5573:     if (! $domain) {
1.60      matthew  5574:         # Determine domain if we have not been given one
1.893     raeburn  5575:         $domain = &Apache::lonnet::default_login_domain();
1.258     albertel 5576:         if ($env{'user.domain'}) { $domain=$env{'user.domain'}; }
                   5577:         if ($env{'request.role.domain'}) { 
                   5578:             $domain=$env{'request.role.domain'}; 
1.60      matthew  5579:         }
                   5580:     }
1.63      www      5581:     return $domain;
                   5582: }
                   5583: ###############################################
1.517     raeburn  5584: 
1.518     albertel 5585: sub devalidate_domconfig_cache {
                   5586:     my ($udom)=@_;
                   5587:     &Apache::lonnet::devalidate_cache_new('domainconfig',$udom);
                   5588: }
                   5589: 
                   5590: # ---------------------- Get domain configuration for a domain
                   5591: sub get_domainconf {
                   5592:     my ($udom) = @_;
                   5593:     my $cachetime=1800;
                   5594:     my ($result,$cached)=&Apache::lonnet::is_cached_new('domainconfig',$udom);
                   5595:     if (defined($cached)) { return %{$result}; }
                   5596: 
                   5597:     my %domconfig = &Apache::lonnet::get_dom('configuration',
1.948     raeburn  5598: 					     ['login','rolecolors','autoenroll'],$udom);
1.632     raeburn  5599:     my (%designhash,%legacy);
1.518     albertel 5600:     if (keys(%domconfig) > 0) {
                   5601:         if (ref($domconfig{'login'}) eq 'HASH') {
1.632     raeburn  5602:             if (keys(%{$domconfig{'login'}})) {
                   5603:                 foreach my $key (keys(%{$domconfig{'login'}})) {
1.699     raeburn  5604:                     if (ref($domconfig{'login'}{$key}) eq 'HASH') {
1.1075.2.87  raeburn  5605:                         if (($key eq 'loginvia') || ($key eq 'headtag')) {
                   5606:                             if (ref($domconfig{'login'}{$key}) eq 'HASH') {
                   5607:                                 foreach my $hostname (keys(%{$domconfig{'login'}{$key}})) {
                   5608:                                     if (ref($domconfig{'login'}{$key}{$hostname}) eq 'HASH') {
                   5609:                                         if ($key eq 'loginvia') {
                   5610:                                             if ($domconfig{'login'}{'loginvia'}{$hostname}{'server'}) {
                   5611:                                                 my $server = $domconfig{'login'}{'loginvia'}{$hostname}{'server'};
                   5612:                                                 $designhash{$udom.'.login.loginvia'} = $server;
                   5613:                                                 if ($domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'} eq 'custom') {
                   5614:                                                     $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'custompath'};
                   5615:                                                 } else {
                   5616:                                                     $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'};
                   5617:                                                 }
1.948     raeburn  5618:                                             }
1.1075.2.87  raeburn  5619:                                         } elsif ($key eq 'headtag') {
                   5620:                                             if ($domconfig{'login'}{'headtag'}{$hostname}{'url'}) {
                   5621:                                                 $designhash{$udom.'.login.headtag_'.$hostname} = $domconfig{'login'}{'headtag'}{$hostname}{'url'};
1.948     raeburn  5622:                                             }
1.946     raeburn  5623:                                         }
1.1075.2.87  raeburn  5624:                                         if ($domconfig{'login'}{$key}{$hostname}{'exempt'}) {
                   5625:                                             $designhash{$udom.'.login.'.$key.'_exempt_'.$hostname} = $domconfig{'login'}{$key}{$hostname}{'exempt'};
                   5626:                                         }
1.946     raeburn  5627:                                     }
                   5628:                                 }
                   5629:                             }
1.1075.2.158  raeburn  5630:                         } elsif ($key eq 'saml') {
                   5631:                             if (ref($domconfig{'login'}{$key}) eq 'HASH') {
                   5632:                                 foreach my $host (keys(%{$domconfig{'login'}{$key}})) {
                   5633:                                     if (ref($domconfig{'login'}{$key}{$host}) eq 'HASH') {
                   5634:                                         $designhash{$udom.'.login.'.$key.'_'.$host} = 1;
1.1075.2.161.  .9(raebu 5635:22):                                         foreach my $item ('text','img','alt','url','title','window','notsso') {
1.1075.2.158  raeburn  5636:                                             $designhash{$udom.'.login.'.$key.'_'.$item.'_'.$host} = $domconfig{'login'}{$key}{$host}{$item};
                   5637:                                         }
                   5638:                                     }
                   5639:                                 }
                   5640:                             }
1.946     raeburn  5641:                         } else {
                   5642:                             foreach my $img (keys(%{$domconfig{'login'}{$key}})) {
                   5643:                                 $designhash{$udom.'.login.'.$key.'_'.$img} = 
                   5644:                                     $domconfig{'login'}{$key}{$img};
                   5645:                             }
1.699     raeburn  5646:                         }
                   5647:                     } else {
                   5648:                         $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
                   5649:                     }
1.632     raeburn  5650:                 }
                   5651:             } else {
                   5652:                 $legacy{'login'} = 1;
1.518     albertel 5653:             }
1.632     raeburn  5654:         } else {
                   5655:             $legacy{'login'} = 1;
1.518     albertel 5656:         }
                   5657:         if (ref($domconfig{'rolecolors'}) eq 'HASH') {
1.632     raeburn  5658:             if (keys(%{$domconfig{'rolecolors'}})) {
                   5659:                 foreach my $role (keys(%{$domconfig{'rolecolors'}})) {
                   5660:                     if (ref($domconfig{'rolecolors'}{$role}) eq 'HASH') {
                   5661:                         foreach my $item (keys(%{$domconfig{'rolecolors'}{$role}})) {
                   5662:                             $designhash{$udom.'.'.$role.'.'.$item}=$domconfig{'rolecolors'}{$role}{$item};
                   5663:                         }
1.518     albertel 5664:                     }
                   5665:                 }
1.632     raeburn  5666:             } else {
                   5667:                 $legacy{'rolecolors'} = 1;
1.518     albertel 5668:             }
1.632     raeburn  5669:         } else {
                   5670:             $legacy{'rolecolors'} = 1;
1.518     albertel 5671:         }
1.948     raeburn  5672:         if (ref($domconfig{'autoenroll'}) eq 'HASH') {
                   5673:             if ($domconfig{'autoenroll'}{'co-owners'}) {
                   5674:                 $designhash{$udom.'.autoassign.co-owners'}=$domconfig{'autoenroll'}{'co-owners'};
                   5675:             }
                   5676:         }
1.632     raeburn  5677:         if (keys(%legacy) > 0) {
                   5678:             my %legacyhash = &get_legacy_domconf($udom);
                   5679:             foreach my $item (keys(%legacyhash)) {
                   5680:                 if ($item =~ /^\Q$udom\E\.login/) {
                   5681:                     if ($legacy{'login'}) { 
                   5682:                         $designhash{$item} = $legacyhash{$item};
                   5683:                     }
                   5684:                 } else {
                   5685:                     if ($legacy{'rolecolors'}) {
                   5686:                         $designhash{$item} = $legacyhash{$item};
                   5687:                     }
1.518     albertel 5688:                 }
                   5689:             }
                   5690:         }
1.632     raeburn  5691:     } else {
                   5692:         %designhash = &get_legacy_domconf($udom); 
1.518     albertel 5693:     }
                   5694:     &Apache::lonnet::do_cache_new('domainconfig',$udom,\%designhash,
                   5695: 				  $cachetime);
                   5696:     return %designhash;
                   5697: }
                   5698: 
1.632     raeburn  5699: sub get_legacy_domconf {
                   5700:     my ($udom) = @_;
                   5701:     my %legacyhash;
                   5702:     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
                   5703:     my $designfile =  $designdir.'/'.$udom.'.tab';
                   5704:     if (-e $designfile) {
1.1075.2.128  raeburn  5705:         if ( open (my $fh,'<',$designfile) ) {
1.632     raeburn  5706:             while (my $line = <$fh>) {
                   5707:                 next if ($line =~ /^\#/);
                   5708:                 chomp($line);
                   5709:                 my ($key,$val)=(split(/\=/,$line));
                   5710:                 if ($val) { $legacyhash{$udom.'.'.$key}=$val; }
                   5711:             }
                   5712:             close($fh);
                   5713:         }
                   5714:     }
1.1026    raeburn  5715:     if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/adm/lonDomLogos/'.$udom.'.gif') {
1.632     raeburn  5716:         $legacyhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
                   5717:     }
                   5718:     return %legacyhash;
                   5719: }
                   5720: 
1.63      www      5721: =pod
                   5722: 
1.112     bowersj2 5723: =item * &domainlogo()
1.63      www      5724: 
                   5725: Inputs: $domain (usually will be undef)
                   5726: 
                   5727: Returns: A link to a domain logo, if the domain logo exists.
                   5728: If the domain logo does not exist, a description of the domain.
                   5729: 
                   5730: =cut
1.112     bowersj2 5731: 
1.63      www      5732: ###############################################
                   5733: sub domainlogo {
1.517     raeburn  5734:     my $domain = &determinedomain(shift);
1.518     albertel 5735:     my %designhash = &get_domainconf($domain);    
1.517     raeburn  5736:     # See if there is a logo
                   5737:     if ($designhash{$domain.'.login.domlogo'} ne '') {
1.519     raeburn  5738:         my $imgsrc = $designhash{$domain.'.login.domlogo'};
1.538     albertel 5739:         if ($imgsrc =~ m{^/(adm|res)/}) {
                   5740: 	    if ($imgsrc =~ m{^/res/}) {
                   5741: 		my $local_name = &Apache::lonnet::filelocation('',$imgsrc);
                   5742: 		&Apache::lonnet::repcopy($local_name);
                   5743: 	    }
                   5744: 	   $imgsrc = &lonhttpdurl($imgsrc);
1.1075.2.161.  .2(raebu 5745:22):         }
                   5746:22):         my $alttext = $domain;
                   5747:22):         if ($designhash{$domain.'.login.alttext_domlogo'} ne '') {
                   5748:22):             $alttext = $designhash{$domain.'.login.alttext_domlogo'};
                   5749:22):         }
                   5750:22):         return '<img src="'.$imgsrc.'" alt="'.$alttext.'" id="lclogindomlogo" />';
1.514     albertel 5751:     } elsif (defined(&Apache::lonnet::domain($domain,'description'))) {
                   5752:         return &Apache::lonnet::domain($domain,'description');
1.59      www      5753:     } else {
1.60      matthew  5754:         return '';
1.59      www      5755:     }
                   5756: }
1.63      www      5757: ##############################################
                   5758: 
                   5759: =pod
                   5760: 
1.112     bowersj2 5761: =item * &designparm()
1.63      www      5762: 
                   5763: Inputs: $which parameter; $domain (usually will be undef)
                   5764: 
                   5765: Returns: value of designparamter $which
                   5766: 
                   5767: =cut
1.112     bowersj2 5768: 
1.397     albertel 5769: 
1.400     albertel 5770: ##############################################
1.397     albertel 5771: sub designparm {
                   5772:     my ($which,$domain)=@_;
                   5773:     if (exists($env{'environment.color.'.$which})) {
1.817     bisitz   5774:         return $env{'environment.color.'.$which};
1.96      www      5775:     }
1.63      www      5776:     $domain=&determinedomain($domain);
1.1016    raeburn  5777:     my %domdesign;
                   5778:     unless ($domain eq 'public') {
                   5779:         %domdesign = &get_domainconf($domain);
                   5780:     }
1.520     raeburn  5781:     my $output;
1.517     raeburn  5782:     if ($domdesign{$domain.'.'.$which} ne '') {
1.817     bisitz   5783:         $output = $domdesign{$domain.'.'.$which};
1.63      www      5784:     } else {
1.520     raeburn  5785:         $output = $defaultdesign{$which};
                   5786:     }
                   5787:     if (($which =~ /^(student|coordinator|author|admin)\.img$/) ||
1.635     raeburn  5788:         ($which =~ /login\.(img|logo|domlogo|login)/)) {
1.538     albertel 5789:         if ($output =~ m{^/(adm|res)/}) {
1.817     bisitz   5790:             if ($output =~ m{^/res/}) {
                   5791:                 my $local_name = &Apache::lonnet::filelocation('',$output);
                   5792:                 &Apache::lonnet::repcopy($local_name);
                   5793:             }
1.520     raeburn  5794:             $output = &lonhttpdurl($output);
                   5795:         }
1.63      www      5796:     }
1.520     raeburn  5797:     return $output;
1.63      www      5798: }
1.59      www      5799: 
1.822     bisitz   5800: ##############################################
                   5801: =pod
                   5802: 
1.832     bisitz   5803: =item * &authorspace()
                   5804: 
1.1028    raeburn  5805: Inputs: $url (usually will be undef).
1.832     bisitz   5806: 
1.1075.2.40  raeburn  5807: Returns: Path to Authoring Space containing the resource or 
1.1028    raeburn  5808:          directory being viewed (or for which action is being taken). 
                   5809:          If $url is provided, and begins /priv/<domain>/<uname>
                   5810:          the path will be that portion of the $context argument.
                   5811:          Otherwise the path will be for the author space of the current
                   5812:          user when the current role is author, or for that of the 
                   5813:          co-author/assistant co-author space when the current role 
                   5814:          is co-author or assistant co-author.
1.832     bisitz   5815: 
                   5816: =cut
                   5817: 
                   5818: sub authorspace {
1.1028    raeburn  5819:     my ($url) = @_;
                   5820:     if ($url ne '') {
                   5821:         if ($url =~ m{^(/priv/$match_domain/$match_username/)}) {
                   5822:            return $1;
                   5823:         }
                   5824:     }
1.832     bisitz   5825:     my $caname = '';
1.1024    www      5826:     my $cadom = '';
1.1028    raeburn  5827:     if ($env{'request.role'} =~ /^(?:ca|aa)/) {
1.1024    www      5828:         ($cadom,$caname) =
1.832     bisitz   5829:             ($env{'request.role'}=~/($match_domain)\/($match_username)$/);
1.1028    raeburn  5830:     } elsif ($env{'request.role'} =~ m{^au\./($match_domain)/}) {
1.832     bisitz   5831:         $caname = $env{'user.name'};
1.1024    www      5832:         $cadom = $env{'user.domain'};
1.832     bisitz   5833:     }
1.1028    raeburn  5834:     if (($caname ne '') && ($cadom ne '')) {
                   5835:         return "/priv/$cadom/$caname/";
                   5836:     }
                   5837:     return;
1.832     bisitz   5838: }
                   5839: 
                   5840: ##############################################
                   5841: =pod
                   5842: 
1.822     bisitz   5843: =item * &head_subbox()
                   5844: 
                   5845: Inputs: $content (contains HTML code with page functions, etc.)
                   5846: 
                   5847: Returns: HTML div with $content
                   5848:          To be included in page header
                   5849: 
                   5850: =cut
                   5851: 
                   5852: sub head_subbox {
                   5853:     my ($content)=@_;
                   5854:     my $output =
1.993     raeburn  5855:         '<div class="LC_head_subbox">'
1.822     bisitz   5856:        .$content
                   5857:        .'</div>'
                   5858: }
                   5859: 
                   5860: ##############################################
                   5861: =pod
                   5862: 
                   5863: =item * &CSTR_pageheader()
                   5864: 
1.1026    raeburn  5865: Input: (optional) filename from which breadcrumb trail is built.
                   5866:        In most cases no input as needed, as $env{'request.filename'}
                   5867:        is appropriate for use in building the breadcrumb trail.
1.1075.2.161.  .6(raebu 5868:22):        frameset flag
                   5869:22):        If page header is being requested for use in a frameset, then
                   5870:22):        the second (option) argument -- frameset will be true, and
                   5871:22):        the target attribute set for links should be target="_parent".
1.822     bisitz   5872: 
                   5873: Returns: HTML div with CSTR path and recent box
1.1075.2.40  raeburn  5874:          To be included on Authoring Space pages
1.822     bisitz   5875: 
                   5876: =cut
                   5877: 
                   5878: sub CSTR_pageheader {
1.1075.2.161.  .6(raebu 5879:22):     my ($trailfile,$frameset) = @_;
1.1026    raeburn  5880:     if ($trailfile eq '') {
                   5881:         $trailfile = $env{'request.filename'};
                   5882:     }
                   5883: 
                   5884: # this is for resources; directories have customtitle, and crumbs
                   5885: # and select recent are created in lonpubdir.pm
                   5886: 
                   5887:     my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
1.1022    www      5888:     my ($udom,$uname,$thisdisfn)=
1.1075.2.29  raeburn  5889:         ($trailfile =~ m{^\Q$londocroot\E/priv/([^/]+)/([^/]+)(?:|/(.*))$});
1.1026    raeburn  5890:     my $formaction = "/priv/$udom/$uname/$thisdisfn";
                   5891:     $formaction =~ s{/+}{/}g;
1.822     bisitz   5892: 
                   5893:     my $parentpath = '';
                   5894:     my $lastitem = '';
                   5895:     if ($thisdisfn =~ m-(.+/)([^/]*)$-) {
                   5896:         $parentpath = $1;
                   5897:         $lastitem = $2;
                   5898:     } else {
                   5899:         $lastitem = $thisdisfn;
                   5900:     }
1.921     bisitz   5901: 
1.1075.2.161.  .6(raebu 5902:22):     my ($target,$crumbtarget) = (' target="_top"','_top');
                   5903:22):     if ($frameset) {
                   5904:22):         $target = ' target="_parent"';
                   5905:22):         $crumbtarget = '_parent';
          .17(raeb 5906:-23):     } elsif (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
                   5907:-23):         $target = '';
                   5908:-23):         $crumbtarget = '';
          .6(raebu 5909:22):     } elsif (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'})) {
                   5910:22):         $target = ' target="'.$env{'request.deeplink.target'}.'"';
                   5911:22):         $crumbtarget = $env{'request.deeplink.target'};
                   5912:22):     }
                   5913:22): 
1.921     bisitz   5914:     my $output =
1.822     bisitz   5915:          '<div>'
                   5916:         .&Apache::loncommon::help_open_menu('','',3,'Authoring') #FIXME: Broken? Where is it?
1.1075.2.40  raeburn  5917:         .'<b>'.&mt('Authoring Space:').'</b> '
1.1075.2.161.  .6(raebu 5918:22):         .'<form name="dirs" method="post" action="'.$formaction.'"'.$target.'>'
                   5919:22):         .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,$crumbtarget,'/priv/'.$udom,undef,undef);
1.921     bisitz   5920: 
                   5921:     if ($lastitem) {
                   5922:         $output .=
                   5923:              '<span class="LC_filename">'
                   5924:             .$lastitem
                   5925:             .'</span>';
                   5926:     }
                   5927:     $output .=
                   5928:          '<br />'
1.1075.2.161.  .6(raebu 5929:22):         #FIXME lonpubdir: &Apache::lonhtmlcommon::crumbs($uname.$thisdisfn.'/',$crumbtarget,'/priv','','+1',1)."</b></tt><br />"
1.822     bisitz   5930:         .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
                   5931:         .'</form>'
1.1075.2.161.  .6(raebu 5932:22):         .&Apache::lonmenu::constspaceform($frameset)
1.822     bisitz   5933:         .'</div>';
1.921     bisitz   5934: 
                   5935:     return $output;
1.822     bisitz   5936: }
                   5937: 
1.60      matthew  5938: ###############################################
                   5939: ###############################################
                   5940: 
                   5941: =pod
                   5942: 
1.112     bowersj2 5943: =back
                   5944: 
1.549     albertel 5945: =head1 HTML Helpers
1.112     bowersj2 5946: 
                   5947: =over 4
                   5948: 
                   5949: =item * &bodytag()
1.60      matthew  5950: 
                   5951: Returns a uniform header for LON-CAPA web pages.
                   5952: 
                   5953: Inputs: 
                   5954: 
1.112     bowersj2 5955: =over 4
                   5956: 
                   5957: =item * $title, A title to be displayed on the page.
                   5958: 
                   5959: =item * $function, the current role (can be undef).
                   5960: 
                   5961: =item * $addentries, extra parameters for the <body> tag.
                   5962: 
                   5963: =item * $bodyonly, if defined, only return the <body> tag.
                   5964: 
                   5965: =item * $domain, if defined, force a given domain.
                   5966: 
                   5967: =item * $forcereg, if page should register as content page (relevant for 
1.86      www      5968:             text interface only)
1.60      matthew  5969: 
1.814     bisitz   5970: =item * $no_nav_bar, if true, keep the 'what is this' info but remove the
                   5971:                      navigational links
1.317     albertel 5972: 
1.338     albertel 5973: =item * $bgcolor, used to override the bgcolor on a webpage to a specific value
                   5974: 
1.1075.2.12  raeburn  5975: =item * $no_inline_link, if true and in remote mode, don't show the
                   5976:          'Switch To Inline Menu' link
                   5977: 
1.460     albertel 5978: =item * $args, optional argument valid values are
                   5979:             no_auto_mt_title -> prevents &mt()ing the title arg
1.1075.2.133  raeburn  5980:             use_absolute     -> for external resource or syllabus, this will
                   5981:                                 contain https://<hostname> if server uses
                   5982:                                 https (as per hosts.tab), but request is for http
                   5983:             hostname         -> hostname, from $r->hostname().
1.460     albertel 5984: 
1.1075.2.15  raeburn  5985: =item * $advtoolsref, optional argument, ref to an array containing
                   5986:             inlineremote items to be added in "Functions" menu below
                   5987:             breadcrumbs.
                   5988: 
1.1075.2.161.  .1(raebu 5989:21): =item * $ltiscope, optional argument, will be one of: resource, map or
                   5990:21):             course, if LON-CAPA is in LTI Provider context. Value is
                   5991:21):             the scope of use, i.e., launch was for access to a single, a map
                   5992:21):             or the entire course.
                   5993:21): 
                   5994:21): =item * $ltiuri, optional argument, if LON-CAPA is in LTI Provider
                   5995:21):             context, this will contain the URL for the landing item in
                   5996:21):             the course, after launch from an LTI Consumer
                   5997:21): 
                   5998:21): =item * $ltimenu, optional argument, if LON-CAPA is in LTI Provider
                   5999:21):             context, this will contain a reference to hash of items
                   6000:21):             to be included in the page header and/or inline menu.
                   6001:21): 
          .8(raebu 6002:22): =item * $menucoll, optional argument, if specific menu collection is in
                   6003:22):             effect, either set as the default for the course, or set for
                   6004:22):             the deeplink paramater for $env{'request.deeplink.login'}
                   6005:22):             then $menucoll will be the number of that collection.
                   6006:22): 
                   6007:22): =item * $menuref, optional argument, reference to a hash, containing the
                   6008:22):             menu options included for the menu in effect, based on the
                   6009:22):             configuration for the numbered menu collection in use.
                   6010:22): 
                   6011:22): =item * $showncrumbsref, reference to a scalar. Calls to lonmenu::innerregister
                   6012:22):             within &bodytag() can result in calls to lonhtmlcommon::breadcrumbs(),
                   6013:22):             if so, $showncrumbsref is set there to 1, and will propagate back
                   6014:22):             via &bodytag() to &start_page(), to prevent lonhtmlcommon::breadcrumbs()
                   6015:22):             being called a second time.
                   6016:22): 
1.112     bowersj2 6017: =back
                   6018: 
1.60      matthew  6019: Returns: A uniform header for LON-CAPA web pages.  
                   6020: If $bodyonly is nonzero, a string containing a <body> tag will be returned.
                   6021: If $bodyonly is undef or zero, an html string containing a <body> tag and 
                   6022: other decorations will be returned.
                   6023: 
                   6024: =cut
                   6025: 
1.54      www      6026: sub bodytag {
1.831     bisitz   6027:     my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,
1.1075.2.161.  .1(raebu 6028:21):         $no_nav_bar,$bgcolor,$no_inline_link,$args,$advtoolsref,
          .8(raebu 6029:22):         $ltiscope,$ltiuri,$ltimenu,$menucoll,$menuref,$showncrumbsref)=@_;
1.339     albertel 6030: 
1.954     raeburn  6031:     my $public;
                   6032:     if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public'))
                   6033:         || ($env{'user.name'} eq '') && ($env{'user.domain'} eq '')) {
                   6034:         $public = 1;
                   6035:     }
1.460     albertel 6036:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
1.1075.2.52  raeburn  6037:     my $httphost = $args->{'use_absolute'};
1.1075.2.133  raeburn  6038:     my $hostname = $args->{'hostname'};
1.339     albertel 6039: 
1.183     matthew  6040:     $function = &get_users_function() if (!$function);
1.339     albertel 6041:     my $img =    &designparm($function.'.img',$domain);
                   6042:     my $font =   &designparm($function.'.font',$domain);
                   6043:     my $pgbg   = $bgcolor || &designparm($function.'.pgbg',$domain);
                   6044: 
1.803     bisitz   6045:     my %design = ( 'style'   => 'margin-top: 0',
1.535     albertel 6046: 		   'bgcolor' => $pgbg,
1.339     albertel 6047: 		   'text'    => $font,
                   6048:                    'alink'   => &designparm($function.'.alink',$domain),
                   6049: 		   'vlink'   => &designparm($function.'.vlink',$domain),
                   6050: 		   'link'    => &designparm($function.'.link',$domain),);
1.438     albertel 6051:     @design{keys(%$addentries)} = @$addentries{keys(%$addentries)}; 
1.339     albertel 6052: 
1.63      www      6053:  # role and realm
1.1075.2.68  raeburn  6054:     my ($role,$realm) = split(m{\./},$env{'request.role'},2);
                   6055:     if ($realm) {
                   6056:         $realm = '/'.$realm;
                   6057:     }
1.1075.2.159  raeburn  6058:     if ($role eq 'ca') {
1.479     albertel 6059:         my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
1.500     albertel 6060:         $realm = &plainname($rname,$rdom);
1.378     raeburn  6061:     } 
1.55      www      6062: # realm
1.1075.2.158  raeburn  6063:     my ($cid,$sec);
1.258     albertel 6064:     if ($env{'request.course.id'}) {
1.1075.2.158  raeburn  6065:         $cid = $env{'request.course.id'};
                   6066:         if ($env{'request.course.sec'}) {
                   6067:             $sec = $env{'request.course.sec'};
                   6068:         }
                   6069:     } elsif ($realm =~ m{^/($match_domain)/($match_courseid)(?:|/(\w+))$}) {
                   6070:         if (&Apache::lonnet::is_course($1,$2)) {
                   6071:             $cid = $1.'_'.$2;
                   6072:             $sec = $3;
                   6073:         }
                   6074:     }
                   6075:     if ($cid) {
1.378     raeburn  6076:         if ($env{'request.role'} !~ /^cr/) {
                   6077:             $role = &Apache::lonnet::plaintext($role,&course_type());
1.1075.2.115  raeburn  6078:         } elsif ($role =~ m{^cr/($match_domain)/\1-domainconfig/(\w+)$}) {
1.1075.2.121  raeburn  6079:             if ($env{'request.role.desc'}) {
                   6080:                 $role = $env{'request.role.desc'};
                   6081:             } else {
                   6082:                 $role = &mt('Helpdesk[_1]','&nbsp;'.$2);
                   6083:             }
1.1075.2.115  raeburn  6084:         } else {
                   6085:             $role = (split(/\//,$role,4))[-1];
1.378     raeburn  6086:         }
1.1075.2.158  raeburn  6087:         if ($sec) {
                   6088:             $role .= ('&nbsp;'x2).'-&nbsp;'.&mt('section:').'&nbsp;'.$sec;
1.898     raeburn  6089:         }   
1.1075.2.158  raeburn  6090: 	$realm = $env{'course.'.$cid.'.description'};
1.378     raeburn  6091:     } else {
                   6092:         $role = &Apache::lonnet::plaintext($role);
1.54      www      6093:     }
1.433     albertel 6094: 
1.359     albertel 6095:     if (!$realm) { $realm='&nbsp;'; }
1.330     albertel 6096: 
1.438     albertel 6097:     my $extra_body_attr = &make_attr_string($forcereg,\%design);
1.329     albertel 6098: 
1.101     www      6099: # construct main body tag
1.359     albertel 6100:     my $bodytag = "<body $extra_body_attr>".
1.1075.2.100  raeburn  6101: 	&Apache::lontexconvert::init_math_support();
1.252     albertel 6102: 
1.1075.2.38  raeburn  6103:     &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
                   6104: 
                   6105:     if (($bodyonly) || ($no_nav_bar) || ($env{'form.inhibitmenu'} eq 'yes')) {
1.60      matthew  6106:         return $bodytag;
1.1075.2.38  raeburn  6107:     }
1.359     albertel 6108: 
1.954     raeburn  6109:     if ($public) {
1.433     albertel 6110: 	undef($role);
                   6111:     }
1.1075.2.158  raeburn  6112: 
1.1075.2.161.  .1(raebu 6113:21):     my $showcrstitle = 1;
                   6114:21):     if (($cid) && ($env{'request.lti.login'})) {
                   6115:21):         if (ref($ltimenu) eq 'HASH') {
                   6116:21):             unless ($ltimenu->{'role'}) {
                   6117:21):                 undef($role);
                   6118:21):             }
                   6119:21):             unless ($ltimenu->{'coursetitle'}) {
                   6120:21):                 $realm='&nbsp;';
                   6121:21):                 $showcrstitle = 0;
                   6122:21):             }
                   6123:21):         }
                   6124:21):     } elsif (($cid) && ($menucoll)) {
                   6125:21):         if (ref($menuref) eq 'HASH') {
                   6126:21):             unless ($menuref->{'role'}) {
                   6127:21):                 undef($role);
                   6128:21):             }
                   6129:21):             unless ($menuref->{'crs'}) {
                   6130:21):                 $realm='&nbsp;';
                   6131:21):                 $showcrstitle = 0;
                   6132:21):             }
                   6133:21):         }
                   6134:21):     }
                   6135:21): 
1.762     bisitz   6136:     my $titleinfo = '<h1>'.$title.'</h1>';
1.359     albertel 6137:     #
                   6138:     # Extra info if you are the DC
                   6139:     my $dc_info = '';
1.1075.2.161.  .1(raebu 6140:21):     if (($env{'user.adv'}) && ($env{'request.course.id'}) && $showcrstitle &&
1.1075.2.158  raeburn  6141:         (exists($env{'user.role.dc./'.$env{'course.'.$cid.'.domain'}.'/'}))) {
1.917     raeburn  6142:         $dc_info = $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
1.380     www      6143:         $dc_info =~ s/\s+$//;
1.359     albertel 6144:     }
                   6145: 
1.1075.2.161.  .1(raebu 6146:21):     my $crstype;
                   6147:21):     if ($cid) {
                   6148:21):         $crstype = $env{'course.'.$cid.'.type'};
                   6149:21):     } elsif ($args->{'crstype'}) {
                   6150:21):         $crstype = $args->{'crstype'};
                   6151:21):     }
                   6152:21): 
1.1075.2.108  raeburn  6153:     $role = '<span class="LC_nobreak">('.$role.')</span>' if ($role && !$env{'browser.mobile'});
1.903     droeschl 6154: 
1.1075.2.13  raeburn  6155:     if ($env{'request.state'} eq 'construct') { $forcereg=1; }
                   6156: 
1.1075.2.38  raeburn  6157: 
                   6158: 
1.1075.2.21  raeburn  6159:     my $funclist;
                   6160:     if (($env{'environment.remote'} eq 'on') && ($env{'request.state'} ne 'construct')) {
1.1075.2.52  raeburn  6161:         $bodytag .= Apache::lonhtmlcommon::scripttag(Apache::lonmenu::utilityfunctions($httphost), 'start')."\n".
1.1075.2.21  raeburn  6162:                     Apache::lonmenu::serverform();
                   6163:         my $forbodytag;
                   6164:         &Apache::lonmenu::prepare_functions($env{'request.noversionuri'},
                   6165:                                             $forcereg,$args->{'group'},
                   6166:                                             $args->{'bread_crumbs'},
1.1075.2.133  raeburn  6167:                                             $advtoolsref,'','',\$forbodytag);
1.1075.2.21  raeburn  6168:         unless (ref($args->{'bread_crumbs'}) eq 'ARRAY') {
                   6169:             $funclist = $forbodytag;
                   6170:         }
                   6171:     } else {
1.903     droeschl 6172: 
                   6173:         #    if ($env{'request.state'} eq 'construct') {
                   6174:         #        $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls
                   6175:         #    }
                   6176: 
1.1075.2.38  raeburn  6177:         $bodytag .= Apache::lonhtmlcommon::scripttag(
1.1075.2.52  raeburn  6178:             Apache::lonmenu::utilityfunctions($httphost), 'start');
1.359     albertel 6179: 
1.1075.2.161.  .1(raebu 6180:21):         unless ($args->{'no_primary_menu'}) {
          .4(raebu 6181:22):             my ($left,$right) = Apache::lonmenu::primary_menu($crstype,$ltimenu,$menucoll,$menuref,
          .6(raebu 6182:22):                                                               $args->{'links_disabled'},
                   6183:22):                                                               $args->{'links_target'});
          .1(raebu 6184:21):             if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
                   6185:21):                 if ($dc_info) {
                   6186:21):                     $dc_info = qq|<span class="LC_cusr_subheading">$dc_info</span>|;
                   6187:21):                 }
                   6188:21):                 $bodytag .= qq|<div id="LC_nav_bar">$left $role<br />
                   6189:21):                                <em>$realm</em> $dc_info</div>|;
                   6190:21):                 return $bodytag;
1.1075.2.1  raeburn  6191:             }
1.894     droeschl 6192: 
1.1075.2.161.  .1(raebu 6193:21):             unless ($env{'request.symb'} =~ m/\.page___\d+___/) {
                   6194:21):                 $bodytag .= qq|<div id="LC_nav_bar">$left $role</div>|;
                   6195:21):             }
1.916     droeschl 6196: 
1.1075.2.161.  .1(raebu 6197:21):             $bodytag .= $right;
1.852     droeschl 6198: 
1.1075.2.161.  .1(raebu 6199:21):             if ($dc_info) {
                   6200:21):                 $dc_info = &dc_courseid_toggle($dc_info);
                   6201:21):             }
                   6202:21):             $bodytag .= qq|<div id="LC_realm">$realm $dc_info</div>|;
1.917     raeburn  6203:         }
1.916     droeschl 6204: 
1.1075.2.61  raeburn  6205:         #if directed to not display the secondary menu, don't.
                   6206:         if ($args->{'no_secondary_menu'}) {
                   6207:             return $bodytag;
                   6208:         }
1.903     droeschl 6209:         #don't show menus for public users
1.954     raeburn  6210:         if (!$public){
1.1075.2.161.  .1(raebu 6211:21):             unless ($args->{'no_inline_menu'}) {
                   6212:21):                 $bodytag .= Apache::lonmenu::secondary_menu($httphost,$ltiscope,$ltimenu,
                   6213:21):                                                             $args->{'no_primary_menu'},
                   6214:21):                                                             $menucoll,$menuref,
          .6(raebu 6215:22):                                                             $args->{'links_disabled'},
                   6216:22):                                                             $args->{'links_target'});
          .1(raebu 6217:21):             }
1.903     droeschl 6218:             $bodytag .= Apache::lonmenu::serverform();
1.920     raeburn  6219:             $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
                   6220:             if ($env{'request.state'} eq 'construct') {
1.962     droeschl 6221:                 $bodytag .= &Apache::lonmenu::innerregister($forcereg,
1.1075.2.161.  .8(raebu 6222:22):                                 $args->{'bread_crumbs'},'','',$hostname,
                   6223:22):                                 $ltiscope,$ltiuri,$showncrumbsref);
1.1075.2.116  raeburn  6224:             } elsif ($forcereg) {
1.1075.2.22  raeburn  6225:                 $bodytag .= &Apache::lonmenu::innerregister($forcereg,undef,
1.1075.2.161.  .8(raebu 6226:22):                                 $args->{'group'},$args->{'hide_buttons'},
                   6227:22):                                 $hostname,$ltiscope,$ltiuri,$showncrumbsref);
1.1075.2.15  raeburn  6228:             } else {
1.1075.2.21  raeburn  6229:                 my $forbodytag;
                   6230:                 &Apache::lonmenu::prepare_functions($env{'request.noversionuri'},
                   6231:                                                     $forcereg,$args->{'group'},
                   6232:                                                     $args->{'bread_crumbs'},
1.1075.2.133  raeburn  6233:                                                     $advtoolsref,'',$hostname,
                   6234:                                                     \$forbodytag);
1.1075.2.21  raeburn  6235:                 unless (ref($args->{'bread_crumbs'}) eq 'ARRAY') {
                   6236:                     $bodytag .= $forbodytag;
                   6237:                 }
1.920     raeburn  6238:             }
1.903     droeschl 6239:         }else{
                   6240:             # this is to seperate menu from content when there's no secondary
                   6241:             # menu. Especially needed for public accessible ressources.
                   6242:             $bodytag .= '<hr style="clear:both" />';
                   6243:             $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); 
1.235     raeburn  6244:         }
1.903     droeschl 6245: 
1.235     raeburn  6246:         return $bodytag;
1.1075.2.12  raeburn  6247:     }
                   6248: 
                   6249: #
                   6250: # Top frame rendering, Remote is up
                   6251: #
                   6252: 
                   6253:     my $imgsrc = $img;
                   6254:     if ($img =~ /^\/adm/) {
                   6255:         $imgsrc = &lonhttpdurl($img);
                   6256:     }
                   6257:     my $upperleft='<img src="'.$imgsrc.'" alt="'.$function.'" />';
                   6258: 
1.1075.2.60  raeburn  6259:     my $help=($no_inline_link?''
                   6260:               :&Apache::loncommon::top_nav_help('Help'));
                   6261: 
1.1075.2.12  raeburn  6262:     # Explicit link to get inline menu
                   6263:     my $menu= ($no_inline_link?''
                   6264:                :'<a href="/adm/remote?action=collapse" target="_top">'.&mt('Switch to Inline Menu Mode').'</a>');
                   6265: 
                   6266:     if ($dc_info) {
                   6267:         $dc_info = qq|<span class="LC_cusr_subheading">($dc_info)</span>|;
                   6268:     }
                   6269: 
1.1075.2.38  raeburn  6270:     my $name = &plainname($env{'user.name'},$env{'user.domain'});
                   6271:     unless ($public) {
                   6272:         $name = &aboutmewrapper($name,$env{'user.name'},$env{'user.domain'},
                   6273:                                 undef,'LC_menubuttons_link');
                   6274:     }
                   6275: 
1.1075.2.12  raeburn  6276:     unless ($env{'form.inhibitmenu'}) {
                   6277:         $bodytag .= qq|<div id="LC_nav_bar">$name $role</div>
1.1075.2.38  raeburn  6278:                        <ol class="LC_primary_menu LC_floatright LC_right">
1.1075.2.60  raeburn  6279:                        <li>$help</li>
1.1075.2.12  raeburn  6280:                        <li>$menu</li>
                   6281:                        </ol><div id="LC_realm"> $realm $dc_info</div>|;
                   6282:     }
1.1075.2.13  raeburn  6283:     if ($env{'request.state'} eq 'construct') {
                   6284:         if (!$public){
                   6285:             if ($env{'request.state'} eq 'construct') {
                   6286:                 $funclist = &Apache::lonhtmlcommon::scripttag(
1.1075.2.52  raeburn  6287:                                 &Apache::lonmenu::utilityfunctions($httphost), 'start').
1.1075.2.13  raeburn  6288:                             &Apache::lonhtmlcommon::scripttag('','end').
                   6289:                             &Apache::lonmenu::innerregister($forcereg,
                   6290:                                                             $args->{'bread_crumbs'});
                   6291:             }
                   6292:         }
                   6293:     }
1.1075.2.21  raeburn  6294:     return $bodytag."\n".$funclist;
1.182     matthew  6295: }
                   6296: 
1.917     raeburn  6297: sub dc_courseid_toggle {
                   6298:     my ($dc_info) = @_;
1.980     raeburn  6299:     return ' <span id="dccidtext" class="LC_cusr_subheading LC_nobreak">'.
1.1069    raeburn  6300:            '<a href="javascript:showCourseID();" class="LC_menubuttons_link">'.
1.917     raeburn  6301:            &mt('(More ...)').'</a></span>'.
                   6302:            '<div id="dccid" class="LC_dccid">'.$dc_info.'</div>';
                   6303: }
                   6304: 
1.330     albertel 6305: sub make_attr_string {
                   6306:     my ($register,$attr_ref) = @_;
                   6307: 
                   6308:     if ($attr_ref && !ref($attr_ref)) {
                   6309: 	die("addentries Must be a hash ref ".
                   6310: 	    join(':',caller(1))." ".
                   6311: 	    join(':',caller(0))." ");
                   6312:     }
                   6313: 
                   6314:     if ($register) {
1.339     albertel 6315: 	my ($on_load,$on_unload);
                   6316: 	foreach my $key (keys(%{$attr_ref})) {
                   6317: 	    if      (lc($key) eq 'onload') {
                   6318: 		$on_load.=$attr_ref->{$key}.';';
                   6319: 		delete($attr_ref->{$key});
                   6320: 
                   6321: 	    } elsif (lc($key) eq 'onunload') {
                   6322: 		$on_unload.=$attr_ref->{$key}.';';
                   6323: 		delete($attr_ref->{$key});
                   6324: 	    }
                   6325: 	}
1.1075.2.12  raeburn  6326:         if ($env{'environment.remote'} eq 'on') {
                   6327:             $attr_ref->{'onload'}  =
                   6328:                 &Apache::lonmenu::loadevents().  $on_load;
                   6329:             $attr_ref->{'onunload'}=
                   6330:                 &Apache::lonmenu::unloadevents().$on_unload;
                   6331:         } else {  
                   6332: 	    $attr_ref->{'onload'}  = $on_load;
                   6333: 	    $attr_ref->{'onunload'}= $on_unload;
                   6334:         }
1.330     albertel 6335:     }
1.339     albertel 6336: 
1.330     albertel 6337:     my $attr_string;
1.1075.2.56  raeburn  6338:     foreach my $attr (sort(keys(%$attr_ref))) {
1.330     albertel 6339: 	$attr_string .= " $attr=\"".$attr_ref->{$attr}.'" ';
                   6340:     }
                   6341:     return $attr_string;
                   6342: }
                   6343: 
                   6344: 
1.182     matthew  6345: ###############################################
1.251     albertel 6346: ###############################################
                   6347: 
                   6348: =pod
                   6349: 
                   6350: =item * &endbodytag()
                   6351: 
                   6352: Returns a uniform footer for LON-CAPA web pages.
                   6353: 
1.635     raeburn  6354: Inputs: 1 - optional reference to an args hash
                   6355: If in the hash, key for noredirectlink has a value which evaluates to true,
                   6356: a 'Continue' link is not displayed if the page contains an
                   6357: internal redirect in the <head></head> section,
                   6358: i.e., $env{'internal.head.redirect'} exists   
1.251     albertel 6359: 
                   6360: =cut
                   6361: 
                   6362: sub endbodytag {
1.635     raeburn  6363:     my ($args) = @_;
1.1075.2.6  raeburn  6364:     my $endbodytag;
                   6365:     unless ((ref($args) eq 'HASH') && ($args->{'notbody'})) {
                   6366:         $endbodytag='</body>';
                   6367:     }
1.315     albertel 6368:     if ( exists( $env{'internal.head.redirect'} ) ) {
1.635     raeburn  6369:         if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
1.1075.2.161.  .9(raebu 6370:22):             my ($endbodyjs,$idattr);
                   6371:22):             if ($env{'internal.head.to_opener'}) {
                   6372:22):                 my $linkid = 'LC_continue_link';
                   6373:22):                 $idattr = ' id="'.$linkid.'"';
                   6374:22):                 my $redirect_for_js = &js_escape($env{'internal.head.redirect'});
                   6375:22):                 $endbodyjs=<<ENDJS;
                   6376:22): <script type="text/javascript">
                   6377:22): // <![CDATA[
                   6378:22): function ebFunction(evt) {
                   6379:22):     evt.preventDefault();
                   6380:22):     var dest = '$redirect_for_js';
                   6381:22):     if (window.opener != null && !window.opener.closed) {
                   6382:22):         window.opener.location.href=dest;
                   6383:22):         window.close();
                   6384:22):     } else {
                   6385:22):         window.location.href=dest;
                   6386:22):     }
                   6387:22):     return false;
                   6388:22): }
                   6389:22): 
                   6390:22): \$(document).ready(function () {
                   6391:22):   if (document.getElementById('$linkid')) {
                   6392:22):     var clickelem = document.getElementById('$linkid');
                   6393:22):     clickelem.addEventListener('click',ebFunction,false);
                   6394:22):   }
                   6395:22): });
                   6396:22): // ]]>
                   6397:22): </script>
                   6398:22): ENDJS
                   6399:22):             }
1.635     raeburn  6400: 	    $endbodytag=
1.1075.2.161.  .9(raebu 6401:22): 	        "$endbodyjs<br /><a href=\"$env{'internal.head.redirect'}\"$idattr>".
1.635     raeburn  6402: 	        &mt('Continue').'</a>'.
                   6403: 	        $endbodytag;
                   6404:         }
1.315     albertel 6405:     }
1.1075.2.161.  .19(raeb 6406:-23):     if ((ref($args) eq 'HASH') && ($args->{'dashjs'})) {
                   6407:-23):         $endbodytag = &Apache::lonhtmlcommon::dash_to_minus_js().$endbodytag;
                   6408:-23):     }
1.251     albertel 6409:     return $endbodytag;
                   6410: }
                   6411: 
1.352     albertel 6412: =pod
                   6413: 
                   6414: =item * &standard_css()
                   6415: 
                   6416: Returns a style sheet
                   6417: 
                   6418: Inputs: (all optional)
                   6419:             domain         -> force to color decorate a page for a specific
                   6420:                                domain
                   6421:             function       -> force usage of a specific rolish color scheme
                   6422:             bgcolor        -> override the default page bgcolor
                   6423: 
                   6424: =cut
                   6425: 
1.343     albertel 6426: sub standard_css {
1.345     albertel 6427:     my ($function,$domain,$bgcolor) = @_;
1.352     albertel 6428:     $function  = &get_users_function() if (!$function);
                   6429:     my $img    = &designparm($function.'.img',   $domain);
                   6430:     my $tabbg  = &designparm($function.'.tabbg', $domain);
                   6431:     my $font   = &designparm($function.'.font',  $domain);
1.801     tempelho 6432:     my $fontmenu = &designparm($function.'.fontmenu', $domain);
1.791     tempelho 6433: #second colour for later usage
1.345     albertel 6434:     my $sidebg = &designparm($function.'.sidebg',$domain);
1.382     albertel 6435:     my $pgbg_or_bgcolor =
                   6436: 	         $bgcolor ||
1.352     albertel 6437: 	         &designparm($function.'.pgbg',  $domain);
1.382     albertel 6438:     my $pgbg   = &designparm($function.'.pgbg',  $domain);
1.352     albertel 6439:     my $alink  = &designparm($function.'.alink', $domain);
                   6440:     my $vlink  = &designparm($function.'.vlink', $domain);
                   6441:     my $link   = &designparm($function.'.link',  $domain);
                   6442: 
1.602     albertel 6443:     my $sans                 = 'Verdana,Arial,Helvetica,sans-serif';
1.395     albertel 6444:     my $mono                 = 'monospace';
1.850     bisitz   6445:     my $data_table_head      = $sidebg;
                   6446:     my $data_table_light     = '#FAFAFA';
1.1060    bisitz   6447:     my $data_table_dark      = '#E0E0E0';
1.470     banghart 6448:     my $data_table_darker    = '#CCCCCC';
1.349     albertel 6449:     my $data_table_highlight = '#FFFF00';
1.352     albertel 6450:     my $mail_new             = '#FFBB77';
                   6451:     my $mail_new_hover       = '#DD9955';
                   6452:     my $mail_read            = '#BBBB77';
                   6453:     my $mail_read_hover      = '#999944';
                   6454:     my $mail_replied         = '#AAAA88';
                   6455:     my $mail_replied_hover   = '#888855';
                   6456:     my $mail_other           = '#99BBBB';
                   6457:     my $mail_other_hover     = '#669999';
1.391     albertel 6458:     my $table_header         = '#DDDDDD';
1.489     raeburn  6459:     my $feedback_link_bg     = '#BBBBBB';
1.911     bisitz   6460:     my $lg_border_color      = '#C8C8C8';
1.952     onken    6461:     my $button_hover         = '#BF2317';
1.392     albertel 6462: 
1.608     albertel 6463:     my $border = ($env{'browser.type'} eq 'explorer' ||
1.911     bisitz   6464:       $env{'browser.type'} eq 'safari'     ) ? '0 2px 0 2px'
                   6465:                                              : '0 3px 0 4px';
1.448     albertel 6466: 
1.523     albertel 6467: 
1.343     albertel 6468:     return <<END;
1.947     droeschl 6469: 
                   6470: /* needed for iframe to allow 100% height in FF */
                   6471: body, html { 
                   6472:     margin: 0;
                   6473:     padding: 0 0.5%;
                   6474:     height: 99%; /* to avoid scrollbars */
                   6475: }
                   6476: 
1.795     www      6477: body {
1.911     bisitz   6478:   font-family: $sans;
                   6479:   line-height:130%;
                   6480:   font-size:0.83em;
                   6481:   color:$font;
1.795     www      6482: }
                   6483: 
1.959     onken    6484: a:focus,
                   6485: a:focus img {
1.795     www      6486:   color: red;
                   6487: }
1.698     harmsja  6488: 
1.911     bisitz   6489: form, .inline {
                   6490:   display: inline;
1.795     www      6491: }
1.721     harmsja  6492: 
1.795     www      6493: .LC_right {
1.911     bisitz   6494:   text-align:right;
1.795     www      6495: }
                   6496: 
                   6497: .LC_middle {
1.911     bisitz   6498:   vertical-align:middle;
1.795     www      6499: }
1.721     harmsja  6500: 
1.1075.2.38  raeburn  6501: .LC_floatleft {
                   6502:   float: left;
                   6503: }
                   6504: 
                   6505: .LC_floatright {
                   6506:   float: right;
                   6507: }
                   6508: 
1.911     bisitz   6509: .LC_400Box {
                   6510:   width:400px;
                   6511: }
1.721     harmsja  6512: 
1.947     droeschl 6513: .LC_iframecontainer {
                   6514:     width: 98%;
                   6515:     margin: 0;
                   6516:     position: fixed;
                   6517:     top: 8.5em;
                   6518:     bottom: 0;
                   6519: }
                   6520: 
                   6521: .LC_iframecontainer iframe{
                   6522:     border: none;
                   6523:     width: 100%;
                   6524:     height: 100%;
                   6525: }
                   6526: 
1.778     bisitz   6527: .LC_filename {
                   6528:   font-family: $mono;
                   6529:   white-space:pre;
1.921     bisitz   6530:   font-size: 120%;
1.778     bisitz   6531: }
                   6532: 
                   6533: .LC_fileicon {
                   6534:   border: none;
                   6535:   height: 1.3em;
                   6536:   vertical-align: text-bottom;
                   6537:   margin-right: 0.3em;
                   6538:   text-decoration:none;
                   6539: }
                   6540: 
1.1008    www      6541: .LC_setting {
                   6542:   text-decoration:underline;
                   6543: }
                   6544: 
1.350     albertel 6545: .LC_error {
                   6546:   color: red;
                   6547: }
1.795     www      6548: 
1.1075.2.15  raeburn  6549: .LC_warning {
                   6550:   color: darkorange;
                   6551: }
                   6552: 
1.457     albertel 6553: .LC_diff_removed {
1.733     bisitz   6554:   color: red;
1.394     albertel 6555: }
1.532     albertel 6556: 
                   6557: .LC_info,
1.457     albertel 6558: .LC_success,
                   6559: .LC_diff_added {
1.350     albertel 6560:   color: green;
                   6561: }
1.795     www      6562: 
1.802     bisitz   6563: div.LC_confirm_box {
                   6564:   background-color: #FAFAFA;
                   6565:   border: 1px solid $lg_border_color;
                   6566:   margin-right: 0;
                   6567:   padding: 5px;
                   6568: }
                   6569: 
                   6570: div.LC_confirm_box .LC_error img,
                   6571: div.LC_confirm_box .LC_success img {
                   6572:   vertical-align: middle;
                   6573: }
                   6574: 
1.1075.2.108  raeburn  6575: .LC_maxwidth {
                   6576:   max-width: 100%;
                   6577:   height: auto;
                   6578: }
                   6579: 
                   6580: .LC_textsize_mobile {
                   6581:   \@media only screen and (max-device-width: 480px) {
                   6582:       -webkit-text-size-adjust:100%; -moz-text-size-adjust:100%; -ms-text-size-adjust:100%;
                   6583:   }
                   6584: }
                   6585: 
1.440     albertel 6586: .LC_icon {
1.771     droeschl 6587:   border: none;
1.790     droeschl 6588:   vertical-align: middle;
1.771     droeschl 6589: }
                   6590: 
1.543     albertel 6591: .LC_docs_spacer {
                   6592:   width: 25px;
                   6593:   height: 1px;
1.771     droeschl 6594:   border: none;
1.543     albertel 6595: }
1.346     albertel 6596: 
1.532     albertel 6597: .LC_internal_info {
1.735     bisitz   6598:   color: #999999;
1.532     albertel 6599: }
                   6600: 
1.794     www      6601: .LC_discussion {
1.1050    www      6602:   background: $data_table_dark;
1.911     bisitz   6603:   border: 1px solid black;
                   6604:   margin: 2px;
1.794     www      6605: }
                   6606: 
                   6607: .LC_disc_action_left {
1.1050    www      6608:   background: $sidebg;
1.911     bisitz   6609:   text-align: left;
1.1050    www      6610:   padding: 4px;
                   6611:   margin: 2px;
1.794     www      6612: }
                   6613: 
                   6614: .LC_disc_action_right {
1.1050    www      6615:   background: $sidebg;
1.911     bisitz   6616:   text-align: right;
1.1050    www      6617:   padding: 4px;
                   6618:   margin: 2px;
1.794     www      6619: }
                   6620: 
                   6621: .LC_disc_new_item {
1.911     bisitz   6622:   background: white;
                   6623:   border: 2px solid red;
1.1050    www      6624:   margin: 4px;
                   6625:   padding: 4px;
1.794     www      6626: }
                   6627: 
                   6628: .LC_disc_old_item {
1.911     bisitz   6629:   background: white;
1.1050    www      6630:   margin: 4px;
                   6631:   padding: 4px;
1.794     www      6632: }
                   6633: 
1.458     albertel 6634: table.LC_pastsubmission {
                   6635:   border: 1px solid black;
                   6636:   margin: 2px;
                   6637: }
                   6638: 
1.924     bisitz   6639: table#LC_menubuttons {
1.345     albertel 6640:   width: 100%;
                   6641:   background: $pgbg;
1.392     albertel 6642:   border: 2px;
1.402     albertel 6643:   border-collapse: separate;
1.803     bisitz   6644:   padding: 0;
1.345     albertel 6645: }
1.392     albertel 6646: 
1.801     tempelho 6647: table#LC_title_bar a {
                   6648:   color: $fontmenu;
                   6649: }
1.836     bisitz   6650: 
1.807     droeschl 6651: table#LC_title_bar {
1.819     tempelho 6652:   clear: both;
1.836     bisitz   6653:   display: none;
1.807     droeschl 6654: }
                   6655: 
1.795     www      6656: table#LC_title_bar,
1.933     droeschl 6657: table.LC_breadcrumbs, /* obsolete? */
1.393     albertel 6658: table#LC_title_bar.LC_with_remote {
1.359     albertel 6659:   width: 100%;
1.392     albertel 6660:   border-color: $pgbg;
                   6661:   border-style: solid;
                   6662:   border-width: $border;
1.379     albertel 6663:   background: $pgbg;
1.801     tempelho 6664:   color: $fontmenu;
1.392     albertel 6665:   border-collapse: collapse;
1.803     bisitz   6666:   padding: 0;
1.819     tempelho 6667:   margin: 0;
1.359     albertel 6668: }
1.795     www      6669: 
1.933     droeschl 6670: ul.LC_breadcrumb_tools_outerlist {
1.913     droeschl 6671:     margin: 0;
                   6672:     padding: 0;
1.933     droeschl 6673:     position: relative;
                   6674:     list-style: none;
1.913     droeschl 6675: }
1.933     droeschl 6676: ul.LC_breadcrumb_tools_outerlist li {
1.913     droeschl 6677:     display: inline;
                   6678: }
1.933     droeschl 6679: 
                   6680: .LC_breadcrumb_tools_navigation {
1.913     droeschl 6681:     padding: 0;
1.933     droeschl 6682:     margin: 0;
                   6683:     float: left;
1.913     droeschl 6684: }
1.933     droeschl 6685: .LC_breadcrumb_tools_tools {
                   6686:     padding: 0;
                   6687:     margin: 0;
1.913     droeschl 6688:     float: right;
                   6689: }
                   6690: 
1.359     albertel 6691: table#LC_title_bar td {
                   6692:   background: $tabbg;
                   6693: }
1.795     www      6694: 
1.911     bisitz   6695: table#LC_menubuttons img {
1.803     bisitz   6696:   border: none;
1.346     albertel 6697: }
1.795     www      6698: 
1.842     droeschl 6699: .LC_breadcrumbs_component {
1.911     bisitz   6700:   float: right;
                   6701:   margin: 0 1em;
1.357     albertel 6702: }
1.842     droeschl 6703: .LC_breadcrumbs_component img {
1.911     bisitz   6704:   vertical-align: middle;
1.777     tempelho 6705: }
1.795     www      6706: 
1.1075.2.108  raeburn  6707: .LC_breadcrumbs_hoverable {
                   6708:   background: $sidebg;
                   6709: }
                   6710: 
1.383     albertel 6711: td.LC_table_cell_checkbox {
                   6712:   text-align: center;
                   6713: }
1.795     www      6714: 
                   6715: .LC_fontsize_small {
1.911     bisitz   6716:   font-size: 70%;
1.705     tempelho 6717: }
                   6718: 
1.844     bisitz   6719: #LC_breadcrumbs {
1.911     bisitz   6720:   clear:both;
                   6721:   background: $sidebg;
                   6722:   border-bottom: 1px solid $lg_border_color;
                   6723:   line-height: 2.5em;
1.933     droeschl 6724:   overflow: hidden;
1.911     bisitz   6725:   margin: 0;
                   6726:   padding: 0;
1.995     raeburn  6727:   text-align: left;
1.819     tempelho 6728: }
1.862     bisitz   6729: 
1.1075.2.16  raeburn  6730: .LC_head_subbox, .LC_actionbox {
1.911     bisitz   6731:   clear:both;
                   6732:   background: #F8F8F8; /* $sidebg; */
1.915     droeschl 6733:   border: 1px solid $sidebg;
1.1075.2.16  raeburn  6734:   margin: 0 0 10px 0;
1.966     bisitz   6735:   padding: 3px;
1.995     raeburn  6736:   text-align: left;
1.822     bisitz   6737: }
                   6738: 
1.795     www      6739: .LC_fontsize_medium {
1.911     bisitz   6740:   font-size: 85%;
1.705     tempelho 6741: }
                   6742: 
1.795     www      6743: .LC_fontsize_large {
1.911     bisitz   6744:   font-size: 120%;
1.705     tempelho 6745: }
                   6746: 
1.346     albertel 6747: .LC_menubuttons_inline_text {
                   6748:   color: $font;
1.698     harmsja  6749:   font-size: 90%;
1.701     harmsja  6750:   padding-left:3px;
1.346     albertel 6751: }
                   6752: 
1.934     droeschl 6753: .LC_menubuttons_inline_text img{
                   6754:   vertical-align: middle;
                   6755: }
                   6756: 
1.1051    www      6757: li.LC_menubuttons_inline_text img {
1.951     onken    6758:   cursor:pointer;
1.1002    droeschl 6759:   text-decoration: none;
1.951     onken    6760: }
                   6761: 
1.526     www      6762: .LC_menubuttons_link {
                   6763:   text-decoration: none;
                   6764: }
1.795     www      6765: 
1.522     albertel 6766: .LC_menubuttons_category {
1.521     www      6767:   color: $font;
1.526     www      6768:   background: $pgbg;
1.521     www      6769:   font-size: larger;
                   6770:   font-weight: bold;
                   6771: }
                   6772: 
1.346     albertel 6773: td.LC_menubuttons_text {
1.911     bisitz   6774:   color: $font;
1.346     albertel 6775: }
1.706     harmsja  6776: 
1.346     albertel 6777: .LC_current_location {
                   6778:   background: $tabbg;
                   6779: }
1.795     www      6780: 
1.1075.2.134  raeburn  6781: td.LC_zero_height {
                   6782:   line-height: 0;
                   6783:   cellpadding: 0;
                   6784: }
                   6785: 
1.938     bisitz   6786: table.LC_data_table {
1.347     albertel 6787:   border: 1px solid #000000;
1.402     albertel 6788:   border-collapse: separate;
1.426     albertel 6789:   border-spacing: 1px;
1.610     albertel 6790:   background: $pgbg;
1.347     albertel 6791: }
1.795     www      6792: 
1.422     albertel 6793: .LC_data_table_dense {
                   6794:   font-size: small;
                   6795: }
1.795     www      6796: 
1.507     raeburn  6797: table.LC_nested_outer {
                   6798:   border: 1px solid #000000;
1.589     raeburn  6799:   border-collapse: collapse;
1.803     bisitz   6800:   border-spacing: 0;
1.507     raeburn  6801:   width: 100%;
                   6802: }
1.795     www      6803: 
1.879     raeburn  6804: table.LC_innerpickbox,
1.507     raeburn  6805: table.LC_nested {
1.803     bisitz   6806:   border: none;
1.589     raeburn  6807:   border-collapse: collapse;
1.803     bisitz   6808:   border-spacing: 0;
1.507     raeburn  6809:   width: 100%;
                   6810: }
1.795     www      6811: 
1.911     bisitz   6812: table.LC_data_table tr th,
                   6813: table.LC_calendar tr th,
1.879     raeburn  6814: table.LC_prior_tries tr th,
                   6815: table.LC_innerpickbox tr th {
1.349     albertel 6816:   font-weight: bold;
                   6817:   background-color: $data_table_head;
1.801     tempelho 6818:   color:$fontmenu;
1.701     harmsja  6819:   font-size:90%;
1.347     albertel 6820: }
1.795     www      6821: 
1.879     raeburn  6822: table.LC_innerpickbox tr th,
                   6823: table.LC_innerpickbox tr td {
                   6824:   vertical-align: top;
                   6825: }
                   6826: 
1.711     raeburn  6827: table.LC_data_table tr.LC_info_row > td {
1.735     bisitz   6828:   background-color: #CCCCCC;
1.711     raeburn  6829:   font-weight: bold;
                   6830:   text-align: left;
                   6831: }
1.795     www      6832: 
1.912     bisitz   6833: table.LC_data_table tr.LC_odd_row > td {
                   6834:   background-color: $data_table_light;
                   6835:   padding: 2px;
                   6836:   vertical-align: top;
                   6837: }
                   6838: 
1.809     bisitz   6839: table.LC_pick_box tr > td.LC_odd_row {
1.349     albertel 6840:   background-color: $data_table_light;
1.912     bisitz   6841:   vertical-align: top;
                   6842: }
                   6843: 
                   6844: table.LC_data_table tr.LC_even_row > td {
                   6845:   background-color: $data_table_dark;
1.425     albertel 6846:   padding: 2px;
1.900     bisitz   6847:   vertical-align: top;
1.347     albertel 6848: }
1.795     www      6849: 
1.809     bisitz   6850: table.LC_pick_box tr > td.LC_even_row {
1.349     albertel 6851:   background-color: $data_table_dark;
1.900     bisitz   6852:   vertical-align: top;
1.347     albertel 6853: }
1.795     www      6854: 
1.425     albertel 6855: table.LC_data_table tr.LC_data_table_highlight td {
                   6856:   background-color: $data_table_darker;
                   6857: }
1.795     www      6858: 
1.639     raeburn  6859: table.LC_data_table tr td.LC_leftcol_header {
                   6860:   background-color: $data_table_head;
                   6861:   font-weight: bold;
                   6862: }
1.795     www      6863: 
1.451     albertel 6864: table.LC_data_table tr.LC_empty_row td,
1.507     raeburn  6865: table.LC_nested tr.LC_empty_row td {
1.421     albertel 6866:   font-weight: bold;
                   6867:   font-style: italic;
                   6868:   text-align: center;
                   6869:   padding: 8px;
1.347     albertel 6870: }
1.795     www      6871: 
1.1075.2.30  raeburn  6872: table.LC_data_table tr.LC_empty_row td,
                   6873: table.LC_data_table tr.LC_footer_row td {
1.940     bisitz   6874:   background-color: $sidebg;
                   6875: }
                   6876: 
                   6877: table.LC_nested tr.LC_empty_row td {
                   6878:   background-color: #FFFFFF;
                   6879: }
                   6880: 
1.890     droeschl 6881: table.LC_caption {
                   6882: }
                   6883: 
1.507     raeburn  6884: table.LC_nested tr.LC_empty_row td {
1.465     albertel 6885:   padding: 4ex
                   6886: }
1.795     www      6887: 
1.507     raeburn  6888: table.LC_nested_outer tr th {
                   6889:   font-weight: bold;
1.801     tempelho 6890:   color:$fontmenu;
1.507     raeburn  6891:   background-color: $data_table_head;
1.701     harmsja  6892:   font-size: small;
1.507     raeburn  6893:   border-bottom: 1px solid #000000;
                   6894: }
1.795     www      6895: 
1.507     raeburn  6896: table.LC_nested_outer tr td.LC_subheader {
                   6897:   background-color: $data_table_head;
                   6898:   font-weight: bold;
                   6899:   font-size: small;
                   6900:   border-bottom: 1px solid #000000;
                   6901:   text-align: right;
1.451     albertel 6902: }
1.795     www      6903: 
1.507     raeburn  6904: table.LC_nested tr.LC_info_row td {
1.735     bisitz   6905:   background-color: #CCCCCC;
1.451     albertel 6906:   font-weight: bold;
                   6907:   font-size: small;
1.507     raeburn  6908:   text-align: center;
                   6909: }
1.795     www      6910: 
1.589     raeburn  6911: table.LC_nested tr.LC_info_row td.LC_left_item,
                   6912: table.LC_nested_outer tr th.LC_left_item {
1.507     raeburn  6913:   text-align: left;
1.451     albertel 6914: }
1.795     www      6915: 
1.507     raeburn  6916: table.LC_nested td {
1.735     bisitz   6917:   background-color: #FFFFFF;
1.451     albertel 6918:   font-size: small;
1.507     raeburn  6919: }
1.795     www      6920: 
1.507     raeburn  6921: table.LC_nested_outer tr th.LC_right_item,
                   6922: table.LC_nested tr.LC_info_row td.LC_right_item,
                   6923: table.LC_nested tr.LC_odd_row td.LC_right_item,
                   6924: table.LC_nested tr td.LC_right_item {
1.451     albertel 6925:   text-align: right;
                   6926: }
                   6927: 
1.507     raeburn  6928: table.LC_nested tr.LC_odd_row td {
1.735     bisitz   6929:   background-color: #EEEEEE;
1.451     albertel 6930: }
                   6931: 
1.473     raeburn  6932: table.LC_createuser {
                   6933: }
                   6934: 
                   6935: table.LC_createuser tr.LC_section_row td {
1.701     harmsja  6936:   font-size: small;
1.473     raeburn  6937: }
                   6938: 
                   6939: table.LC_createuser tr.LC_info_row td  {
1.735     bisitz   6940:   background-color: #CCCCCC;
1.473     raeburn  6941:   font-weight: bold;
                   6942:   text-align: center;
                   6943: }
                   6944: 
1.349     albertel 6945: table.LC_calendar {
                   6946:   border: 1px solid #000000;
                   6947:   border-collapse: collapse;
1.917     raeburn  6948:   width: 98%;
1.349     albertel 6949: }
1.795     www      6950: 
1.349     albertel 6951: table.LC_calendar_pickdate {
                   6952:   font-size: xx-small;
                   6953: }
1.795     www      6954: 
1.349     albertel 6955: table.LC_calendar tr td {
                   6956:   border: 1px solid #000000;
                   6957:   vertical-align: top;
1.917     raeburn  6958:   width: 14%;
1.349     albertel 6959: }
1.795     www      6960: 
1.349     albertel 6961: table.LC_calendar tr td.LC_calendar_day_empty {
                   6962:   background-color: $data_table_dark;
                   6963: }
1.795     www      6964: 
1.779     bisitz   6965: table.LC_calendar tr td.LC_calendar_day_current {
                   6966:   background-color: $data_table_highlight;
1.777     tempelho 6967: }
1.795     www      6968: 
1.938     bisitz   6969: table.LC_data_table tr td.LC_mail_new {
1.349     albertel 6970:   background-color: $mail_new;
                   6971: }
1.795     www      6972: 
1.938     bisitz   6973: table.LC_data_table tr.LC_mail_new:hover {
1.349     albertel 6974:   background-color: $mail_new_hover;
                   6975: }
1.795     www      6976: 
1.938     bisitz   6977: table.LC_data_table tr td.LC_mail_read {
1.349     albertel 6978:   background-color: $mail_read;
                   6979: }
1.795     www      6980: 
1.938     bisitz   6981: /*
                   6982: table.LC_data_table tr.LC_mail_read:hover {
1.349     albertel 6983:   background-color: $mail_read_hover;
                   6984: }
1.938     bisitz   6985: */
1.795     www      6986: 
1.938     bisitz   6987: table.LC_data_table tr td.LC_mail_replied {
1.349     albertel 6988:   background-color: $mail_replied;
                   6989: }
1.795     www      6990: 
1.938     bisitz   6991: /*
                   6992: table.LC_data_table tr.LC_mail_replied:hover {
1.349     albertel 6993:   background-color: $mail_replied_hover;
                   6994: }
1.938     bisitz   6995: */
1.795     www      6996: 
1.938     bisitz   6997: table.LC_data_table tr td.LC_mail_other {
1.349     albertel 6998:   background-color: $mail_other;
                   6999: }
1.795     www      7000: 
1.938     bisitz   7001: /*
                   7002: table.LC_data_table tr.LC_mail_other:hover {
1.349     albertel 7003:   background-color: $mail_other_hover;
                   7004: }
1.938     bisitz   7005: */
1.494     raeburn  7006: 
1.777     tempelho 7007: table.LC_data_table tr > td.LC_browser_file,
                   7008: table.LC_data_table tr > td.LC_browser_file_published {
1.899     bisitz   7009:   background: #AAEE77;
1.389     albertel 7010: }
1.795     www      7011: 
1.777     tempelho 7012: table.LC_data_table tr > td.LC_browser_file_locked,
                   7013: table.LC_data_table tr > td.LC_browser_file_unpublished {
1.389     albertel 7014:   background: #FFAA99;
1.387     albertel 7015: }
1.795     www      7016: 
1.777     tempelho 7017: table.LC_data_table tr > td.LC_browser_file_obsolete {
1.899     bisitz   7018:   background: #888888;
1.779     bisitz   7019: }
1.795     www      7020: 
1.777     tempelho 7021: table.LC_data_table tr > td.LC_browser_file_modified,
1.779     bisitz   7022: table.LC_data_table tr > td.LC_browser_file_metamodified {
1.899     bisitz   7023:   background: #F8F866;
1.777     tempelho 7024: }
1.795     www      7025: 
1.696     bisitz   7026: table.LC_data_table tr.LC_browser_folder > td {
1.899     bisitz   7027:   background: #E0E8FF;
1.387     albertel 7028: }
1.696     bisitz   7029: 
1.707     bisitz   7030: table.LC_data_table tr > td.LC_roles_is {
1.911     bisitz   7031:   /* background: #77FF77; */
1.707     bisitz   7032: }
1.795     www      7033: 
1.707     bisitz   7034: table.LC_data_table tr > td.LC_roles_future {
1.939     bisitz   7035:   border-right: 8px solid #FFFF77;
1.707     bisitz   7036: }
1.795     www      7037: 
1.707     bisitz   7038: table.LC_data_table tr > td.LC_roles_will {
1.939     bisitz   7039:   border-right: 8px solid #FFAA77;
1.707     bisitz   7040: }
1.795     www      7041: 
1.707     bisitz   7042: table.LC_data_table tr > td.LC_roles_expired {
1.939     bisitz   7043:   border-right: 8px solid #FF7777;
1.707     bisitz   7044: }
1.795     www      7045: 
1.707     bisitz   7046: table.LC_data_table tr > td.LC_roles_will_not {
1.939     bisitz   7047:   border-right: 8px solid #AAFF77;
1.707     bisitz   7048: }
1.795     www      7049: 
1.707     bisitz   7050: table.LC_data_table tr > td.LC_roles_selected {
1.939     bisitz   7051:   border-right: 8px solid #11CC55;
1.707     bisitz   7052: }
                   7053: 
1.388     albertel 7054: span.LC_current_location {
1.701     harmsja  7055:   font-size:larger;
1.388     albertel 7056:   background: $pgbg;
                   7057: }
1.387     albertel 7058: 
1.1029    www      7059: span.LC_current_nav_location {
                   7060:   font-weight:bold;
                   7061:   background: $sidebg;
                   7062: }
                   7063: 
1.395     albertel 7064: span.LC_parm_menu_item {
                   7065:   font-size: larger;
                   7066: }
1.795     www      7067: 
1.395     albertel 7068: span.LC_parm_scope_all {
                   7069:   color: red;
                   7070: }
1.795     www      7071: 
1.395     albertel 7072: span.LC_parm_scope_folder {
                   7073:   color: green;
                   7074: }
1.795     www      7075: 
1.395     albertel 7076: span.LC_parm_scope_resource {
                   7077:   color: orange;
                   7078: }
1.795     www      7079: 
1.395     albertel 7080: span.LC_parm_part {
                   7081:   color: blue;
                   7082: }
1.795     www      7083: 
1.911     bisitz   7084: span.LC_parm_folder,
                   7085: span.LC_parm_symb {
1.395     albertel 7086:   font-size: x-small;
                   7087:   font-family: $mono;
                   7088:   color: #AAAAAA;
                   7089: }
                   7090: 
1.977     bisitz   7091: ul.LC_parm_parmlist li {
                   7092:   display: inline-block;
                   7093:   padding: 0.3em 0.8em;
                   7094:   vertical-align: top;
                   7095:   width: 150px;
                   7096:   border-top:1px solid $lg_border_color;
                   7097: }
                   7098: 
1.795     www      7099: td.LC_parm_overview_level_menu,
                   7100: td.LC_parm_overview_map_menu,
                   7101: td.LC_parm_overview_parm_selectors,
                   7102: td.LC_parm_overview_restrictions  {
1.396     albertel 7103:   border: 1px solid black;
                   7104:   border-collapse: collapse;
                   7105: }
1.795     www      7106: 
1.396     albertel 7107: table.LC_parm_overview_restrictions td {
                   7108:   border-width: 1px 4px 1px 4px;
                   7109:   border-style: solid;
                   7110:   border-color: $pgbg;
                   7111:   text-align: center;
                   7112: }
1.795     www      7113: 
1.396     albertel 7114: table.LC_parm_overview_restrictions th {
                   7115:   background: $tabbg;
                   7116:   border-width: 1px 4px 1px 4px;
                   7117:   border-style: solid;
                   7118:   border-color: $pgbg;
                   7119: }
1.795     www      7120: 
1.398     albertel 7121: table#LC_helpmenu {
1.803     bisitz   7122:   border: none;
1.398     albertel 7123:   height: 55px;
1.803     bisitz   7124:   border-spacing: 0;
1.398     albertel 7125: }
                   7126: 
                   7127: table#LC_helpmenu fieldset legend {
                   7128:   font-size: larger;
                   7129: }
1.795     www      7130: 
1.397     albertel 7131: table#LC_helpmenu_links {
                   7132:   width: 100%;
                   7133:   border: 1px solid black;
                   7134:   background: $pgbg;
1.803     bisitz   7135:   padding: 0;
1.397     albertel 7136:   border-spacing: 1px;
                   7137: }
1.795     www      7138: 
1.397     albertel 7139: table#LC_helpmenu_links tr td {
                   7140:   padding: 1px;
                   7141:   background: $tabbg;
1.399     albertel 7142:   text-align: center;
                   7143:   font-weight: bold;
1.397     albertel 7144: }
1.396     albertel 7145: 
1.795     www      7146: table#LC_helpmenu_links a:link,
                   7147: table#LC_helpmenu_links a:visited,
1.397     albertel 7148: table#LC_helpmenu_links a:active {
                   7149:   text-decoration: none;
                   7150:   color: $font;
                   7151: }
1.795     www      7152: 
1.397     albertel 7153: table#LC_helpmenu_links a:hover {
                   7154:   text-decoration: underline;
                   7155:   color: $vlink;
                   7156: }
1.396     albertel 7157: 
1.417     albertel 7158: .LC_chrt_popup_exists {
                   7159:   border: 1px solid #339933;
                   7160:   margin: -1px;
                   7161: }
1.795     www      7162: 
1.417     albertel 7163: .LC_chrt_popup_up {
                   7164:   border: 1px solid yellow;
                   7165:   margin: -1px;
                   7166: }
1.795     www      7167: 
1.417     albertel 7168: .LC_chrt_popup {
                   7169:   border: 1px solid #8888FF;
                   7170:   background: #CCCCFF;
                   7171: }
1.795     www      7172: 
1.421     albertel 7173: table.LC_pick_box {
                   7174:   border-collapse: separate;
                   7175:   background: white;
                   7176:   border: 1px solid black;
                   7177:   border-spacing: 1px;
                   7178: }
1.795     www      7179: 
1.421     albertel 7180: table.LC_pick_box td.LC_pick_box_title {
1.850     bisitz   7181:   background: $sidebg;
1.421     albertel 7182:   font-weight: bold;
1.900     bisitz   7183:   text-align: left;
1.740     bisitz   7184:   vertical-align: top;
1.421     albertel 7185:   width: 184px;
                   7186:   padding: 8px;
                   7187: }
1.795     www      7188: 
1.579     raeburn  7189: table.LC_pick_box td.LC_pick_box_value {
                   7190:   text-align: left;
                   7191:   padding: 8px;
                   7192: }
1.795     www      7193: 
1.579     raeburn  7194: table.LC_pick_box td.LC_pick_box_select {
                   7195:   text-align: left;
                   7196:   padding: 8px;
                   7197: }
1.795     www      7198: 
1.424     albertel 7199: table.LC_pick_box td.LC_pick_box_separator {
1.803     bisitz   7200:   padding: 0;
1.421     albertel 7201:   height: 1px;
                   7202:   background: black;
                   7203: }
1.795     www      7204: 
1.421     albertel 7205: table.LC_pick_box td.LC_pick_box_submit {
                   7206:   text-align: right;
                   7207: }
1.795     www      7208: 
1.579     raeburn  7209: table.LC_pick_box td.LC_evenrow_value {
                   7210:   text-align: left;
                   7211:   padding: 8px;
                   7212:   background-color: $data_table_light;
                   7213: }
1.795     www      7214: 
1.579     raeburn  7215: table.LC_pick_box td.LC_oddrow_value {
                   7216:   text-align: left;
                   7217:   padding: 8px;
                   7218:   background-color: $data_table_light;
                   7219: }
1.795     www      7220: 
1.579     raeburn  7221: span.LC_helpform_receipt_cat {
                   7222:   font-weight: bold;
                   7223: }
1.795     www      7224: 
1.424     albertel 7225: table.LC_group_priv_box {
                   7226:   background: white;
                   7227:   border: 1px solid black;
                   7228:   border-spacing: 1px;
                   7229: }
1.795     www      7230: 
1.424     albertel 7231: table.LC_group_priv_box td.LC_pick_box_title {
                   7232:   background: $tabbg;
                   7233:   font-weight: bold;
                   7234:   text-align: right;
                   7235:   width: 184px;
                   7236: }
1.795     www      7237: 
1.424     albertel 7238: table.LC_group_priv_box td.LC_groups_fixed {
                   7239:   background: $data_table_light;
                   7240:   text-align: center;
                   7241: }
1.795     www      7242: 
1.424     albertel 7243: table.LC_group_priv_box td.LC_groups_optional {
                   7244:   background: $data_table_dark;
                   7245:   text-align: center;
                   7246: }
1.795     www      7247: 
1.424     albertel 7248: table.LC_group_priv_box td.LC_groups_functionality {
                   7249:   background: $data_table_darker;
                   7250:   text-align: center;
                   7251:   font-weight: bold;
                   7252: }
1.795     www      7253: 
1.424     albertel 7254: table.LC_group_priv td {
                   7255:   text-align: left;
1.803     bisitz   7256:   padding: 0;
1.424     albertel 7257: }
                   7258: 
                   7259: .LC_navbuttons {
                   7260:   margin: 2ex 0ex 2ex 0ex;
                   7261: }
1.795     www      7262: 
1.423     albertel 7263: .LC_topic_bar {
                   7264:   font-weight: bold;
                   7265:   background: $tabbg;
1.918     wenzelju 7266:   margin: 1em 0em 1em 2em;
1.805     bisitz   7267:   padding: 3px;
1.918     wenzelju 7268:   font-size: 1.2em;
1.423     albertel 7269: }
1.795     www      7270: 
1.423     albertel 7271: .LC_topic_bar span {
1.918     wenzelju 7272:   left: 0.5em;
                   7273:   position: absolute;
1.423     albertel 7274:   vertical-align: middle;
1.918     wenzelju 7275:   font-size: 1.2em;
1.423     albertel 7276: }
1.795     www      7277: 
1.423     albertel 7278: table.LC_course_group_status {
                   7279:   margin: 20px;
                   7280: }
1.795     www      7281: 
1.423     albertel 7282: table.LC_status_selector td {
                   7283:   vertical-align: top;
                   7284:   text-align: center;
1.424     albertel 7285:   padding: 4px;
                   7286: }
1.795     www      7287: 
1.599     albertel 7288: div.LC_feedback_link {
1.616     albertel 7289:   clear: both;
1.829     kalberla 7290:   background: $sidebg;
1.779     bisitz   7291:   width: 100%;
1.829     kalberla 7292:   padding-bottom: 10px;
                   7293:   border: 1px $tabbg solid;
1.833     kalberla 7294:   height: 22px;
                   7295:   line-height: 22px;
                   7296:   padding-top: 5px;
                   7297: }
                   7298: 
                   7299: div.LC_feedback_link img {
                   7300:   height: 22px;
1.867     kalberla 7301:   vertical-align:middle;
1.829     kalberla 7302: }
                   7303: 
1.911     bisitz   7304: div.LC_feedback_link a {
1.829     kalberla 7305:   text-decoration: none;
1.489     raeburn  7306: }
1.795     www      7307: 
1.867     kalberla 7308: div.LC_comblock {
1.911     bisitz   7309:   display:inline;
1.867     kalberla 7310:   color:$font;
                   7311:   font-size:90%;
                   7312: }
                   7313: 
                   7314: div.LC_feedback_link div.LC_comblock {
                   7315:   padding-left:5px;
                   7316: }
                   7317: 
                   7318: div.LC_feedback_link div.LC_comblock a {
                   7319:   color:$font;
                   7320: }
                   7321: 
1.489     raeburn  7322: span.LC_feedback_link {
1.858     bisitz   7323:   /* background: $feedback_link_bg; */
1.599     albertel 7324:   font-size: larger;
                   7325: }
1.795     www      7326: 
1.599     albertel 7327: span.LC_message_link {
1.858     bisitz   7328:   /* background: $feedback_link_bg; */
1.599     albertel 7329:   font-size: larger;
                   7330:   position: absolute;
                   7331:   right: 1em;
1.489     raeburn  7332: }
1.421     albertel 7333: 
1.515     albertel 7334: table.LC_prior_tries {
1.524     albertel 7335:   border: 1px solid #000000;
                   7336:   border-collapse: separate;
                   7337:   border-spacing: 1px;
1.515     albertel 7338: }
1.523     albertel 7339: 
1.515     albertel 7340: table.LC_prior_tries td {
1.524     albertel 7341:   padding: 2px;
1.515     albertel 7342: }
1.523     albertel 7343: 
                   7344: .LC_answer_correct {
1.795     www      7345:   background: lightgreen;
                   7346:   color: darkgreen;
                   7347:   padding: 6px;
1.523     albertel 7348: }
1.795     www      7349: 
1.523     albertel 7350: .LC_answer_charged_try {
1.797     www      7351:   background: #FFAAAA;
1.795     www      7352:   color: darkred;
                   7353:   padding: 6px;
1.523     albertel 7354: }
1.795     www      7355: 
1.779     bisitz   7356: .LC_answer_not_charged_try,
1.523     albertel 7357: .LC_answer_no_grade,
                   7358: .LC_answer_late {
1.795     www      7359:   background: lightyellow;
1.523     albertel 7360:   color: black;
1.795     www      7361:   padding: 6px;
1.523     albertel 7362: }
1.795     www      7363: 
1.523     albertel 7364: .LC_answer_previous {
1.795     www      7365:   background: lightblue;
                   7366:   color: darkblue;
                   7367:   padding: 6px;
1.523     albertel 7368: }
1.795     www      7369: 
1.779     bisitz   7370: .LC_answer_no_message {
1.777     tempelho 7371:   background: #FFFFFF;
                   7372:   color: black;
1.795     www      7373:   padding: 6px;
1.779     bisitz   7374: }
1.795     www      7375: 
1.1075.2.140  raeburn  7376: .LC_answer_unknown,
                   7377: .LC_answer_warning {
1.779     bisitz   7378:   background: orange;
                   7379:   color: black;
1.795     www      7380:   padding: 6px;
1.777     tempelho 7381: }
1.795     www      7382: 
1.529     albertel 7383: span.LC_prior_numerical,
                   7384: span.LC_prior_string,
                   7385: span.LC_prior_custom,
                   7386: span.LC_prior_reaction,
                   7387: span.LC_prior_math {
1.925     bisitz   7388:   font-family: $mono;
1.523     albertel 7389:   white-space: pre;
                   7390: }
                   7391: 
1.525     albertel 7392: span.LC_prior_string {
1.925     bisitz   7393:   font-family: $mono;
1.525     albertel 7394:   white-space: pre;
                   7395: }
                   7396: 
1.523     albertel 7397: table.LC_prior_option {
                   7398:   width: 100%;
                   7399:   border-collapse: collapse;
                   7400: }
1.795     www      7401: 
1.911     bisitz   7402: table.LC_prior_rank,
1.795     www      7403: table.LC_prior_match {
1.528     albertel 7404:   border-collapse: collapse;
                   7405: }
1.795     www      7406: 
1.528     albertel 7407: table.LC_prior_option tr td,
                   7408: table.LC_prior_rank tr td,
                   7409: table.LC_prior_match tr td {
1.524     albertel 7410:   border: 1px solid #000000;
1.515     albertel 7411: }
                   7412: 
1.855     bisitz   7413: .LC_nobreak {
1.544     albertel 7414:   white-space: nowrap;
1.519     raeburn  7415: }
                   7416: 
1.576     raeburn  7417: span.LC_cusr_emph {
                   7418:   font-style: italic;
                   7419: }
                   7420: 
1.633     raeburn  7421: span.LC_cusr_subheading {
                   7422:   font-weight: normal;
                   7423:   font-size: 85%;
                   7424: }
                   7425: 
1.861     bisitz   7426: div.LC_docs_entry_move {
1.859     bisitz   7427:   border: 1px solid #BBBBBB;
1.545     albertel 7428:   background: #DDDDDD;
1.861     bisitz   7429:   width: 22px;
1.859     bisitz   7430:   padding: 1px;
                   7431:   margin: 0;
1.545     albertel 7432: }
                   7433: 
1.861     bisitz   7434: table.LC_data_table tr > td.LC_docs_entry_commands,
                   7435: table.LC_data_table tr > td.LC_docs_entry_parameter {
1.545     albertel 7436:   font-size: x-small;
                   7437: }
1.795     www      7438: 
1.861     bisitz   7439: .LC_docs_entry_parameter {
                   7440:   white-space: nowrap;
                   7441: }
                   7442: 
1.544     albertel 7443: .LC_docs_copy {
1.545     albertel 7444:   color: #000099;
1.544     albertel 7445: }
1.795     www      7446: 
1.544     albertel 7447: .LC_docs_cut {
1.545     albertel 7448:   color: #550044;
1.544     albertel 7449: }
1.795     www      7450: 
1.544     albertel 7451: .LC_docs_rename {
1.545     albertel 7452:   color: #009900;
1.544     albertel 7453: }
1.795     www      7454: 
1.544     albertel 7455: .LC_docs_remove {
1.545     albertel 7456:   color: #990000;
                   7457: }
                   7458: 
1.1075.2.134  raeburn  7459: .LC_domprefs_email,
1.547     albertel 7460: .LC_docs_reinit_warn,
                   7461: .LC_docs_ext_edit {
                   7462:   font-size: x-small;
                   7463: }
                   7464: 
1.545     albertel 7465: table.LC_docs_adddocs td,
                   7466: table.LC_docs_adddocs th {
                   7467:   border: 1px solid #BBBBBB;
                   7468:   padding: 4px;
                   7469:   background: #DDDDDD;
1.543     albertel 7470: }
                   7471: 
1.584     albertel 7472: table.LC_sty_begin {
                   7473:   background: #BBFFBB;
                   7474: }
1.795     www      7475: 
1.584     albertel 7476: table.LC_sty_end {
                   7477:   background: #FFBBBB;
                   7478: }
                   7479: 
1.589     raeburn  7480: table.LC_double_column {
1.803     bisitz   7481:   border-width: 0;
1.589     raeburn  7482:   border-collapse: collapse;
                   7483:   width: 100%;
                   7484:   padding: 2px;
                   7485: }
                   7486: 
                   7487: table.LC_double_column tr td.LC_left_col {
1.590     raeburn  7488:   top: 2px;
1.589     raeburn  7489:   left: 2px;
                   7490:   width: 47%;
                   7491:   vertical-align: top;
                   7492: }
                   7493: 
                   7494: table.LC_double_column tr td.LC_right_col {
                   7495:   top: 2px;
1.779     bisitz   7496:   right: 2px;
1.589     raeburn  7497:   width: 47%;
                   7498:   vertical-align: top;
                   7499: }
                   7500: 
1.591     raeburn  7501: div.LC_left_float {
                   7502:   float: left;
                   7503:   padding-right: 5%;
1.597     albertel 7504:   padding-bottom: 4px;
1.591     raeburn  7505: }
                   7506: 
                   7507: div.LC_clear_float_header {
1.597     albertel 7508:   padding-bottom: 2px;
1.591     raeburn  7509: }
                   7510: 
                   7511: div.LC_clear_float_footer {
1.597     albertel 7512:   padding-top: 10px;
1.591     raeburn  7513:   clear: both;
                   7514: }
                   7515: 
1.597     albertel 7516: div.LC_grade_show_user {
1.941     bisitz   7517: /*  border-left: 5px solid $sidebg; */
                   7518:   border-top: 5px solid #000000;
                   7519:   margin: 50px 0 0 0;
1.936     bisitz   7520:   padding: 15px 0 5px 10px;
1.597     albertel 7521: }
1.795     www      7522: 
1.936     bisitz   7523: div.LC_grade_show_user_odd_row {
1.941     bisitz   7524: /*  border-left: 5px solid #000000; */
                   7525: }
                   7526: 
                   7527: div.LC_grade_show_user div.LC_Box {
                   7528:   margin-right: 50px;
1.597     albertel 7529: }
                   7530: 
                   7531: div.LC_grade_submissions,
                   7532: div.LC_grade_message_center,
1.936     bisitz   7533: div.LC_grade_info_links {
1.597     albertel 7534:   margin: 5px;
                   7535:   width: 99%;
                   7536:   background: #FFFFFF;
                   7537: }
1.795     www      7538: 
1.597     albertel 7539: div.LC_grade_submissions_header,
1.936     bisitz   7540: div.LC_grade_message_center_header {
1.705     tempelho 7541:   font-weight: bold;
                   7542:   font-size: large;
1.597     albertel 7543: }
1.795     www      7544: 
1.597     albertel 7545: div.LC_grade_submissions_body,
1.936     bisitz   7546: div.LC_grade_message_center_body {
1.597     albertel 7547:   border: 1px solid black;
                   7548:   width: 99%;
                   7549:   background: #FFFFFF;
                   7550: }
1.795     www      7551: 
1.613     albertel 7552: table.LC_scantron_action {
                   7553:   width: 100%;
                   7554: }
1.795     www      7555: 
1.613     albertel 7556: table.LC_scantron_action tr th {
1.698     harmsja  7557:   font-weight:bold;
                   7558:   font-style:normal;
1.613     albertel 7559: }
1.795     www      7560: 
1.779     bisitz   7561: .LC_edit_problem_header,
1.614     albertel 7562: div.LC_edit_problem_footer {
1.705     tempelho 7563:   font-weight: normal;
                   7564:   font-size:  medium;
1.602     albertel 7565:   margin: 2px;
1.1060    bisitz   7566:   background-color: $sidebg;
1.600     albertel 7567: }
1.795     www      7568: 
1.600     albertel 7569: div.LC_edit_problem_header,
1.602     albertel 7570: div.LC_edit_problem_header div,
1.614     albertel 7571: div.LC_edit_problem_footer,
                   7572: div.LC_edit_problem_footer div,
1.602     albertel 7573: div.LC_edit_problem_editxml_header,
                   7574: div.LC_edit_problem_editxml_header div {
1.1075.2.112  raeburn  7575:   z-index: 100;
1.600     albertel 7576: }
1.795     www      7577: 
1.600     albertel 7578: div.LC_edit_problem_header_title {
1.705     tempelho 7579:   font-weight: bold;
                   7580:   font-size: larger;
1.602     albertel 7581:   background: $tabbg;
                   7582:   padding: 3px;
1.1060    bisitz   7583:   margin: 0 0 5px 0;
1.602     albertel 7584: }
1.795     www      7585: 
1.602     albertel 7586: table.LC_edit_problem_header_title {
                   7587:   width: 100%;
1.600     albertel 7588:   background: $tabbg;
1.602     albertel 7589: }
                   7590: 
1.1075.2.112  raeburn  7591: div.LC_edit_actionbar {
                   7592:     background-color: $sidebg;
                   7593:     margin: 0;
                   7594:     padding: 0;
                   7595:     line-height: 200%;
1.602     albertel 7596: }
1.795     www      7597: 
1.1075.2.112  raeburn  7598: div.LC_edit_actionbar div{
                   7599:     padding: 0;
                   7600:     margin: 0;
                   7601:     display: inline-block;
1.600     albertel 7602: }
1.795     www      7603: 
1.1075.2.34  raeburn  7604: .LC_edit_opt {
                   7605:   padding-left: 1em;
                   7606:   white-space: nowrap;
                   7607: }
                   7608: 
1.1075.2.57  raeburn  7609: .LC_edit_problem_latexhelper{
                   7610:     text-align: right;
                   7611: }
                   7612: 
                   7613: #LC_edit_problem_colorful div{
                   7614:     margin-left: 40px;
                   7615: }
                   7616: 
1.1075.2.112  raeburn  7617: #LC_edit_problem_codemirror div{
                   7618:     margin-left: 0px;
                   7619: }
                   7620: 
1.911     bisitz   7621: img.stift {
1.803     bisitz   7622:   border-width: 0;
                   7623:   vertical-align: middle;
1.677     riegler  7624: }
1.680     riegler  7625: 
1.923     bisitz   7626: table td.LC_mainmenu_col_fieldset {
1.680     riegler  7627:   vertical-align: top;
1.777     tempelho 7628: }
1.795     www      7629: 
1.716     raeburn  7630: div.LC_createcourse {
1.911     bisitz   7631:   margin: 10px 10px 10px 10px;
1.716     raeburn  7632: }
                   7633: 
1.917     raeburn  7634: .LC_dccid {
1.1075.2.38  raeburn  7635:   float: right;
1.917     raeburn  7636:   margin: 0.2em 0 0 0;
                   7637:   padding: 0;
                   7638:   font-size: 90%;
                   7639:   display:none;
                   7640: }
                   7641: 
1.897     wenzelju 7642: ol.LC_primary_menu a:hover,
1.721     harmsja  7643: ol#LC_MenuBreadcrumbs a:hover,
                   7644: ol#LC_PathBreadcrumbs a:hover,
1.897     wenzelju 7645: ul#LC_secondary_menu a:hover,
1.721     harmsja  7646: .LC_FormSectionClearButton input:hover
1.795     www      7647: ul.LC_TabContent   li:hover a {
1.952     onken    7648:   color:$button_hover;
1.911     bisitz   7649:   text-decoration:none;
1.693     droeschl 7650: }
                   7651: 
1.779     bisitz   7652: h1 {
1.911     bisitz   7653:   padding: 0;
                   7654:   line-height:130%;
1.693     droeschl 7655: }
1.698     harmsja  7656: 
1.911     bisitz   7657: h2,
                   7658: h3,
                   7659: h4,
                   7660: h5,
                   7661: h6 {
                   7662:   margin: 5px 0 5px 0;
                   7663:   padding: 0;
                   7664:   line-height:130%;
1.693     droeschl 7665: }
1.795     www      7666: 
                   7667: .LC_hcell {
1.911     bisitz   7668:   padding:3px 15px 3px 15px;
                   7669:   margin: 0;
                   7670:   background-color:$tabbg;
                   7671:   color:$fontmenu;
                   7672:   border-bottom:solid 1px $lg_border_color;
1.693     droeschl 7673: }
1.795     www      7674: 
1.840     bisitz   7675: .LC_Box > .LC_hcell {
1.911     bisitz   7676:   margin: 0 -10px 10px -10px;
1.835     bisitz   7677: }
                   7678: 
1.721     harmsja  7679: .LC_noBorder {
1.911     bisitz   7680:   border: 0;
1.698     harmsja  7681: }
1.693     droeschl 7682: 
1.721     harmsja  7683: .LC_FormSectionClearButton input {
1.911     bisitz   7684:   background-color:transparent;
                   7685:   border: none;
                   7686:   cursor:pointer;
                   7687:   text-decoration:underline;
1.693     droeschl 7688: }
1.763     bisitz   7689: 
                   7690: .LC_help_open_topic {
1.911     bisitz   7691:   color: #FFFFFF;
                   7692:   background-color: #EEEEFF;
                   7693:   margin: 1px;
                   7694:   padding: 4px;
                   7695:   border: 1px solid #000033;
                   7696:   white-space: nowrap;
                   7697:   /* vertical-align: middle; */
1.759     neumanie 7698: }
1.693     droeschl 7699: 
1.911     bisitz   7700: dl,
                   7701: ul,
                   7702: div,
                   7703: fieldset {
                   7704:   margin: 10px 10px 10px 0;
                   7705:   /* overflow: hidden; */
1.693     droeschl 7706: }
1.795     www      7707: 
1.1075.2.161.  .18(raeb 7708:-23): fieldset#LC_selectuser {
                   7709:-23):     margin: 0;
                   7710:-23):     padding: 0;
                   7711:-23): }
                   7712:-23): 
1.1075.2.90  raeburn  7713: article.geogebraweb div {
                   7714:     margin: 0;
                   7715: }
                   7716: 
1.838     bisitz   7717: fieldset > legend {
1.911     bisitz   7718:   font-weight: bold;
                   7719:   padding: 0 5px 0 5px;
1.838     bisitz   7720: }
                   7721: 
1.813     bisitz   7722: #LC_nav_bar {
1.911     bisitz   7723:   float: left;
1.995     raeburn  7724:   background-color: $pgbg_or_bgcolor;
1.966     bisitz   7725:   margin: 0 0 2px 0;
1.807     droeschl 7726: }
                   7727: 
1.916     droeschl 7728: #LC_realm {
                   7729:   margin: 0.2em 0 0 0;
                   7730:   padding: 0;
                   7731:   font-weight: bold;
                   7732:   text-align: center;
1.995     raeburn  7733:   background-color: $pgbg_or_bgcolor;
1.916     droeschl 7734: }
                   7735: 
1.911     bisitz   7736: #LC_nav_bar em {
                   7737:   font-weight: bold;
                   7738:   font-style: normal;
1.807     droeschl 7739: }
                   7740: 
1.897     wenzelju 7741: ol.LC_primary_menu {
1.934     droeschl 7742:   margin: 0;
1.1075.2.2  raeburn  7743:   padding: 0;
1.807     droeschl 7744: }
                   7745: 
1.852     droeschl 7746: ol#LC_PathBreadcrumbs {
1.911     bisitz   7747:   margin: 0;
1.693     droeschl 7748: }
                   7749: 
1.897     wenzelju 7750: ol.LC_primary_menu li {
1.1075.2.2  raeburn  7751:   color: RGB(80, 80, 80);
                   7752:   vertical-align: middle;
                   7753:   text-align: left;
                   7754:   list-style: none;
1.1075.2.112  raeburn  7755:   position: relative;
1.1075.2.2  raeburn  7756:   float: left;
1.1075.2.112  raeburn  7757:   z-index: 100; /* will be displayed above codemirror and underneath the help-layer */
                   7758:   line-height: 1.5em;
1.1075.2.2  raeburn  7759: }
                   7760: 
1.1075.2.113  raeburn  7761: ol.LC_primary_menu li a, 
1.1075.2.112  raeburn  7762: ol.LC_primary_menu li p {
1.1075.2.2  raeburn  7763:   display: block;
                   7764:   margin: 0;
                   7765:   padding: 0 5px 0 10px;
                   7766:   text-decoration: none;
                   7767: }
                   7768: 
1.1075.2.112  raeburn  7769: ol.LC_primary_menu li p span.LC_primary_menu_innertitle {
                   7770:   display: inline-block;
                   7771:   width: 95%;
                   7772:   text-align: left;
                   7773: }
                   7774: 
                   7775: ol.LC_primary_menu li p span.LC_primary_menu_innerarrow {
                   7776:   display: inline-block;
                   7777:   width: 5%;
                   7778:   float: right;
                   7779:   text-align: right;
                   7780:   font-size: 70%;
                   7781: }
                   7782: 
                   7783: ol.LC_primary_menu ul {
1.1075.2.2  raeburn  7784:   display: none;
1.1075.2.112  raeburn  7785:   width: 15em;
1.1075.2.2  raeburn  7786:   background-color: $data_table_light;
1.1075.2.112  raeburn  7787:   position: absolute;
                   7788:   top: 100%;
                   7789: }
                   7790: 
                   7791: ol.LC_primary_menu ul ul {
                   7792:   left: 100%;
                   7793:   top: 0;
1.1075.2.2  raeburn  7794: }
                   7795: 
1.1075.2.112  raeburn  7796: ol.LC_primary_menu li:hover > ul, ol.LC_primary_menu li.hover > ul {
1.1075.2.2  raeburn  7797:   display: block;
                   7798:   position: absolute;
                   7799:   margin: 0;
                   7800:   padding: 0;
1.1075.2.5  raeburn  7801:   z-index: 2;
1.1075.2.2  raeburn  7802: }
                   7803: 
                   7804: ol.LC_primary_menu li:hover li, ol.LC_primary_menu li.hover li {
1.1075.2.112  raeburn  7805: /* First Submenu -> size should be smaller than the menu title of the whole menu */
1.1075.2.2  raeburn  7806:   font-size: 90%;
1.911     bisitz   7807:   vertical-align: top;
1.1075.2.2  raeburn  7808:   float: none;
1.1075.2.5  raeburn  7809:   border-left: 1px solid black;
                   7810:   border-right: 1px solid black;
1.1075.2.112  raeburn  7811: /* A dark bottom border to visualize different menu options;
                   7812: overwritten in the create_submenu routine for the last border-bottom of the menu */
                   7813:   border-bottom: 1px solid $data_table_dark;
1.1075.2.2  raeburn  7814: }
                   7815: 
1.1075.2.112  raeburn  7816: ol.LC_primary_menu li li p:hover {
                   7817:   color:$button_hover;
                   7818:   text-decoration:none;
                   7819:   background-color:$data_table_dark;
1.1075.2.2  raeburn  7820: }
                   7821: 
                   7822: ol.LC_primary_menu li li a:hover {
                   7823:    color:$button_hover;
                   7824:    background-color:$data_table_dark;
1.693     droeschl 7825: }
                   7826: 
1.1075.2.112  raeburn  7827: /* Font-size equal to the size of the predecessors*/
                   7828: ol.LC_primary_menu li:hover li li {
                   7829:   font-size: 100%;
                   7830: }
                   7831: 
1.897     wenzelju 7832: ol.LC_primary_menu li img {
1.911     bisitz   7833:   vertical-align: bottom;
1.934     droeschl 7834:   height: 1.1em;
1.1075.2.3  raeburn  7835:   margin: 0.2em 0 0 0;
1.693     droeschl 7836: }
                   7837: 
1.897     wenzelju 7838: ol.LC_primary_menu a {
1.911     bisitz   7839:   color: RGB(80, 80, 80);
                   7840:   text-decoration: none;
1.693     droeschl 7841: }
1.795     www      7842: 
1.949     droeschl 7843: ol.LC_primary_menu a.LC_new_message {
                   7844:   font-weight:bold;
                   7845:   color: darkred;
                   7846: }
                   7847: 
1.975     raeburn  7848: ol.LC_docs_parameters {
                   7849:   margin-left: 0;
                   7850:   padding: 0;
                   7851:   list-style: none;
                   7852: }
                   7853: 
                   7854: ol.LC_docs_parameters li {
                   7855:   margin: 0;
                   7856:   padding-right: 20px;
                   7857:   display: inline;
                   7858: }
                   7859: 
1.976     raeburn  7860: ol.LC_docs_parameters li:before {
                   7861:   content: "\\002022 \\0020";
                   7862: }
                   7863: 
                   7864: li.LC_docs_parameters_title {
                   7865:   font-weight: bold;
                   7866: }
                   7867: 
                   7868: ol.LC_docs_parameters li.LC_docs_parameters_title:before {
                   7869:   content: "";
                   7870: }
                   7871: 
1.897     wenzelju 7872: ul#LC_secondary_menu {
1.1075.2.23  raeburn  7873:   clear: right;
1.911     bisitz   7874:   color: $fontmenu;
                   7875:   background: $tabbg;
                   7876:   list-style: none;
                   7877:   padding: 0;
                   7878:   margin: 0;
                   7879:   width: 100%;
1.995     raeburn  7880:   text-align: left;
1.1075.2.4  raeburn  7881:   float: left;
1.808     droeschl 7882: }
                   7883: 
1.897     wenzelju 7884: ul#LC_secondary_menu li {
1.911     bisitz   7885:   font-weight: bold;
                   7886:   line-height: 1.8em;
                   7887:   border-right: 1px solid black;
1.1075.2.4  raeburn  7888:   float: left;
                   7889: }
                   7890: 
                   7891: ul#LC_secondary_menu li.LC_hoverable:hover, ul#LC_secondary_menu li.hover {
                   7892:   background-color: $data_table_light;
                   7893: }
                   7894: 
                   7895: ul#LC_secondary_menu li a {
                   7896:   padding: 0 0.8em;
                   7897: }
                   7898: 
                   7899: ul#LC_secondary_menu li ul {
                   7900:   display: none;
                   7901: }
                   7902: 
                   7903: ul#LC_secondary_menu li:hover ul, ul#LC_secondary_menu li.hover ul {
                   7904:   display: block;
                   7905:   position: absolute;
                   7906:   margin: 0;
                   7907:   padding: 0;
                   7908:   list-style:none;
                   7909:   float: none;
                   7910:   background-color: $data_table_light;
1.1075.2.5  raeburn  7911:   z-index: 2;
1.1075.2.10  raeburn  7912:   margin-left: -1px;
1.1075.2.4  raeburn  7913: }
                   7914: 
                   7915: ul#LC_secondary_menu li ul li {
                   7916:   font-size: 90%;
                   7917:   vertical-align: top;
                   7918:   border-left: 1px solid black;
                   7919:   border-right: 1px solid black;
1.1075.2.33  raeburn  7920:   background-color: $data_table_light;
1.1075.2.4  raeburn  7921:   list-style:none;
                   7922:   float: none;
                   7923: }
                   7924: 
                   7925: ul#LC_secondary_menu li ul li:hover, ul#LC_secondary_menu li ul li.hover {
                   7926:   background-color: $data_table_dark;
1.807     droeschl 7927: }
                   7928: 
1.847     tempelho 7929: ul.LC_TabContent {
1.911     bisitz   7930:   display:block;
                   7931:   background: $sidebg;
                   7932:   border-bottom: solid 1px $lg_border_color;
                   7933:   list-style:none;
1.1020    raeburn  7934:   margin: -1px -10px 0 -10px;
1.911     bisitz   7935:   padding: 0;
1.693     droeschl 7936: }
                   7937: 
1.795     www      7938: ul.LC_TabContent li,
                   7939: ul.LC_TabContentBigger li {
1.911     bisitz   7940:   float:left;
1.741     harmsja  7941: }
1.795     www      7942: 
1.897     wenzelju 7943: ul#LC_secondary_menu li a {
1.911     bisitz   7944:   color: $fontmenu;
                   7945:   text-decoration: none;
1.693     droeschl 7946: }
1.795     www      7947: 
1.721     harmsja  7948: ul.LC_TabContent {
1.952     onken    7949:   min-height:20px;
1.721     harmsja  7950: }
1.795     www      7951: 
                   7952: ul.LC_TabContent li {
1.911     bisitz   7953:   vertical-align:middle;
1.959     onken    7954:   padding: 0 16px 0 10px;
1.911     bisitz   7955:   background-color:$tabbg;
                   7956:   border-bottom:solid 1px $lg_border_color;
1.1020    raeburn  7957:   border-left: solid 1px $font;
1.721     harmsja  7958: }
1.795     www      7959: 
1.847     tempelho 7960: ul.LC_TabContent .right {
1.911     bisitz   7961:   float:right;
1.847     tempelho 7962: }
                   7963: 
1.911     bisitz   7964: ul.LC_TabContent li a,
                   7965: ul.LC_TabContent li {
                   7966:   color:rgb(47,47,47);
                   7967:   text-decoration:none;
                   7968:   font-size:95%;
                   7969:   font-weight:bold;
1.952     onken    7970:   min-height:20px;
                   7971: }
                   7972: 
1.959     onken    7973: ul.LC_TabContent li a:hover,
                   7974: ul.LC_TabContent li a:focus {
1.952     onken    7975:   color: $button_hover;
1.959     onken    7976:   background:none;
                   7977:   outline:none;
1.952     onken    7978: }
                   7979: 
                   7980: ul.LC_TabContent li:hover {
                   7981:   color: $button_hover;
                   7982:   cursor:pointer;
1.721     harmsja  7983: }
1.795     www      7984: 
1.911     bisitz   7985: ul.LC_TabContent li.active {
1.952     onken    7986:   color: $font;
1.911     bisitz   7987:   background:#FFFFFF url(/adm/lonIcons/open.gif) no-repeat scroll right center;
1.952     onken    7988:   border-bottom:solid 1px #FFFFFF;
                   7989:   cursor: default;
1.744     ehlerst  7990: }
1.795     www      7991: 
1.959     onken    7992: ul.LC_TabContent li.active a {
                   7993:   color:$font;
                   7994:   background:#FFFFFF;
                   7995:   outline: none;
                   7996: }
1.1047    raeburn  7997: 
                   7998: ul.LC_TabContent li.goback {
                   7999:   float: left;
                   8000:   border-left: none;
                   8001: }
                   8002: 
1.870     tempelho 8003: #maincoursedoc {
1.911     bisitz   8004:   clear:both;
1.870     tempelho 8005: }
                   8006: 
                   8007: ul.LC_TabContentBigger {
1.911     bisitz   8008:   display:block;
                   8009:   list-style:none;
                   8010:   padding: 0;
1.870     tempelho 8011: }
                   8012: 
1.795     www      8013: ul.LC_TabContentBigger li {
1.911     bisitz   8014:   vertical-align:bottom;
                   8015:   height: 30px;
                   8016:   font-size:110%;
                   8017:   font-weight:bold;
                   8018:   color: #737373;
1.841     tempelho 8019: }
                   8020: 
1.957     onken    8021: ul.LC_TabContentBigger li.active {
                   8022:   position: relative;
                   8023:   top: 1px;
                   8024: }
                   8025: 
1.870     tempelho 8026: ul.LC_TabContentBigger li a {
1.911     bisitz   8027:   background:url('/adm/lonIcons/tabbgleft.gif') left bottom no-repeat;
                   8028:   height: 30px;
                   8029:   line-height: 30px;
                   8030:   text-align: center;
                   8031:   display: block;
                   8032:   text-decoration: none;
1.958     onken    8033:   outline: none;  
1.741     harmsja  8034: }
1.795     www      8035: 
1.870     tempelho 8036: ul.LC_TabContentBigger li.active a {
1.911     bisitz   8037:   background:url('/adm/lonIcons/tabbgleft.gif') left top no-repeat;
                   8038:   color:$font;
1.744     ehlerst  8039: }
1.795     www      8040: 
1.870     tempelho 8041: ul.LC_TabContentBigger li b {
1.911     bisitz   8042:   background: url('/adm/lonIcons/tabbgright.gif') no-repeat right bottom;
                   8043:   display: block;
                   8044:   float: left;
                   8045:   padding: 0 30px;
1.957     onken    8046:   border-bottom: 1px solid $lg_border_color;
1.870     tempelho 8047: }
                   8048: 
1.956     onken    8049: ul.LC_TabContentBigger li:hover b {
                   8050:   color:$button_hover;
                   8051: }
                   8052: 
1.870     tempelho 8053: ul.LC_TabContentBigger li.active b {
1.911     bisitz   8054:   background:url('/adm/lonIcons/tabbgright.gif') right top no-repeat;
                   8055:   color:$font;
1.957     onken    8056:   border: 0;
1.741     harmsja  8057: }
1.693     droeschl 8058: 
1.870     tempelho 8059: 
1.862     bisitz   8060: ul.LC_CourseBreadcrumbs {
                   8061:   background: $sidebg;
1.1020    raeburn  8062:   height: 2em;
1.862     bisitz   8063:   padding-left: 10px;
1.1020    raeburn  8064:   margin: 0;
1.862     bisitz   8065:   list-style-position: inside;
                   8066: }
                   8067: 
1.911     bisitz   8068: ol#LC_MenuBreadcrumbs,
1.862     bisitz   8069: ol#LC_PathBreadcrumbs {
1.911     bisitz   8070:   padding-left: 10px;
                   8071:   margin: 0;
1.933     droeschl 8072:   height: 2.5em;  /* equal to #LC_breadcrumbs line-height */
1.693     droeschl 8073: }
                   8074: 
1.911     bisitz   8075: ol#LC_MenuBreadcrumbs li,
                   8076: ol#LC_PathBreadcrumbs li,
1.862     bisitz   8077: ul.LC_CourseBreadcrumbs li {
1.911     bisitz   8078:   display: inline;
1.933     droeschl 8079:   white-space: normal;  
1.693     droeschl 8080: }
                   8081: 
1.823     bisitz   8082: ol#LC_MenuBreadcrumbs li a,
1.862     bisitz   8083: ul.LC_CourseBreadcrumbs li a {
1.911     bisitz   8084:   text-decoration: none;
                   8085:   font-size:90%;
1.693     droeschl 8086: }
1.795     www      8087: 
1.969     droeschl 8088: ol#LC_MenuBreadcrumbs h1 {
                   8089:   display: inline;
                   8090:   font-size: 90%;
                   8091:   line-height: 2.5em;
                   8092:   margin: 0;
                   8093:   padding: 0;
                   8094: }
                   8095: 
1.795     www      8096: ol#LC_PathBreadcrumbs li a {
1.911     bisitz   8097:   text-decoration:none;
                   8098:   font-size:100%;
                   8099:   font-weight:bold;
1.693     droeschl 8100: }
1.795     www      8101: 
1.840     bisitz   8102: .LC_Box {
1.911     bisitz   8103:   border: solid 1px $lg_border_color;
                   8104:   padding: 0 10px 10px 10px;
1.746     neumanie 8105: }
1.795     www      8106: 
1.1020    raeburn  8107: .LC_DocsBox {
                   8108:   border: solid 1px $lg_border_color;
                   8109:   padding: 0 0 10px 10px;
                   8110: }
                   8111: 
1.795     www      8112: .LC_AboutMe_Image {
1.911     bisitz   8113:   float:left;
                   8114:   margin-right:10px;
1.747     neumanie 8115: }
1.795     www      8116: 
                   8117: .LC_Clear_AboutMe_Image {
1.911     bisitz   8118:   clear:left;
1.747     neumanie 8119: }
1.795     www      8120: 
1.721     harmsja  8121: dl.LC_ListStyleClean dt {
1.911     bisitz   8122:   padding-right: 5px;
                   8123:   display: table-header-group;
1.693     droeschl 8124: }
                   8125: 
1.721     harmsja  8126: dl.LC_ListStyleClean dd {
1.911     bisitz   8127:   display: table-row;
1.693     droeschl 8128: }
                   8129: 
1.721     harmsja  8130: .LC_ListStyleClean,
                   8131: .LC_ListStyleSimple,
                   8132: .LC_ListStyleNormal,
1.795     www      8133: .LC_ListStyleSpecial {
1.911     bisitz   8134:   /* display:block; */
                   8135:   list-style-position: inside;
                   8136:   list-style-type: none;
                   8137:   overflow: hidden;
                   8138:   padding: 0;
1.693     droeschl 8139: }
                   8140: 
1.721     harmsja  8141: .LC_ListStyleSimple li,
                   8142: .LC_ListStyleSimple dd,
                   8143: .LC_ListStyleNormal li,
                   8144: .LC_ListStyleNormal dd,
                   8145: .LC_ListStyleSpecial li,
1.795     www      8146: .LC_ListStyleSpecial dd {
1.911     bisitz   8147:   margin: 0;
                   8148:   padding: 5px 5px 5px 10px;
                   8149:   clear: both;
1.693     droeschl 8150: }
                   8151: 
1.721     harmsja  8152: .LC_ListStyleClean li,
                   8153: .LC_ListStyleClean dd {
1.911     bisitz   8154:   padding-top: 0;
                   8155:   padding-bottom: 0;
1.693     droeschl 8156: }
                   8157: 
1.721     harmsja  8158: .LC_ListStyleSimple dd,
1.795     www      8159: .LC_ListStyleSimple li {
1.911     bisitz   8160:   border-bottom: solid 1px $lg_border_color;
1.693     droeschl 8161: }
                   8162: 
1.721     harmsja  8163: .LC_ListStyleSpecial li,
                   8164: .LC_ListStyleSpecial dd {
1.911     bisitz   8165:   list-style-type: none;
                   8166:   background-color: RGB(220, 220, 220);
                   8167:   margin-bottom: 4px;
1.693     droeschl 8168: }
                   8169: 
1.721     harmsja  8170: table.LC_SimpleTable {
1.911     bisitz   8171:   margin:5px;
                   8172:   border:solid 1px $lg_border_color;
1.795     www      8173: }
1.693     droeschl 8174: 
1.721     harmsja  8175: table.LC_SimpleTable tr {
1.911     bisitz   8176:   padding: 0;
                   8177:   border:solid 1px $lg_border_color;
1.693     droeschl 8178: }
1.795     www      8179: 
                   8180: table.LC_SimpleTable thead {
1.911     bisitz   8181:   background:rgb(220,220,220);
1.693     droeschl 8182: }
                   8183: 
1.721     harmsja  8184: div.LC_columnSection {
1.911     bisitz   8185:   display: block;
                   8186:   clear: both;
                   8187:   overflow: hidden;
                   8188:   margin: 0;
1.693     droeschl 8189: }
                   8190: 
1.721     harmsja  8191: div.LC_columnSection>* {
1.911     bisitz   8192:   float: left;
                   8193:   margin: 10px 20px 10px 0;
                   8194:   overflow:hidden;
1.693     droeschl 8195: }
1.721     harmsja  8196: 
1.795     www      8197: table em {
1.911     bisitz   8198:   font-weight: bold;
                   8199:   font-style: normal;
1.748     schulted 8200: }
1.795     www      8201: 
1.779     bisitz   8202: table.LC_tableBrowseRes,
1.795     www      8203: table.LC_tableOfContent {
1.911     bisitz   8204:   border:none;
                   8205:   border-spacing: 1px;
                   8206:   padding: 3px;
                   8207:   background-color: #FFFFFF;
                   8208:   font-size: 90%;
1.753     droeschl 8209: }
1.789     droeschl 8210: 
1.911     bisitz   8211: table.LC_tableOfContent {
                   8212:   border-collapse: collapse;
1.789     droeschl 8213: }
                   8214: 
1.771     droeschl 8215: table.LC_tableBrowseRes a,
1.768     schulted 8216: table.LC_tableOfContent a {
1.911     bisitz   8217:   background-color: transparent;
                   8218:   text-decoration: none;
1.753     droeschl 8219: }
                   8220: 
1.795     www      8221: table.LC_tableOfContent img {
1.911     bisitz   8222:   border: none;
                   8223:   height: 1.3em;
                   8224:   vertical-align: text-bottom;
                   8225:   margin-right: 0.3em;
1.753     droeschl 8226: }
1.757     schulted 8227: 
1.795     www      8228: a#LC_content_toolbar_firsthomework {
1.911     bisitz   8229:   background-image:url(/res/adm/pages/open-first-problem.gif);
1.774     ehlerst  8230: }
                   8231: 
1.795     www      8232: a#LC_content_toolbar_everything {
1.911     bisitz   8233:   background-image:url(/res/adm/pages/show-all.gif);
1.774     ehlerst  8234: }
                   8235: 
1.795     www      8236: a#LC_content_toolbar_uncompleted {
1.911     bisitz   8237:   background-image:url(/res/adm/pages/show-incomplete-problems.gif);
1.774     ehlerst  8238: }
                   8239: 
1.795     www      8240: #LC_content_toolbar_clearbubbles {
1.911     bisitz   8241:   background-image:url(/res/adm/pages/mark-discussionentries-read.gif);
1.774     ehlerst  8242: }
                   8243: 
1.795     www      8244: a#LC_content_toolbar_changefolder {
1.911     bisitz   8245:   background : url(/res/adm/pages/close-all-folders.gif) top center ;
1.757     schulted 8246: }
                   8247: 
1.795     www      8248: a#LC_content_toolbar_changefolder_toggled {
1.911     bisitz   8249:   background-image:url(/res/adm/pages/open-all-folders.gif);
1.757     schulted 8250: }
                   8251: 
1.1043    raeburn  8252: a#LC_content_toolbar_edittoplevel {
                   8253:   background-image:url(/res/adm/pages/edittoplevel.gif);
                   8254: }
                   8255: 
1.1075.2.161.  .12(raeb 8256:-23): a#LC_content_toolbar_printout {
                   8257:-23):   background-image:url(/res/adm/pages/printout.gif);
                   8258:-23): }
                   8259:-23): 
1.795     www      8260: ul#LC_toolbar li a:hover {
1.911     bisitz   8261:   background-position: bottom center;
1.757     schulted 8262: }
                   8263: 
1.795     www      8264: ul#LC_toolbar {
1.911     bisitz   8265:   padding: 0;
                   8266:   margin: 2px;
                   8267:   list-style:none;
                   8268:   position:relative;
                   8269:   background-color:white;
1.1075.2.9  raeburn  8270:   overflow: auto;
1.757     schulted 8271: }
                   8272: 
1.795     www      8273: ul#LC_toolbar li {
1.911     bisitz   8274:   border:1px solid white;
                   8275:   padding: 0;
                   8276:   margin: 0;
                   8277:   float: left;
                   8278:   display:inline;
                   8279:   vertical-align:middle;
1.1075.2.9  raeburn  8280:   white-space: nowrap;
1.911     bisitz   8281: }
1.757     schulted 8282: 
1.783     amueller 8283: 
1.795     www      8284: a.LC_toolbarItem {
1.911     bisitz   8285:   display:block;
                   8286:   padding: 0;
                   8287:   margin: 0;
                   8288:   height: 32px;
                   8289:   width: 32px;
                   8290:   color:white;
                   8291:   border: none;
                   8292:   background-repeat:no-repeat;
                   8293:   background-color:transparent;
1.757     schulted 8294: }
                   8295: 
1.915     droeschl 8296: ul.LC_funclist {
                   8297:     margin: 0;
                   8298:     padding: 0.5em 1em 0.5em 0;
                   8299: }
                   8300: 
1.933     droeschl 8301: ul.LC_funclist > li:first-child {
                   8302:     font-weight:bold; 
                   8303:     margin-left:0.8em;
                   8304: }
                   8305: 
1.915     droeschl 8306: ul.LC_funclist + ul.LC_funclist {
                   8307:     /* 
                   8308:        left border as a seperator if we have more than
                   8309:        one list 
                   8310:     */
                   8311:     border-left: 1px solid $sidebg;
                   8312:     /* 
                   8313:        this hides the left border behind the border of the 
                   8314:        outer box if element is wrapped to the next 'line' 
                   8315:     */
                   8316:     margin-left: -1px;
                   8317: }
                   8318: 
1.843     bisitz   8319: ul.LC_funclist li {
1.915     droeschl 8320:   display: inline;
1.782     bisitz   8321:   white-space: nowrap;
1.915     droeschl 8322:   margin: 0 0 0 25px;
                   8323:   line-height: 150%;
1.782     bisitz   8324: }
                   8325: 
1.974     wenzelju 8326: .LC_hidden {
                   8327:   display: none;
                   8328: }
                   8329: 
1.1030    www      8330: .LCmodal-overlay {
                   8331: 		position:fixed;
                   8332: 		top:0;
                   8333: 		right:0;
                   8334: 		bottom:0;
                   8335: 		left:0;
                   8336: 		height:100%;
                   8337: 		width:100%;
                   8338: 		margin:0;
                   8339: 		padding:0;
                   8340: 		background:#999;
                   8341: 		opacity:.75;
                   8342: 		filter: alpha(opacity=75);
                   8343: 		-moz-opacity: 0.75;
                   8344: 		z-index:101;
                   8345: }
                   8346: 
                   8347: * html .LCmodal-overlay {   
                   8348: 		position: absolute;
                   8349: 		height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
                   8350: }
                   8351: 
                   8352: .LCmodal-window {
                   8353: 		position:fixed;
                   8354: 		top:50%;
                   8355: 		left:50%;
                   8356: 		margin:0;
                   8357: 		padding:0;
                   8358: 		z-index:102;
                   8359: 	}
                   8360: 
                   8361: * html .LCmodal-window {
                   8362: 		position:absolute;
                   8363: }
                   8364: 
                   8365: .LCclose-window {
                   8366: 		position:absolute;
                   8367: 		width:32px;
                   8368: 		height:32px;
                   8369: 		right:8px;
                   8370: 		top:8px;
                   8371: 		background:transparent url('/res/adm/pages/process-stop.png') no-repeat scroll right top;
                   8372: 		text-indent:-99999px;
                   8373: 		overflow:hidden;
                   8374: 		cursor:pointer;
                   8375: }
                   8376: 
1.1075.2.158  raeburn  8377: .LCisDisabled {
                   8378:   cursor: not-allowed;
                   8379:   opacity: 0.5;
                   8380: }
                   8381: 
                   8382: a[aria-disabled="true"] {
                   8383:   color: currentColor;
                   8384:   display: inline-block;  /* For IE11/ MS Edge bug */
                   8385:   pointer-events: none;
                   8386:   text-decoration: none;
                   8387: }
                   8388: 
1.1075.2.141  raeburn  8389: pre.LC_wordwrap {
                   8390:   white-space: pre-wrap;
                   8391:   white-space: -moz-pre-wrap;
                   8392:   white-space: -pre-wrap;
                   8393:   white-space: -o-pre-wrap;
                   8394:   word-wrap: break-word;
                   8395: }
                   8396: 
1.1075.2.17  raeburn  8397: /*
                   8398:   styles used by TTH when "Default set of options to pass to tth/m
                   8399:   when converting TeX" in course settings has been set
                   8400: 
                   8401:   option passed: -t
                   8402: 
                   8403: */
                   8404: 
                   8405: td div.comp { margin-top: -0.6ex; margin-bottom: -1ex;}
                   8406: td div.comb { margin-top: -0.6ex; margin-bottom: -.6ex;}
                   8407: td div.hrcomp { line-height: 0.9; margin-top: -0.8ex; margin-bottom: -1ex;}
                   8408: td div.norm {line-height:normal;}
                   8409: 
                   8410: /*
                   8411:   option passed -y3
                   8412: */
                   8413: 
                   8414: span.roman {font-family: serif; font-style: normal; font-weight: normal;}
                   8415: span.overacc2 {position: relative;  left: .8em; top: -1.2ex;}
                   8416: span.overacc1 {position: relative;  left: .6em; top: -1.2ex;}
                   8417: 
1.1075.2.121  raeburn  8418: #LC_minitab_header {
                   8419:   float:left;
                   8420:   width:100%;
                   8421:   background:#DAE0D2 url("/res/adm/pages/minitabmenu_bg.gif") repeat-x bottom;
                   8422:   font-size:93%;
                   8423:   line-height:normal;
                   8424:   margin: 0.5em 0 0.5em 0;
                   8425: }
                   8426: #LC_minitab_header ul {
                   8427:   margin:0;
                   8428:   padding:10px 10px 0;
                   8429:   list-style:none;
                   8430: }
                   8431: #LC_minitab_header li {
                   8432:   float:left;
                   8433:   background:url("/res/adm/pages/minitabmenu_left.gif") no-repeat left top;
                   8434:   margin:0;
                   8435:   padding:0 0 0 9px;
                   8436: }
                   8437: #LC_minitab_header a {
                   8438:   display:block;
                   8439:   background:url("/res/adm/pages/minitabmenu_right.gif") no-repeat right top;
                   8440:   padding:5px 15px 4px 6px;
                   8441: }
                   8442: #LC_minitab_header #LC_current_minitab {
                   8443:   background-image:url("/res/adm/pages/minitabmenu_left_on.gif");
                   8444: }
                   8445: #LC_minitab_header #LC_current_minitab a {
                   8446:   background-image:url("/res/adm/pages/minitabmenu_right_on.gif");
                   8447:   padding-bottom:5px;
                   8448: }
                   8449: 
                   8450: 
1.343     albertel 8451: END
                   8452: }
                   8453: 
1.306     albertel 8454: =pod
                   8455: 
                   8456: =item * &headtag()
                   8457: 
                   8458: Returns a uniform footer for LON-CAPA web pages.
                   8459: 
1.307     albertel 8460: Inputs: $title - optional title for the head
                   8461:         $head_extra - optional extra HTML to put inside the <head>
1.315     albertel 8462:         $args - optional arguments
1.319     albertel 8463:             force_register - if is true call registerurl so the remote is 
                   8464:                              informed
1.415     albertel 8465:             redirect       -> array ref of
                   8466:                                    1- seconds before redirect occurs
                   8467:                                    2- url to redirect to
                   8468:                                    3- whether the side effect should occur
1.315     albertel 8469:                            (side effect of setting 
                   8470:                                $env{'internal.head.redirect'} to the url 
1.1075.2.161.  .9(raebu 8471:22):                                redirected to)
                   8472:22):                                    4- whether the redirect target should be
                   8473:22):                                       the opener of the current (pop-up)
                   8474:22):                                       window (side effect of setting
                   8475:22):                                       $env{'internal.head.to_opener'} to
                   8476:22):                                       1, if true.
          .10(raeb 8477:-22):                                    5- whether encrypt check should be skipped
1.352     albertel 8478:             domain         -> force to color decorate a page for a specific
                   8479:                                domain
                   8480:             function       -> force usage of a specific rolish color scheme
                   8481:             bgcolor        -> override the default page bgcolor
1.460     albertel 8482:             no_auto_mt_title
                   8483:                            -> prevent &mt()ing the title arg
1.464     albertel 8484: 
1.306     albertel 8485: =cut
                   8486: 
                   8487: sub headtag {
1.313     albertel 8488:     my ($title,$head_extra,$args) = @_;
1.306     albertel 8489:     
1.363     albertel 8490:     my $function = $args->{'function'} || &get_users_function();
                   8491:     my $domain   = $args->{'domain'}   || &determinedomain();
                   8492:     my $bgcolor  = $args->{'bgcolor'}  || &designparm($function.'.pgbg',$domain);
1.1075.2.52  raeburn  8493:     my $httphost = $args->{'use_absolute'};
1.418     albertel 8494:     my $url = join(':',$env{'user.name'},$env{'user.domain'},
1.458     albertel 8495: 		   $Apache::lonnet::perlvar{'lonVersion'},
1.531     albertel 8496: 		   #time(),
1.418     albertel 8497: 		   $env{'environment.color.timestamp'},
1.363     albertel 8498: 		   $function,$domain,$bgcolor);
                   8499: 
1.369     www      8500:     $url = '/adm/css/'.&escape($url).'.css';
1.363     albertel 8501: 
1.308     albertel 8502:     my $result =
                   8503: 	'<head>'.
1.1075.2.56  raeburn  8504: 	&font_settings($args);
1.319     albertel 8505: 
1.1075.2.72  raeburn  8506:     my $inhibitprint;
                   8507:     if ($args->{'print_suppress'}) {
                   8508:         $inhibitprint = &print_suppression();
                   8509:     }
1.1064    raeburn  8510: 
1.461     albertel 8511:     if (!$args->{'frameset'}) {
                   8512: 	$result .= &Apache::lonhtmlcommon::htmlareaheaders();
                   8513:     }
1.1075.2.12  raeburn  8514:     if ($args->{'force_register'}) {
                   8515:         $result .= &Apache::lonmenu::registerurl(1);
1.319     albertel 8516:     }
1.436     albertel 8517:     if (!$args->{'no_nav_bar'} 
                   8518: 	&& !$args->{'only_body'}
                   8519: 	&& !$args->{'frameset'}) {
1.1075.2.52  raeburn  8520: 	$result .= &help_menu_js($httphost);
1.1032    www      8521:         $result.=&modal_window();
1.1038    www      8522:         $result.=&togglebox_script();
1.1034    www      8523:         $result.=&wishlist_window();
1.1041    www      8524:         $result.=&LCprogressbarUpdate_script();
1.1034    www      8525:     } else {
                   8526:         if ($args->{'add_modal'}) {
                   8527:            $result.=&modal_window();
                   8528:         }
                   8529:         if ($args->{'add_wishlist'}) {
                   8530:            $result.=&wishlist_window();
                   8531:         }
1.1038    www      8532:         if ($args->{'add_togglebox'}) {
                   8533:            $result.=&togglebox_script();
                   8534:         }
1.1041    www      8535:         if ($args->{'add_progressbar'}) {
                   8536:            $result.=&LCprogressbarUpdate_script();
                   8537:         }
1.436     albertel 8538:     }
1.314     albertel 8539:     if (ref($args->{'redirect'})) {
1.1075.2.161.  .10(raeb 8540:-22): 	my ($time,$url,$inhibit_continue,$to_opener,$skip_enc_check) = @{$args->{'redirect'}};
                   8541:-22):         if (!$skip_enc_check) {
                   8542:-22): 	    $url = &Apache::lonenc::check_encrypt($url);
                   8543:-22):         }
1.414     albertel 8544: 	if (!$inhibit_continue) {
                   8545: 	    $env{'internal.head.redirect'} = $url;
                   8546: 	}
1.1075.2.161.  .9(raebu 8547:22):         $result.=<<"ADDMETA";
1.313     albertel 8548: <meta http-equiv="pragma" content="no-cache" />
1.1075.2.161.  .9(raebu 8549:22): ADDMETA
                   8550:22):         if ($to_opener) {
                   8551:22):             $env{'internal.head.to_opener'} = 1;
                   8552:22):             my $dest = &js_escape($url);
                   8553:22):             my $timeout = int($time * 1000);
                   8554:22):             $result .=<<"ENDJS";
                   8555:22): <script type="text/javascript">
                   8556:22): // <![CDATA[
                   8557:22): function LC_To_Opener() {
                   8558:22):     var dest = '$dest';
                   8559:22):     if (dest != '') {
                   8560:22):         if (window.opener != null && !window.opener.closed) {
                   8561:22):             window.opener.location.href=dest;
                   8562:22):             window.close();
                   8563:22):         } else {
                   8564:22):             window.location.href=dest;
                   8565:22):         }
                   8566:22):     }
                   8567:22): }
                   8568:22): \$(document).ready(function () {
                   8569:22):     setTimeout('LC_To_Opener()',$timeout);
                   8570:22): });
                   8571:22): // ]]>
                   8572:22): </script>
                   8573:22): ENDJS
                   8574:22):         } else {
                   8575:22):             $result.=<<"ADDMETA";
1.344     albertel 8576: <meta http-equiv="Refresh" content="$time; url=$url" />
1.313     albertel 8577: ADDMETA
1.1075.2.161.  .9(raebu 8578:22):         }
1.1075.2.89  raeburn  8579:     } else {
                   8580:         unless (($args->{'frameset'}) || ($args->{'js_ready'}) || ($args->{'only_body'}) || ($args->{'no_nav_bar'})) {
                   8581:             my $requrl = $env{'request.uri'};
                   8582:             if ($requrl eq '') {
                   8583:                 $requrl = $ENV{'REQUEST_URI'};
                   8584:                 $requrl =~ s/\?.+$//;
                   8585:             }
                   8586:             unless (($requrl =~ m{^/adm/(?:switchserver|login|authenticate|logout|groupsort|cleanup|helper|slotrequest|grades)(\?|$)}) ||
                   8587:                     (($requrl =~ m{^/res/}) && (($env{'form.submitted'} eq 'scantron') ||
                   8588:                      ($env{'form.grade_symb'}) || ($Apache::lonhomework::scantronmode)))) {
                   8589:                 my $dom_in_use = $Apache::lonnet::perlvar{'lonDefDomain'};
                   8590:                 unless (&Apache::lonnet::allowed('mau',$dom_in_use)) {
                   8591:                     my %domdefs = &Apache::lonnet::get_domain_defaults($dom_in_use);
1.1075.2.145  raeburn  8592:                     my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
1.1075.2.151  raeburn  8593:                     my ($offload,$offloadoth);
1.1075.2.89  raeburn  8594:                     if (ref($domdefs{'offloadnow'}) eq 'HASH') {
                   8595:                         if ($domdefs{'offloadnow'}{$lonhost}) {
1.1075.2.145  raeburn  8596:                             $offload = 1;
1.1075.2.151  raeburn  8597:                             if (($env{'user.domain'} ne '') && ($env{'user.domain'} ne $dom_in_use) &&
                   8598:                                 (!(($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public')))) {
                   8599:                                 unless (&Apache::lonnet::shared_institution($env{'user.domain'})) {
                   8600:                                     $offloadoth = 1;
                   8601:                                     $dom_in_use = $env{'user.domain'};
                   8602:                                 }
                   8603:                             }
1.1075.2.145  raeburn  8604:                         }
                   8605:                     }
                   8606:                     unless ($offload) {
                   8607:                         if (ref($domdefs{'offloadoth'}) eq 'HASH') {
                   8608:                             if ($domdefs{'offloadoth'}{$lonhost}) {
                   8609:                                 if (($env{'user.domain'} ne '') && ($env{'user.domain'} ne $dom_in_use) &&
                   8610:                                     (!(($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public')))) {
                   8611:                                     unless (&Apache::lonnet::shared_institution($env{'user.domain'})) {
                   8612:                                         $offload = 1;
1.1075.2.151  raeburn  8613:                                         $offloadoth = 1;
1.1075.2.145  raeburn  8614:                                         $dom_in_use = $env{'user.domain'};
                   8615:                                     }
1.1075.2.89  raeburn  8616:                                 }
1.1075.2.145  raeburn  8617:                             }
                   8618:                         }
                   8619:                     }
                   8620:                     if ($offload) {
1.1075.2.158  raeburn  8621:                         my $newserver = &Apache::lonnet::spareserver(undef,30000,undef,1,$dom_in_use);
1.1075.2.151  raeburn  8622:                         if (($newserver eq '') && ($offloadoth)) {
                   8623:                             my @domains = &Apache::lonnet::current_machine_domains();
1.1075.2.161.  .1(raebu 8624:21):                             if (($dom_in_use ne '') && (!grep(/^\Q$dom_in_use\E$/,@domains))) {
1.1075.2.151  raeburn  8625:                                 ($newserver) = &Apache::lonnet::choose_server($dom_in_use);
                   8626:                             }
                   8627:                         }
1.1075.2.145  raeburn  8628:                         if (($newserver) && ($newserver ne $lonhost)) {
                   8629:                             my $numsec = 5;
                   8630:                             my $timeout = $numsec * 1000;
                   8631:                             my ($newurl,$locknum,%locks,$msg);
                   8632:                             if ($env{'request.role.adv'}) {
                   8633:                                 ($locknum,%locks) = &Apache::lonnet::get_locks();
                   8634:                             }
                   8635:                             my $disable_submit = 0;
                   8636:                             if ($requrl =~ /$LONCAPA::assess_re/) {
                   8637:                                 $disable_submit = 1;
                   8638:                             }
                   8639:                             if ($locknum) {
                   8640:                                 my @lockinfo = sort(values(%locks));
1.1075.2.153  raeburn  8641:                                 $msg = &mt('Once the following tasks are complete:')." \n".
1.1075.2.145  raeburn  8642:                                        join(", ",sort(values(%locks)))."\n";
                   8643:                                 if (&show_course()) {
                   8644:                                     $msg .= &mt('your session will be transferred to a different server, after you click "Courses".');
1.1075.2.89  raeburn  8645:                                 } else {
1.1075.2.145  raeburn  8646:                                     $msg .= &mt('your session will be transferred to a different server, after you click "Roles".');
                   8647:                                 }
                   8648:                             } else {
                   8649:                                 if (($requrl =~ m{^/res/}) && ($env{'form.submitted'} =~ /^part_/)) {
                   8650:                                     $msg = &mt('Your LON-CAPA submission has been recorded')."\n";
                   8651:                                 }
                   8652:                                 $msg .= &mt('Your current LON-CAPA session will be transferred to a different server in [quant,_1,second].',$numsec);
                   8653:                                 $newurl = '/adm/switchserver?otherserver='.$newserver;
                   8654:                                 if (($env{'request.role'}) && ($env{'request.role'} ne 'cm')) {
                   8655:                                     $newurl .= '&role='.$env{'request.role'};
                   8656:                                 }
                   8657:                                 if ($env{'request.symb'}) {
                   8658:                                     my $shownsymb = &Apache::lonenc::check_encrypt($env{'request.symb'});
                   8659:                                     if ($shownsymb =~ m{^/enc/}) {
                   8660:                                         my $reqdmajor = 2;
                   8661:                                         my $reqdminor = 11;
                   8662:                                         my $reqdsubminor = 3;
                   8663:                                         my $newserverrev = &Apache::lonnet::get_server_loncaparev('',$newserver);
                   8664:                                         my $remoterev = &Apache::lonnet::get_server_loncaparev(undef,$newserver);
                   8665:                                         my ($major,$minor,$subminor) = ($remoterev =~ /^\'?(\d+)\.(\d+)\.(\d+|)[\w.\-]+\'?$/);
                   8666:                                         if (($major eq '' && $minor eq '') ||
                   8667:                                             (($reqdmajor > $major) || (($reqdmajor == $major) && ($reqdminor > $minor)) ||
                   8668:                                             (($reqdmajor == $major) && ($reqdminor == $minor) && (($subminor eq '') ||
                   8669:                                              ($reqdsubminor > $subminor))))) {
                   8670:                                             undef($shownsymb);
                   8671:                                         }
1.1075.2.89  raeburn  8672:                                     }
1.1075.2.145  raeburn  8673:                                     if ($shownsymb) {
                   8674:                                         &js_escape(\$shownsymb);
                   8675:                                         $newurl .= '&symb='.$shownsymb;
1.1075.2.89  raeburn  8676:                                     }
1.1075.2.145  raeburn  8677:                                 } else {
                   8678:                                     my $shownurl = &Apache::lonenc::check_encrypt($requrl);
                   8679:                                     &js_escape(\$shownurl);
                   8680:                                     $newurl .= '&origurl='.$shownurl;
1.1075.2.89  raeburn  8681:                                 }
1.1075.2.145  raeburn  8682:                             }
                   8683:                             &js_escape(\$msg);
                   8684:                             $result.=<<OFFLOAD
1.1075.2.89  raeburn  8685: <meta http-equiv="pragma" content="no-cache" />
                   8686: <script type="text/javascript">
1.1075.2.92  raeburn  8687: // <![CDATA[
1.1075.2.89  raeburn  8688: function LC_Offload_Now() {
                   8689:     var dest = "$newurl";
                   8690:     if (dest != '') {
                   8691:         window.location.href="$newurl";
                   8692:     }
                   8693: }
1.1075.2.92  raeburn  8694: \$(document).ready(function () {
                   8695:     window.alert('$msg');
                   8696:     if ($disable_submit) {
1.1075.2.89  raeburn  8697:         \$(".LC_hwk_submit").prop("disabled", true);
                   8698:         \$( ".LC_textline" ).prop( "readonly", "readonly");
1.1075.2.92  raeburn  8699:     }
                   8700:     setTimeout('LC_Offload_Now()', $timeout);
                   8701: });
                   8702: // ]]>
1.1075.2.89  raeburn  8703: </script>
                   8704: OFFLOAD
                   8705:                         }
                   8706:                     }
                   8707:                 }
                   8708:             }
                   8709:         }
1.313     albertel 8710:     }
1.306     albertel 8711:     if (!defined($title)) {
                   8712: 	$title = 'The LearningOnline Network with CAPA';
                   8713:     }
1.460     albertel 8714:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
                   8715:     $result .= '<title> LON-CAPA '.$title.'</title>'
1.1075.2.61  raeburn  8716: 	.'<link rel="stylesheet" type="text/css" href="'.$url.'"';
                   8717:     if (!$args->{'frameset'}) {
                   8718:         $result .= ' /';
                   8719:     }
                   8720:     $result .= '>'
1.1064    raeburn  8721:         .$inhibitprint
1.414     albertel 8722: 	.$head_extra;
1.1075.2.108  raeburn  8723:     my $clientmobile;
                   8724:     if (($env{'user.name'} eq '') && ($env{'user.domain'} eq '')) {
                   8725:         (undef,undef,undef,undef,undef,undef,$clientmobile) = &decode_user_agent();
                   8726:     } else {
                   8727:         $clientmobile = $env{'browser.mobile'};
                   8728:     }
                   8729:     if ($clientmobile) {
1.1075.2.42  raeburn  8730:         $result .= '
                   8731: <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
                   8732: <meta name="apple-mobile-web-app-capable" content="yes" />';
                   8733:     }
1.1075.2.126  raeburn  8734:     $result .= '<meta name="google" content="notranslate" />'."\n";
1.962     droeschl 8735:     return $result.'</head>';
1.306     albertel 8736: }
                   8737: 
                   8738: =pod
                   8739: 
1.340     albertel 8740: =item * &font_settings()
                   8741: 
                   8742: Returns neccessary <meta> to set the proper encoding
                   8743: 
1.1075.2.56  raeburn  8744: Inputs: optional reference to HASH -- $args passed to &headtag()
1.340     albertel 8745: 
                   8746: =cut
                   8747: 
                   8748: sub font_settings {
1.1075.2.56  raeburn  8749:     my ($args) = @_;
1.340     albertel 8750:     my $headerstring='';
1.1075.2.56  raeburn  8751:     if ((!$env{'browser.mathml'} && $env{'browser.unicode'}) ||
                   8752:         ((ref($args) eq 'HASH') && ($args->{'browser.unicode'}))) {
1.340     albertel 8753: 	$headerstring.=
1.1075.2.61  raeburn  8754: 	    '<meta http-equiv="Content-Type" content="text/html; charset=utf-8"';
                   8755:         if (!$args->{'frameset'}) {
                   8756:             $headerstring.= ' /';
                   8757:         }
                   8758:         $headerstring .= '>'."\n";
1.340     albertel 8759:     }
                   8760:     return $headerstring;
                   8761: }
                   8762: 
1.341     albertel 8763: =pod
                   8764: 
1.1064    raeburn  8765: =item * &print_suppression()
                   8766: 
                   8767: In course context returns css which causes the body to be blank when media="print",
                   8768: if printout generation is unavailable for the current resource.
                   8769: 
                   8770: This could be because:
                   8771: 
                   8772: (a) printstartdate is in the future
                   8773: 
                   8774: (b) printenddate is in the past
                   8775: 
                   8776: (c) there is an active exam block with "printout"
                   8777: functionality blocked
                   8778: 
                   8779: Users with pav, pfo or evb privileges are exempt.
                   8780: 
                   8781: Inputs: none
                   8782: 
                   8783: =cut
                   8784: 
                   8785: 
                   8786: sub print_suppression {
                   8787:     my $noprint;
                   8788:     if ($env{'request.course.id'}) {
                   8789:         my $scope = $env{'request.course.id'};
                   8790:         if ((&Apache::lonnet::allowed('pav',$scope)) ||
                   8791:             (&Apache::lonnet::allowed('pfo',$scope))) {
                   8792:             return;
                   8793:         }
                   8794:         if ($env{'request.course.sec'} ne '') {
                   8795:             $scope .= "/$env{'request.course.sec'}";
                   8796:             if ((&Apache::lonnet::allowed('pav',$scope)) ||
                   8797:                 (&Apache::lonnet::allowed('pfo',$scope))) {
1.1065    raeburn  8798:                 return;
1.1064    raeburn  8799:             }
                   8800:         }
                   8801:         my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   8802:         my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1075.2.158  raeburn  8803:         my $clientip = &Apache::lonnet::get_requestor_ip();
                   8804:         my $blocked = &blocking_status('printout',$clientip,$cnum,$cdom,undef,1);
1.1064    raeburn  8805:         if ($blocked) {
                   8806:             my $checkrole = "cm./$cdom/$cnum";
                   8807:             if ($env{'request.course.sec'} ne '') {
                   8808:                 $checkrole .= "/$env{'request.course.sec'}";
                   8809:             }
                   8810:             unless ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
                   8811:                     ($env{'request.role'} !~ m{^st\./$cdom/$cnum})) {
                   8812:                 $noprint = 1;
                   8813:             }
                   8814:         }
                   8815:         unless ($noprint) {
                   8816:             my $symb = &Apache::lonnet::symbread();
                   8817:             if ($symb ne '') {
                   8818:                 my $navmap = Apache::lonnavmaps::navmap->new();
                   8819:                 if (ref($navmap)) {
                   8820:                     my $res = $navmap->getBySymb($symb);
                   8821:                     if (ref($res)) {
                   8822:                         if (!$res->resprintable()) {
                   8823:                             $noprint = 1;
                   8824:                         }
                   8825:                     }
                   8826:                 }
                   8827:             }
                   8828:         }
                   8829:         if ($noprint) {
                   8830:             return <<"ENDSTYLE";
                   8831: <style type="text/css" media="print">
                   8832:     body { display:none }
                   8833: </style>
                   8834: ENDSTYLE
                   8835:         }
                   8836:     }
                   8837:     return;
                   8838: }
                   8839: 
                   8840: =pod
                   8841: 
1.341     albertel 8842: =item * &xml_begin()
                   8843: 
                   8844: Returns the needed doctype and <html>
                   8845: 
                   8846: Inputs: none
                   8847: 
                   8848: =cut
                   8849: 
                   8850: sub xml_begin {
1.1075.2.61  raeburn  8851:     my ($is_frameset) = @_;
1.341     albertel 8852:     my $output='';
                   8853: 
                   8854:     if ($env{'browser.mathml'}) {
                   8855: 	$output='<?xml version="1.0"?>'
                   8856:             #.'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'."\n"
                   8857: #            .'<!DOCTYPE html SYSTEM "/adm/MathML/mathml.dtd" '
                   8858:             
                   8859: #	    .'<!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">] >'
                   8860: 	    .'<!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">'
                   8861:             .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" ' 
                   8862: 	    .'xmlns="http://www.w3.org/1999/xhtml">';
1.1075.2.61  raeburn  8863:     } elsif ($is_frameset) {
                   8864:         $output='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'."\n".
                   8865:                 '<html>'."\n";
1.341     albertel 8866:     } else {
1.1075.2.61  raeburn  8867: 	$output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n".
                   8868:                 '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
1.341     albertel 8869:     }
                   8870:     return $output;
                   8871: }
1.340     albertel 8872: 
                   8873: =pod
                   8874: 
1.306     albertel 8875: =item * &start_page()
                   8876: 
                   8877: Returns a complete <html> .. <body> section for LON-CAPA web pages.
                   8878: 
1.648     raeburn  8879: Inputs:
                   8880: 
                   8881: =over 4
                   8882: 
                   8883: $title - optional title for the page
                   8884: 
                   8885: $head_extra - optional extra HTML to incude inside the <head>
                   8886: 
                   8887: $args - additional optional args supported are:
                   8888: 
                   8889: =over 8
                   8890: 
                   8891:              only_body      -> is true will set &bodytag() onlybodytag
1.317     albertel 8892:                                     arg on
1.814     bisitz   8893:              no_nav_bar     -> is true will set &bodytag() no_nav_bar arg on
1.648     raeburn  8894:              add_entries    -> additional attributes to add to the  <body>
                   8895:              domain         -> force to color decorate a page for a 
1.317     albertel 8896:                                     specific domain
1.648     raeburn  8897:              function       -> force usage of a specific rolish color
1.317     albertel 8898:                                     scheme
1.648     raeburn  8899:              redirect       -> see &headtag()
                   8900:              bgcolor        -> override the default page bg color
                   8901:              js_ready       -> return a string ready for being used in 
1.317     albertel 8902:                                     a javascript writeln
1.648     raeburn  8903:              html_encode    -> return a string ready for being used in 
1.320     albertel 8904:                                     a html attribute
1.648     raeburn  8905:              force_register -> if is true will turn on the &bodytag()
1.317     albertel 8906:                                     $forcereg arg
1.648     raeburn  8907:              frameset       -> if true will start with a <frameset>
1.330     albertel 8908:                                     rather than <body>
1.648     raeburn  8909:              skip_phases    -> hash ref of 
1.338     albertel 8910:                                     head -> skip the <html><head> generation
                   8911:                                     body -> skip all <body> generation
1.1075.2.12  raeburn  8912:              no_inline_link -> if true and in remote mode, don't show the
                   8913:                                     'Switch To Inline Menu' link
1.648     raeburn  8914:              no_auto_mt_title -> prevent &mt()ing the title arg
1.867     kalberla 8915:              bread_crumbs ->             Array containing breadcrumbs
1.983     raeburn  8916:              bread_crumbs_component ->  if exists show it as headline else show only the breadcrumbs
1.1075.2.123  raeburn  8917:              bread_crumbs_nomenu -> if true will pass false as the value of $menulink
                   8918:                                     to lonhtmlcommon::breadcrumbs
1.1075.2.15  raeburn  8919:              group          -> includes the current group, if page is for a
                   8920:                                specific group
1.1075.2.133  raeburn  8921:              use_absolute   -> for request for external resource or syllabus, this
                   8922:                                will contain https://<hostname> if server uses
                   8923:                                https (as per hosts.tab), but request is for http
                   8924:              hostname       -> hostname, originally from $r->hostname(), (optional).
1.1075.2.158  raeburn  8925:              links_disabled -> Links in primary and secondary menus are disabled
                   8926:                                (Can enable them once page has loaded - see lonroles.pm
                   8927:                                for an example).
1.1075.2.161.  .6(raebu 8928:22):              links_target   -> Target for links, e.g., _parent (optional).
1.361     albertel 8929: 
1.648     raeburn  8930: =back
1.460     albertel 8931: 
1.648     raeburn  8932: =back
1.562     albertel 8933: 
1.306     albertel 8934: =cut
                   8935: 
                   8936: sub start_page {
1.309     albertel 8937:     my ($title,$head_extra,$args) = @_;
1.318     albertel 8938:     #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
1.319     albertel 8939: 
1.315     albertel 8940:     $env{'internal.start_page'}++;
1.1075.2.161.  .1(raebu 8941:21):     my ($result,@advtools,$ltiscope,$ltiuri,%ltimenu,$menucoll,%menu);
1.964     droeschl 8942: 
1.338     albertel 8943:     if (! exists($args->{'skip_phases'}{'head'}) ) {
1.1075.2.62  raeburn  8944:         $result .= &xml_begin($args->{'frameset'}) . &headtag($title, $head_extra, $args);
1.338     albertel 8945:     }
1.1075.2.161.  .1(raebu 8946:21): 
                   8947:21):     if (($env{'request.course.id'}) && ($env{'request.lti.login'})) {
                   8948:21):         if ($env{'course.'.$env{'request.course.id'}.'.lti.override'}) {
                   8949:21):             unless ($env{'course.'.$env{'request.course.id'}.'.lti.topmenu'}) {
                   8950:21):                 $args->{'no_primary_menu'} = 1;
                   8951:21):             }
                   8952:21):             unless ($env{'course.'.$env{'request.course.id'}.'.lti.inlinemenu'}) {
                   8953:21):                 $args->{'no_inline_menu'} = 1;
                   8954:21):             }
                   8955:21):             if ($env{'course.'.$env{'request.course.id'}.'.lti.lcmenu'}) {
                   8956:21):                 map { $ltimenu{$_} = 1; } split(/,/,$env{'course.'.$env{'request.course.id'}.'.lti.lcmenu'});
                   8957:21):             }
                   8958:21):         } else {
                   8959:21):             my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   8960:21):             my %lti = &Apache::lonnet::get_domain_lti($cdom,'provider');
                   8961:21):             if (ref($lti{$env{'request.lti.login'}}) eq 'HASH') {
                   8962:21):                 unless ($lti{$env{'request.lti.login'}}{'topmenu'}) {
                   8963:21):                     $args->{'no_primary_menu'} = 1;
                   8964:21):                 }
                   8965:21):                 unless ($lti{$env{'request.lti.login'}}{'inlinemenu'}) {
                   8966:21):                     $args->{'no_inline_menu'} = 1;
                   8967:21):                 }
                   8968:21):                 if (ref($lti{$env{'request.lti.login'}}{'lcmenu'}) eq 'ARRAY') {
                   8969:21):                     map { $ltimenu{$_} = 1; } @{$lti{$env{'request.lti.login'}}{'lcmenu'}};
                   8970:21):                 }
                   8971:21):             }
                   8972:21):         }
                   8973:21):         ($ltiscope,$ltiuri) = &LONCAPA::ltiutils::lti_provider_scope($env{'request.lti.uri'},
                   8974:21):                                   $env{'course.'.$env{'request.course.id'}.'.domain'},
                   8975:21):                                   $env{'course.'.$env{'request.course.id'}.'.num'});
                   8976:21):     } elsif ($env{'request.course.id'}) {
                   8977:21):         my $expiretime=600;
                   8978:21):         if ((time-$env{'course.'.$env{'request.course.id'}.'.last_cache'}) > $expiretime) {
                   8979:21):             &Apache::lonnet::coursedescription($env{'request.course.id'},{'freshen_cache' => 1});
                   8980:21):         }
                   8981:21):         my ($deeplinkmenu,$menuref);
                   8982:21):         ($menucoll,$deeplinkmenu,$menuref) = &menucoll_in_effect();
                   8983:21):         if ($menucoll) {
                   8984:21):             if (ref($menuref) eq 'HASH') {
                   8985:21):                 %menu = %{$menuref};
                   8986:21):             }
                   8987:21):             if ($menu{'top'} eq 'n') {
                   8988:21):                 $args->{'no_primary_menu'} = 1;
                   8989:21):             }
                   8990:21):             if ($menu{'inline'} eq 'n') {
                   8991:21):                 unless (&Apache::lonnet::allowed('opa')) {
                   8992:21):                     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   8993:21):                     my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
                   8994:21):                     my $crstype = &course_type();
                   8995:21):                     my $now = time;
                   8996:21):                     my $ccrole;
                   8997:21):                     if ($crstype eq 'Community') {
                   8998:21):                         $ccrole = 'co';
                   8999:21):                     } else {
                   9000:21):                         $ccrole = 'cc';
                   9001:21):                     }
                   9002:21):                     if ($env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum}) {
                   9003:21):                         my ($start,$end) = split(/\./,$env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum});
                   9004:21):                         if ((($start) && ($start<0)) ||
                   9005:21):                             (($end) && ($end<$now))  ||
                   9006:21):                             (($start) && ($now<$start))) {
                   9007:21):                             $args->{'no_inline_menu'} = 1;
                   9008:21):                         }
                   9009:21):                     } else {
                   9010:21):                         $args->{'no_inline_menu'} = 1;
                   9011:21):                     }
                   9012:21):                 }
                   9013:21):             }
                   9014:21):         }
                   9015:21):     }
          .4(raebu 9016:22): 
          .8(raebu 9017:22):     my $showncrumbs;
1.338     albertel 9018:     if (! exists($args->{'skip_phases'}{'body'}) ) {
                   9019: 	if ($args->{'frameset'}) {
                   9020: 	    my $attr_string = &make_attr_string($args->{'force_register'},
                   9021: 						$args->{'add_entries'});
                   9022: 	    $result .= "\n<frameset $attr_string>\n";
1.831     bisitz   9023:         } else {
                   9024:             $result .=
                   9025:                 &bodytag($title, 
                   9026:                          $args->{'function'},       $args->{'add_entries'},
                   9027:                          $args->{'only_body'},      $args->{'domain'},
                   9028:                          $args->{'force_register'}, $args->{'no_nav_bar'},
1.1075.2.12  raeburn  9029:                          $args->{'bgcolor'},        $args->{'no_inline_link'},
1.1075.2.161.  .1(raebu 9030:21):                          $args,                     \@advtools,
          .8(raebu 9031:22):                          $ltiscope,$ltiuri,\%ltimenu,$menucoll,\%menu,\$showncrumbs);
1.831     bisitz   9032:         }
1.330     albertel 9033:     }
1.338     albertel 9034: 
1.315     albertel 9035:     if ($args->{'js_ready'}) {
1.713     kaisler  9036: 		$result = &js_ready($result);
1.315     albertel 9037:     }
1.320     albertel 9038:     if ($args->{'html_encode'}) {
1.713     kaisler  9039: 		$result = &html_encode($result);
                   9040:     }
                   9041: 
1.813     bisitz   9042:     # Preparation for new and consistent functionlist at top of screen
                   9043:     # if ($args->{'functionlist'}) {
                   9044:     #            $result .= &build_functionlist();
                   9045:     #}
                   9046: 
1.964     droeschl 9047:     # Don't add anything more if only_body wanted or in const space
                   9048:     return $result if    $args->{'only_body'} 
                   9049:                       || $env{'request.state'} eq 'construct';
1.813     bisitz   9050: 
                   9051:     #Breadcrumbs
1.758     kaisler  9052:     if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) {
1.1075.2.161.  .8(raebu 9053:22):         unless ($showncrumbs) {
1.758     kaisler  9054: 		&Apache::lonhtmlcommon::clear_breadcrumbs();
                   9055: 		#if any br links exists, add them to the breadcrumbs
                   9056: 		if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') {         
                   9057: 			foreach my $crumb (@{$args->{'bread_crumbs'}}){
                   9058: 				&Apache::lonhtmlcommon::add_breadcrumb($crumb);
                   9059: 			}
                   9060: 		}
1.1075.2.19  raeburn  9061:                 # if @advtools array contains items add then to the breadcrumbs
                   9062:                 if (@advtools > 0) {
                   9063:                     &Apache::lonmenu::advtools_crumbs(@advtools);
                   9064:                 }
1.1075.2.123  raeburn  9065:                 my $menulink;
                   9066:                 # if arg: bread_crumbs_nomenu is true pass 0 as $menulink item.
1.1075.2.161.  .1(raebu 9067:21):                 if ((exists($args->{'bread_crumbs_nomenu'})) ||
                   9068:21):                     ($ltiscope eq 'map') || ($ltiscope eq 'resource')) {
1.1075.2.123  raeburn  9069:                     $menulink = 0;
                   9070:                 } else {
                   9071:                     undef($menulink);
                   9072:                 }
1.1075.2.161.  .8(raebu 9073:22):                 my $linkprotout;
                   9074:22):                 if ($env{'request.deeplink.login'}) {
                   9075:22):                     my $linkprotout = &Apache::lonmenu::linkprot_exit();
                   9076:22):                     if ($linkprotout) {
                   9077:22):                         &Apache::lonhtmlcommon::add_breadcrumb_tool('tools',$linkprotout);
                   9078:22):                     }
                   9079:22):                 }
1.758     kaisler  9080: 		#if bread_crumbs_component exists show it as headline else show only the breadcrumbs
                   9081: 		if(exists($args->{'bread_crumbs_component'})){
1.1075.2.123  raeburn  9082: 			$result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'},'',$menulink);
1.1075.2.161.  .1(raebu 9083:21): 		} else {
1.1075.2.123  raeburn  9084: 			$result .= &Apache::lonhtmlcommon::breadcrumbs('','',$menulink);
1.758     kaisler  9085: 		}
1.1075.2.161.  .8(raebu 9086:22):         }
1.1075.2.24  raeburn  9087:     } elsif (($env{'environment.remote'} eq 'on') &&
                   9088:              ($env{'form.inhibitmenu'} ne 'yes') &&
                   9089:              ($env{'request.noversionuri'} =~ m{^/res/}) &&
                   9090:              ($env{'request.noversionuri'} !~ m{^/res/adm/pages/})) {
1.1075.2.21  raeburn  9091:         $result .= '<div style="padding:0;margin:0;clear:both"><hr /></div>';
1.320     albertel 9092:     }
1.315     albertel 9093:     return $result;
1.306     albertel 9094: }
                   9095: 
                   9096: sub end_page {
1.315     albertel 9097:     my ($args) = @_;
                   9098:     $env{'internal.end_page'}++;
1.330     albertel 9099:     my $result;
1.335     albertel 9100:     if ($args->{'discussion'}) {
                   9101: 	my ($target,$parser);
                   9102: 	if (ref($args->{'discussion'})) {
                   9103: 	    ($target,$parser) =($args->{'discussion'}{'target'},
                   9104: 				$args->{'discussion'}{'parser'});
                   9105: 	}
                   9106: 	$result .= &Apache::lonxml::xmlend($target,$parser);
                   9107:     }
1.330     albertel 9108:     if ($args->{'frameset'}) {
                   9109: 	$result .= '</frameset>';
                   9110:     } else {
1.635     raeburn  9111: 	$result .= &endbodytag($args);
1.330     albertel 9112:     }
1.1075.2.6  raeburn  9113:     unless ($args->{'notbody'}) {
                   9114:         $result .= "\n</html>";
                   9115:     }
1.330     albertel 9116: 
1.315     albertel 9117:     if ($args->{'js_ready'}) {
1.317     albertel 9118: 	$result = &js_ready($result);
1.315     albertel 9119:     }
1.335     albertel 9120: 
1.320     albertel 9121:     if ($args->{'html_encode'}) {
                   9122: 	$result = &html_encode($result);
                   9123:     }
1.335     albertel 9124: 
1.315     albertel 9125:     return $result;
                   9126: }
                   9127: 
1.1075.2.161.  .1(raebu 9128:21): sub menucoll_in_effect {
                   9129:21):     my ($menucoll,$deeplinkmenu,%menu);
                   9130:21):     if ($env{'request.course.id'}) {
                   9131:21):         $menucoll = $env{'course.'.$env{'request.course.id'}.'.menudefault'};
                   9132:21):         if ($env{'request.deeplink.login'}) {
                   9133:21):             my ($deeplink_symb,$deeplink,$check_login_symb);
                   9134:21):             my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
                   9135:21):             my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   9136:21):             if ($env{'request.noversionuri'} =~ m{^/(res|uploaded)/}) {
                   9137:21):                 if ($env{'request.noversionuri'} =~ /\.(page|sequence)$/) {
                   9138:21):                     my $navmap = Apache::lonnavmaps::navmap->new();
                   9139:21):                     if (ref($navmap)) {
                   9140:21):                         $deeplink = $navmap->get_mapparam(undef,
                   9141:21):                                                           &Apache::lonnet::declutter($env{'request.noversionuri'}),
                   9142:21):                                                           '0.deeplink');
                   9143:21):                     } else {
                   9144:21):                         $check_login_symb = 1;
                   9145:21):                     }
                   9146:21):                 } else {
                   9147:21):                     my $symb=&Apache::lonnet::symbread();
                   9148:21):                     if ($symb) {
                   9149:21):                         $deeplink = &Apache::lonnet::EXT('resource.0.deeplink',$symb);
                   9150:21):                     } else {
                   9151:21):                         $check_login_symb = 1;
                   9152:21):                     }
                   9153:21):                 }
                   9154:21):             } else {
                   9155:21):                 $check_login_symb = 1;
                   9156:21):             }
                   9157:21):             if ($check_login_symb) {
                   9158:21):                 $deeplink_symb = &deeplink_login_symb($cnum,$cdom);
                   9159:21):                 if ($deeplink_symb =~ /\.(page|sequence)$/) {
                   9160:21):                     my $mapname = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($deeplink_symb))[2]);
                   9161:21):                     my $navmap = Apache::lonnavmaps::navmap->new();
                   9162:21):                     if (ref($navmap)) {
                   9163:21):                         $deeplink = $navmap->get_mapparam(undef,$mapname,'0.deeplink');
                   9164:21):                     }
                   9165:21):                 } else {
                   9166:21):                     $deeplink = &Apache::lonnet::EXT('resource.0.deeplink',$deeplink_symb);
                   9167:21):                 }
                   9168:21):             }
                   9169:21):             if ($deeplink ne '') {
          .6(raebu 9170:22):                 my ($state,$others,$listed,$scope,$protect,$display,$target) = split(/,/,$deeplink);
          .1(raebu 9171:21):                 if ($display =~ /^\d+$/) {
                   9172:21):                     $deeplinkmenu = 1;
                   9173:21):                     $menucoll = $display;
                   9174:21):                 }
                   9175:21):             }
                   9176:21):         }
                   9177:21):         if ($menucoll) {
                   9178:21):             %menu = &page_menu($env{'course.'.$env{'request.course.id'}.'.menucollections'},$menucoll);
                   9179:21):         }
                   9180:21):     }
                   9181:21):     return ($menucoll,$deeplinkmenu,\%menu);
                   9182:21): }
                   9183:21): 
                   9184:21): sub deeplink_login_symb {
                   9185:21):     my ($cnum,$cdom) = @_;
                   9186:21):     my $login_symb;
                   9187:21):     if ($env{'request.deeplink.login'}) {
                   9188:21):         $login_symb = &symb_from_tinyurl($env{'request.deeplink.login'},$cnum,$cdom);
                   9189:21):     }
                   9190:21):     return $login_symb;
                   9191:21): }
                   9192:21): 
                   9193:21): sub symb_from_tinyurl {
                   9194:21):     my ($url,$cnum,$cdom) = @_;
                   9195:21):     if ($url =~ m{^\Q/tiny/$cdom/\E(\w+)$}) {
                   9196:21):         my $key = $1;
                   9197:21):         my ($tinyurl,$login);
                   9198:21):         my ($result,$cached)=&Apache::lonnet::is_cached_new('tiny',$cdom."\0".$key);
                   9199:21):         if (defined($cached)) {
                   9200:21):             $tinyurl = $result;
                   9201:21):         } else {
                   9202:21):             my $configuname = &Apache::lonnet::get_domainconfiguser($cdom);
                   9203:21):             my %currtiny = &Apache::lonnet::get('tiny',[$key],$cdom,$configuname);
                   9204:21):             if ($currtiny{$key} ne '') {
                   9205:21):                 $tinyurl = $currtiny{$key};
                   9206:21):                 &Apache::lonnet::do_cache_new('tiny',$cdom."\0".$key,$currtiny{$key},600);
                   9207:21):             }
                   9208:21):         }
                   9209:21):         if ($tinyurl ne '') {
                   9210:21):             my ($cnumreq,$symb) = split(/\&/,$tinyurl);
                   9211:21):             if (wantarray) {
                   9212:21):                 return ($cnumreq,$symb);
                   9213:21):             } elsif ($cnumreq eq $cnum) {
                   9214:21):                 return $symb;
                   9215:21):             }
                   9216:21):         }
                   9217:21):     }
                   9218:21):     if (wantarray) {
                   9219:21):         return ();
                   9220:21):     } else {
                   9221:21):         return;
                   9222:21):     }
                   9223:21): }
                   9224:21): 
          .17(raeb 9225:-23): sub usable_exttools {
                   9226:-23):     my %tooltypes;
                   9227:-23):     if ($env{'request.course.id'}) {
                   9228:-23):         if ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'}) {
                   9229:-23):            if ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'} eq 'both') {
                   9230:-23):                %tooltypes = (
                   9231:-23):                              crs => 1,
                   9232:-23):                              dom => 1,
                   9233:-23):                             );
                   9234:-23):            } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'} eq 'crs') {
                   9235:-23):                $tooltypes{'crs'} = 1;
                   9236:-23):            } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'} eq 'dom') {
                   9237:-23):                $tooltypes{'dom'} = 1;
                   9238:-23):            }
                   9239:-23):         } else {
                   9240:-23):             my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   9241:-23):             my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
                   9242:-23):             my $crstype = lc($env{'course.'.$env{'request.course.id'}.'.type'});
                   9243:-23):             if ($crstype eq '') {
                   9244:-23):                 $crstype = 'course';
                   9245:-23):             }
                   9246:-23):             if ($crstype eq 'course') {
                   9247:-23):                 if ($env{'course.'.$env{'request.course.id'}.'internal.coursecode'}) {
                   9248:-23):                     $crstype = 'official';
                   9249:-23):                 } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.textbook'}) {
                   9250:-23):                     $crstype = 'textbook';
                   9251:-23):                 } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.lti'}) {
                   9252:-23):                     $crstype = 'lti';
                   9253:-23):                 } else {
                   9254:-23):                     $crstype = 'unofficial';
                   9255:-23):                 }
                   9256:-23):             }
                   9257:-23):             my %domdefaults = &Apache::lonnet::get_domain_defaults($cdom);
                   9258:-23):             if ($domdefaults{$crstype.'domexttool'}) {
                   9259:-23):                 $tooltypes{'dom'} = 1;
                   9260:-23):             }
                   9261:-23):             if ($domdefaults{$crstype.'exttool'}) {
                   9262:-23):                 $tooltypes{'crs'} = 1;
                   9263:-23):             }
                   9264:-23):         }
                   9265:-23):     }
                   9266:-23):     return %tooltypes;
                   9267:-23): }
                   9268:-23): 
1.1034    www      9269: sub wishlist_window {
                   9270:     return(<<'ENDWISHLIST');
1.1046    raeburn  9271: <script type="text/javascript">
1.1034    www      9272: // <![CDATA[
                   9273: // <!-- BEGIN LON-CAPA Internal
                   9274: function set_wishlistlink(title, path) {
                   9275:     if (!title) {
                   9276:         title = document.title;
                   9277:         title = title.replace(/^LON-CAPA /,'');
                   9278:     }
1.1075.2.65  raeburn  9279:     title = encodeURIComponent(title);
1.1075.2.83  raeburn  9280:     title = title.replace("'","\\\'");
1.1034    www      9281:     if (!path) {
                   9282:         path = location.pathname;
                   9283:     }
1.1075.2.65  raeburn  9284:     path = encodeURIComponent(path);
1.1075.2.83  raeburn  9285:     path = path.replace("'","\\\'");
1.1034    www      9286:     Win = window.open('/adm/wishlist?mode=newLink&setTitle='+title+'&setPath='+path,
                   9287:                       'wishlistNewLink','width=560,height=350,scrollbars=0');
                   9288: }
                   9289: // END LON-CAPA Internal -->
                   9290: // ]]>
                   9291: </script>
                   9292: ENDWISHLIST
                   9293: }
                   9294: 
1.1030    www      9295: sub modal_window {
                   9296:     return(<<'ENDMODAL');
1.1046    raeburn  9297: <script type="text/javascript">
1.1030    www      9298: // <![CDATA[
                   9299: // <!-- BEGIN LON-CAPA Internal
                   9300: var modalWindow = {
                   9301: 	parent:"body",
                   9302: 	windowId:null,
                   9303: 	content:null,
                   9304: 	width:null,
                   9305: 	height:null,
                   9306: 	close:function()
                   9307: 	{
                   9308: 	        $(".LCmodal-window").remove();
                   9309: 	        $(".LCmodal-overlay").remove();
                   9310: 	},
                   9311: 	open:function()
                   9312: 	{
                   9313: 		var modal = "";
                   9314: 		modal += "<div class=\"LCmodal-overlay\"></div>";
                   9315: 		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;\">";
                   9316: 		modal += this.content;
                   9317: 		modal += "</div>";	
                   9318: 
                   9319: 		$(this.parent).append(modal);
                   9320: 
                   9321: 		$(".LCmodal-window").append("<a class=\"LCclose-window\"></a>");
                   9322: 		$(".LCclose-window").click(function(){modalWindow.close();});
                   9323: 		$(".LCmodal-overlay").click(function(){modalWindow.close();});
                   9324: 	}
                   9325: };
1.1075.2.42  raeburn  9326: 	var openMyModal = function(source,width,height,scrolling,transparency,style)
1.1030    www      9327: 	{
1.1075.2.119  raeburn  9328:                 source = source.replace(/'/g,"&#39;");
1.1030    www      9329: 		modalWindow.windowId = "myModal";
                   9330: 		modalWindow.width = width;
                   9331: 		modalWindow.height = height;
1.1075.2.80  raeburn  9332: 		modalWindow.content = "<iframe width='"+width+"' height='"+height+"' frameborder='0' scrolling='"+scrolling+"' allowtransparency='"+transparency+"' src='" + source + "' style='"+style+"'></iframe>";
1.1030    www      9333: 		modalWindow.open();
1.1075.2.87  raeburn  9334: 	};
1.1030    www      9335: // END LON-CAPA Internal -->
                   9336: // ]]>
                   9337: </script>
                   9338: ENDMODAL
                   9339: }
                   9340: 
                   9341: sub modal_link {
1.1075.2.42  raeburn  9342:     my ($link,$linktext,$width,$height,$target,$scrolling,$title,$transparency,$style)=@_;
1.1030    www      9343:     unless ($width) { $width=480; }
                   9344:     unless ($height) { $height=400; }
1.1031    www      9345:     unless ($scrolling) { $scrolling='yes'; }
1.1075.2.42  raeburn  9346:     unless ($transparency) { $transparency='true'; }
                   9347: 
1.1074    raeburn  9348:     my $target_attr;
                   9349:     if (defined($target)) {
                   9350:         $target_attr = 'target="'.$target.'"';
                   9351:     }
                   9352:     return <<"ENDLINK";
1.1075.2.143  raeburn  9353: <a href="$link" $target_attr title="$title" onclick="javascript:openMyModal('$link',$width,$height,'$scrolling','$transparency','$style'); return false;">$linktext</a>
1.1074    raeburn  9354: ENDLINK
1.1030    www      9355: }
                   9356: 
1.1032    www      9357: sub modal_adhoc_script {
1.1075.2.155  raeburn  9358:     my ($funcname,$width,$height,$content,$possmathjax)=@_;
                   9359:     my $mathjax;
                   9360:     if ($possmathjax) {
                   9361:         $mathjax = <<'ENDJAX';
                   9362:                if (typeof MathJax == 'object') {
                   9363:                    MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
                   9364:                }
                   9365: ENDJAX
                   9366:     }
1.1032    www      9367:     return (<<ENDADHOC);
1.1046    raeburn  9368: <script type="text/javascript">
1.1032    www      9369: // <![CDATA[
                   9370:         var $funcname = function()
                   9371:         {
                   9372:                 modalWindow.windowId = "myModal";
                   9373:                 modalWindow.width = $width;
                   9374:                 modalWindow.height = $height;
                   9375:                 modalWindow.content = '$content';
                   9376:                 modalWindow.open();
1.1075.2.155  raeburn  9377:                 $mathjax
1.1032    www      9378:         };  
                   9379: // ]]>
                   9380: </script>
                   9381: ENDADHOC
                   9382: }
                   9383: 
1.1041    www      9384: sub modal_adhoc_inner {
1.1075.2.155  raeburn  9385:     my ($funcname,$width,$height,$content,$possmathjax)=@_;
1.1041    www      9386:     my $innerwidth=$width-20;
                   9387:     $content=&js_ready(
1.1042    www      9388:                &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}).
1.1075.2.42  raeburn  9389:                  &start_scrollbox($width.'px',$innerwidth.'px',$height.'px','myModal','#FFFFFF',undef,1).
                   9390:                  $content.
1.1041    www      9391:                  &end_scrollbox().
1.1075.2.42  raeburn  9392:                  &end_page()
1.1041    www      9393:              );
1.1075.2.155  raeburn  9394:     return &modal_adhoc_script($funcname,$width,$height,$content,$possmathjax);
1.1041    www      9395: }
                   9396: 
                   9397: sub modal_adhoc_window {
1.1075.2.155  raeburn  9398:     my ($funcname,$width,$height,$content,$linktext,$possmathjax)=@_;
                   9399:     return &modal_adhoc_inner($funcname,$width,$height,$content,$possmathjax).
1.1041    www      9400:            "<a href=\"javascript:$funcname();void(0);\">".$linktext."</a>";
                   9401: }
                   9402: 
                   9403: sub modal_adhoc_launch {
                   9404:     my ($funcname,$width,$height,$content)=@_;
                   9405:     return &modal_adhoc_inner($funcname,$width,$height,$content).(<<ENDLAUNCH);
                   9406: <script type="text/javascript">
                   9407: // <![CDATA[
                   9408: $funcname();
                   9409: // ]]>
                   9410: </script>
                   9411: ENDLAUNCH
                   9412: }
                   9413: 
                   9414: sub modal_adhoc_close {
                   9415:     return (<<ENDCLOSE);
                   9416: <script type="text/javascript">
                   9417: // <![CDATA[
                   9418: modalWindow.close();
                   9419: // ]]>
                   9420: </script>
                   9421: ENDCLOSE
                   9422: }
                   9423: 
1.1038    www      9424: sub togglebox_script {
                   9425:    return(<<ENDTOGGLE);
                   9426: <script type="text/javascript"> 
                   9427: // <![CDATA[
                   9428: function LCtoggleDisplay(id,hidetext,showtext) {
                   9429:    link = document.getElementById(id + "link").childNodes[0];
                   9430:    with (document.getElementById(id).style) {
                   9431:       if (display == "none" ) {
                   9432:           display = "inline";
                   9433:           link.nodeValue = hidetext;
                   9434:         } else {
                   9435:           display = "none";
                   9436:           link.nodeValue = showtext;
                   9437:        }
                   9438:    }
                   9439: }
                   9440: // ]]>
                   9441: </script>
                   9442: ENDTOGGLE
                   9443: }
                   9444: 
1.1039    www      9445: sub start_togglebox {
                   9446:     my ($id,$heading,$headerbg,$hidetext,$showtext)=@_;
                   9447:     unless ($heading) { $heading=''; } else { $heading.=' '; }
                   9448:     unless ($showtext) { $showtext=&mt('show'); }
                   9449:     unless ($hidetext) { $hidetext=&mt('hide'); }
                   9450:     unless ($headerbg) { $headerbg='#FFFFFF'; }
                   9451:     return &start_data_table().
                   9452:            &start_data_table_header_row().
                   9453:            '<td bgcolor="'.$headerbg.'">'.$heading.
                   9454:            '[<a id="'.$id.'link" href="javascript:LCtoggleDisplay(\''.$id.'\',\''.$hidetext.'\',\''.
                   9455:            $showtext.'\')">'.$showtext.'</a>]</td>'.
                   9456:            &end_data_table_header_row().
                   9457:            '<tr id="'.$id.'" style="display:none""><td>';
                   9458: }
                   9459: 
                   9460: sub end_togglebox {
                   9461:     return '</td></tr>'.&end_data_table();
                   9462: }
                   9463: 
1.1041    www      9464: sub LCprogressbar_script {
1.1075.2.130  raeburn  9465:    my ($id,$number_to_do)=@_;
                   9466:    if ($number_to_do) {
                   9467:        return(<<ENDPROGRESS);
1.1041    www      9468: <script type="text/javascript">
                   9469: // <![CDATA[
1.1045    www      9470: \$('#progressbar$id').progressbar({
1.1041    www      9471:   value: 0,
                   9472:   change: function(event, ui) {
                   9473:     var newVal = \$(this).progressbar('option', 'value');
                   9474:     \$('.pblabel', this).text(LCprogressTxt);
                   9475:   }
                   9476: });
                   9477: // ]]>
                   9478: </script>
                   9479: ENDPROGRESS
1.1075.2.130  raeburn  9480:    } else {
                   9481:        return(<<ENDPROGRESS);
                   9482: <script type="text/javascript">
                   9483: // <![CDATA[
                   9484: \$('#progressbar$id').progressbar({
                   9485:   value: false,
                   9486:   create: function(event, ui) {
                   9487:     \$('.ui-widget-header', this).css({'background':'#F0F0F0'});
                   9488:     \$('.ui-progressbar-overlay', this).css({'margin':'0'});
                   9489:   }
                   9490: });
                   9491: // ]]>
                   9492: </script>
                   9493: ENDPROGRESS
                   9494:    }
1.1041    www      9495: }
                   9496: 
                   9497: sub LCprogressbarUpdate_script {
                   9498:    return(<<ENDPROGRESSUPDATE);
                   9499: <style type="text/css">
                   9500: .ui-progressbar { position:relative; }
1.1075.2.130  raeburn  9501: .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      9502: .pblabel { position: absolute; width: 100%; text-align: center; line-height: 1.9em; }
                   9503: </style>
                   9504: <script type="text/javascript">
                   9505: // <![CDATA[
1.1045    www      9506: var LCprogressTxt='---';
                   9507: 
1.1075.2.130  raeburn  9508: function LCupdateProgress(percent,progresstext,id,maxnum) {
1.1041    www      9509:    LCprogressTxt=progresstext;
1.1075.2.130  raeburn  9510:    if ((maxnum == '') || (maxnum == undefined) || (maxnum == null)) {
                   9511:        \$('#progressbar'+id).find('.progress-label').text(LCprogressTxt);
                   9512:    } else if (percent === \$('#progressbar'+id).progressbar( "value" )) {
                   9513:        \$('#progressbar'+id).find('.pblabel').text(LCprogressTxt);
                   9514:    } else {
                   9515:        \$('#progressbar'+id).progressbar('value',percent);
                   9516:    }
1.1041    www      9517: }
                   9518: // ]]>
                   9519: </script>
                   9520: ENDPROGRESSUPDATE
                   9521: }
                   9522: 
1.1042    www      9523: my $LClastpercent;
1.1045    www      9524: my $LCidcnt;
                   9525: my $LCcurrentid;
1.1042    www      9526: 
1.1041    www      9527: sub LCprogressbar {
1.1075.2.130  raeburn  9528:     my ($r,$number_to_do,$preamble)=@_;
1.1042    www      9529:     $LClastpercent=0;
1.1045    www      9530:     $LCidcnt++;
                   9531:     $LCcurrentid=$$.'_'.$LCidcnt;
1.1075.2.130  raeburn  9532:     my ($starting,$content);
                   9533:     if ($number_to_do) {
                   9534:         $starting=&mt('Starting');
                   9535:         $content=(<<ENDPROGBAR);
                   9536: $preamble
1.1045    www      9537:   <div id="progressbar$LCcurrentid">
1.1041    www      9538:     <span class="pblabel">$starting</span>
                   9539:   </div>
                   9540: ENDPROGBAR
1.1075.2.130  raeburn  9541:     } else {
                   9542:         $starting=&mt('Loading...');
                   9543:         $LClastpercent='false';
                   9544:         $content=(<<ENDPROGBAR);
                   9545: $preamble
                   9546:   <div id="progressbar$LCcurrentid">
                   9547:       <div class="progress-label">$starting</div>
                   9548:   </div>
                   9549: ENDPROGBAR
                   9550:     }
                   9551:     &r_print($r,$content.&LCprogressbar_script($LCcurrentid,$number_to_do));
1.1041    www      9552: }
                   9553: 
                   9554: sub LCprogressbarUpdate {
1.1075.2.130  raeburn  9555:     my ($r,$val,$text,$number_to_do)=@_;
                   9556:     if ($number_to_do) {
                   9557:         unless ($val) { 
                   9558:             if ($LClastpercent) {
                   9559:                 $val=$LClastpercent;
                   9560:             } else {
                   9561:                 $val=0;
                   9562:             }
                   9563:         }
                   9564:         if ($val<0) { $val=0; }
                   9565:         if ($val>100) { $val=0; }
                   9566:         $LClastpercent=$val;
                   9567:         unless ($text) { $text=$val.'%'; }
                   9568:     } else {
                   9569:         $val = 'false';
1.1042    www      9570:     }
1.1041    www      9571:     $text=&js_ready($text);
1.1044    www      9572:     &r_print($r,<<ENDUPDATE);
1.1041    www      9573: <script type="text/javascript">
                   9574: // <![CDATA[
1.1075.2.130  raeburn  9575: LCupdateProgress($val,'$text','$LCcurrentid','$number_to_do');
1.1041    www      9576: // ]]>
                   9577: </script>
                   9578: ENDUPDATE
1.1035    www      9579: }
                   9580: 
1.1042    www      9581: sub LCprogressbarClose {
                   9582:     my ($r)=@_;
                   9583:     $LClastpercent=0;
1.1044    www      9584:     &r_print($r,<<ENDCLOSE);
1.1042    www      9585: <script type="text/javascript">
                   9586: // <![CDATA[
1.1045    www      9587: \$("#progressbar$LCcurrentid").hide('slow'); 
1.1042    www      9588: // ]]>
                   9589: </script>
                   9590: ENDCLOSE
1.1044    www      9591: }
                   9592: 
                   9593: sub r_print {
                   9594:     my ($r,$to_print)=@_;
                   9595:     if ($r) {
                   9596:       $r->print($to_print);
                   9597:       $r->rflush();
                   9598:     } else {
                   9599:       print($to_print);
                   9600:     }
1.1042    www      9601: }
                   9602: 
1.320     albertel 9603: sub html_encode {
                   9604:     my ($result) = @_;
                   9605: 
1.322     albertel 9606:     $result = &HTML::Entities::encode($result,'<>&"');
1.320     albertel 9607:     
                   9608:     return $result;
                   9609: }
1.1044    www      9610: 
1.317     albertel 9611: sub js_ready {
                   9612:     my ($result) = @_;
                   9613: 
1.323     albertel 9614:     $result =~ s/[\n\r]/ /xmsg;
                   9615:     $result =~ s/\\/\\\\/xmsg;
                   9616:     $result =~ s/'/\\'/xmsg;
1.372     albertel 9617:     $result =~ s{</}{<\\/}xmsg;
1.317     albertel 9618:     
                   9619:     return $result;
                   9620: }
                   9621: 
1.315     albertel 9622: sub validate_page {
                   9623:     if (  exists($env{'internal.start_page'})
1.316     albertel 9624: 	  &&     $env{'internal.start_page'} > 1) {
                   9625: 	&Apache::lonnet::logthis('start_page called multiple times '.
1.318     albertel 9626: 				 $env{'internal.start_page'}.' '.
1.316     albertel 9627: 				 $ENV{'request.filename'});
1.315     albertel 9628:     }
                   9629:     if (  exists($env{'internal.end_page'})
1.316     albertel 9630: 	  &&     $env{'internal.end_page'} > 1) {
                   9631: 	&Apache::lonnet::logthis('end_page called multiple times '.
1.318     albertel 9632: 				 $env{'internal.end_page'}.' '.
1.316     albertel 9633: 				 $env{'request.filename'});
1.315     albertel 9634:     }
                   9635:     if (     exists($env{'internal.start_page'})
                   9636: 	&& ! exists($env{'internal.end_page'})) {
1.316     albertel 9637: 	&Apache::lonnet::logthis('start_page called without end_page '.
                   9638: 				 $env{'request.filename'});
1.315     albertel 9639:     }
                   9640:     if (   ! exists($env{'internal.start_page'})
                   9641: 	&&   exists($env{'internal.end_page'})) {
1.316     albertel 9642: 	&Apache::lonnet::logthis('end_page called without start_page'.
                   9643: 				 $env{'request.filename'});
1.315     albertel 9644:     }
1.306     albertel 9645: }
1.315     albertel 9646: 
1.996     www      9647: 
                   9648: sub start_scrollbox {
1.1075.2.56  raeburn  9649:     my ($outerwidth,$width,$height,$id,$bgcolor,$cursor,$needjsready) = @_;
1.998     raeburn  9650:     unless ($outerwidth) { $outerwidth='520px'; }
                   9651:     unless ($width) { $width='500px'; }
                   9652:     unless ($height) { $height='200px'; }
1.1075    raeburn  9653:     my ($table_id,$div_id,$tdcol);
1.1018    raeburn  9654:     if ($id ne '') {
1.1075.2.42  raeburn  9655:         $table_id = ' id="table_'.$id.'"';
                   9656:         $div_id = ' id="div_'.$id.'"';
1.1018    raeburn  9657:     }
1.1075    raeburn  9658:     if ($bgcolor ne '') {
                   9659:         $tdcol = "background-color: $bgcolor;";
                   9660:     }
1.1075.2.42  raeburn  9661:     my $nicescroll_js;
                   9662:     if ($env{'browser.mobile'}) {
                   9663:         $nicescroll_js = &nicescroll_javascript('div_'.$id,$cursor,$needjsready);
                   9664:     }
1.1075    raeburn  9665:     return <<"END";
1.1075.2.42  raeburn  9666: $nicescroll_js
                   9667: 
                   9668: <table style="width: $outerwidth; border: 1px solid none;"$table_id><tr><td style="width: $width;$tdcol">
1.1075.2.56  raeburn  9669: <div style="overflow:auto; width:$width; height:$height;"$div_id>
1.1075    raeburn  9670: END
1.996     www      9671: }
                   9672: 
                   9673: sub end_scrollbox {
1.1036    www      9674:     return '</div></td></tr></table>';
1.996     www      9675: }
                   9676: 
1.1075.2.42  raeburn  9677: sub nicescroll_javascript {
                   9678:     my ($id,$cursor,$needjsready,$framecheck,$location) = @_;
                   9679:     my %options;
                   9680:     if (ref($cursor) eq 'HASH') {
                   9681:         %options = %{$cursor};
                   9682:     }
                   9683:     unless ($options{'railalign'} =~ /^left|right$/) {
                   9684:         $options{'railalign'} = 'left';
                   9685:     }
                   9686:     unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
                   9687:         my $function  = &get_users_function();
                   9688:         $options{'cursorcolor'} = &designparm($function.'.sidebg',$env{'request.role.domain'});
                   9689:         unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
                   9690:             $options{'cursorcolor'} = '#00F';
                   9691:         }
                   9692:     }
                   9693:     if ($options{'cursoropacity'} =~ /^[\d.]+$/) {
                   9694:         unless ($options{'cursoropacity'} >= 0.0 && $options{'cursoropacity'} <=1.0) {
                   9695:             $options{'cursoropacity'}='1.0';
                   9696:         }
                   9697:     } else {
                   9698:         $options{'cursoropacity'}='1.0';
                   9699:     }
                   9700:     if ($options{'cursorfixedheight'} eq 'none') {
                   9701:         delete($options{'cursorfixedheight'});
                   9702:     } else {
                   9703:         unless ($options{'cursorfixedheight'} =~ /^\d+$/) { $options{'cursorfixedheight'}='50'; }
                   9704:     }
                   9705:     unless ($options{'railoffset'} =~ /^{[\w\:\d\-,]+}$/) {
                   9706:         delete($options{'railoffset'});
                   9707:     }
                   9708:     my @niceoptions;
                   9709:     while (my($key,$value) = each(%options)) {
                   9710:         if ($value =~ /^\{.+\}$/) {
                   9711:             push(@niceoptions,$key.':'.$value);
                   9712:         } else {
                   9713:             push(@niceoptions,$key.':"'.$value.'"');
                   9714:         }
                   9715:     }
                   9716:     my $nicescroll_js = '
                   9717: $(document).ready(
                   9718:       function() {
                   9719:           $("#'.$id.'").niceScroll({'.join(',',@niceoptions).'});
                   9720:       }
                   9721: );
                   9722: ';
                   9723:     if ($framecheck) {
                   9724:         $nicescroll_js .= '
                   9725: function expand_div(caller) {
                   9726:     if (top === self) {
                   9727:         document.getElementById("'.$id.'").style.width = "auto";
                   9728:         document.getElementById("'.$id.'").style.height = "auto";
                   9729:     } else {
                   9730:         try {
                   9731:             if (parent.frames) {
                   9732:                 if (parent.frames.length > 1) {
                   9733:                     var framesrc = parent.frames[1].location.href;
                   9734:                     var currsrc = framesrc.replace(/\#.*$/,"");
                   9735:                     if ((caller == "search") || (currsrc == "'.$location.'")) {
                   9736:                         document.getElementById("'.$id.'").style.width = "auto";
                   9737:                         document.getElementById("'.$id.'").style.height = "auto";
                   9738:                     }
                   9739:                 }
                   9740:             }
                   9741:         } catch (e) {
                   9742:             return;
                   9743:         }
                   9744:     }
                   9745:     return;
                   9746: }
                   9747: ';
                   9748:     }
                   9749:     if ($needjsready) {
                   9750:         $nicescroll_js = '
                   9751: <script type="text/javascript">'."\n".$nicescroll_js."\n</script>\n";
                   9752:     } else {
                   9753:         $nicescroll_js = &Apache::lonhtmlcommon::scripttag($nicescroll_js);
                   9754:     }
                   9755:     return $nicescroll_js;
                   9756: }
                   9757: 
1.318     albertel 9758: sub simple_error_page {
1.1075.2.49  raeburn  9759:     my ($r,$title,$msg,$args) = @_;
1.1075.2.161.  .4(raebu 9760:22):     my %displayargs;
1.1075.2.49  raeburn  9761:     if (ref($args) eq 'HASH') {
                   9762:         if (!$args->{'no_auto_mt_msg'}) { $msg = &mt($msg); }
1.1075.2.161.  .4(raebu 9763:22):         if ($args->{'only_body'}) {
                   9764:22):             $displayargs{'only_body'} = 1;
                   9765:22):         }
                   9766:22):         if ($args->{'no_nav_bar'}) {
                   9767:22):             $displayargs{'no_nav_bar'} = 1;
                   9768:22):         }
1.1075.2.49  raeburn  9769:     } else {
                   9770:         $msg = &mt($msg);
                   9771:     }
                   9772: 
1.318     albertel 9773:     my $page =
1.1075.2.161.  .4(raebu 9774:22): 	&Apache::loncommon::start_page($title,'',\%displayargs).
1.1075.2.49  raeburn  9775: 	'<p class="LC_error">'.$msg.'</p>'.
1.318     albertel 9776: 	&Apache::loncommon::end_page();
                   9777:     if (ref($r)) {
                   9778: 	$r->print($page);
1.327     albertel 9779: 	return;
1.318     albertel 9780:     }
                   9781:     return $page;
                   9782: }
1.347     albertel 9783: 
                   9784: {
1.610     albertel 9785:     my @row_count;
1.961     onken    9786: 
                   9787:     sub start_data_table_count {
                   9788:         unshift(@row_count, 0);
                   9789:         return;
                   9790:     }
                   9791: 
                   9792:     sub end_data_table_count {
                   9793:         shift(@row_count);
                   9794:         return;
                   9795:     }
                   9796: 
1.347     albertel 9797:     sub start_data_table {
1.1018    raeburn  9798: 	my ($add_class,$id) = @_;
1.422     albertel 9799: 	my $css_class = (join(' ','LC_data_table',$add_class));
1.1018    raeburn  9800:         my $table_id;
                   9801:         if (defined($id)) {
                   9802:             $table_id = ' id="'.$id.'"';
                   9803:         }
1.961     onken    9804: 	&start_data_table_count();
1.1018    raeburn  9805: 	return '<table class="'.$css_class.'"'.$table_id.'>'."\n";
1.347     albertel 9806:     }
                   9807: 
                   9808:     sub end_data_table {
1.961     onken    9809: 	&end_data_table_count();
1.389     albertel 9810: 	return '</table>'."\n";;
1.347     albertel 9811:     }
                   9812: 
                   9813:     sub start_data_table_row {
1.974     wenzelju 9814: 	my ($add_class, $id) = @_;
1.610     albertel 9815: 	$row_count[0]++;
                   9816: 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.900     bisitz   9817: 	$css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
1.974     wenzelju 9818:         $id = (' id="'.$id.'"') unless ($id eq '');
                   9819:         return  '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.347     albertel 9820:     }
1.471     banghart 9821:     
                   9822:     sub continue_data_table_row {
1.974     wenzelju 9823: 	my ($add_class, $id) = @_;
1.610     albertel 9824: 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.974     wenzelju 9825: 	$css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
                   9826:         $id = (' id="'.$id.'"') unless ($id eq '');
                   9827:         return  '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.471     banghart 9828:     }
1.347     albertel 9829: 
                   9830:     sub end_data_table_row {
1.389     albertel 9831: 	return '</tr>'."\n";;
1.347     albertel 9832:     }
1.367     www      9833: 
1.421     albertel 9834:     sub start_data_table_empty_row {
1.707     bisitz   9835: #	$row_count[0]++;
1.421     albertel 9836: 	return  '<tr class="LC_empty_row" >'."\n";;
                   9837:     }
                   9838: 
                   9839:     sub end_data_table_empty_row {
                   9840: 	return '</tr>'."\n";;
                   9841:     }
                   9842: 
1.367     www      9843:     sub start_data_table_header_row {
1.389     albertel 9844: 	return  '<tr class="LC_header_row">'."\n";;
1.367     www      9845:     }
                   9846: 
                   9847:     sub end_data_table_header_row {
1.389     albertel 9848: 	return '</tr>'."\n";;
1.367     www      9849:     }
1.890     droeschl 9850: 
                   9851:     sub data_table_caption {
                   9852:         my $caption = shift;
                   9853:         return "<caption class=\"LC_caption\">$caption</caption>";
                   9854:     }
1.347     albertel 9855: }
                   9856: 
1.548     albertel 9857: =pod
                   9858: 
                   9859: =item * &inhibit_menu_check($arg)
                   9860: 
                   9861: Checks for a inhibitmenu state and generates output to preserve it
                   9862: 
                   9863: Inputs:         $arg - can be any of
                   9864:                      - undef - in which case the return value is a string 
                   9865:                                to add  into arguments list of a uri
                   9866:                      - 'input' - in which case the return value is a HTML
                   9867:                                  <form> <input> field of type hidden to
                   9868:                                  preserve the value
                   9869:                      - a url - in which case the return value is the url with
                   9870:                                the neccesary cgi args added to preserve the
                   9871:                                inhibitmenu state
                   9872:                      - a ref to a url - no return value, but the string is
                   9873:                                         updated to include the neccessary cgi
                   9874:                                         args to preserve the inhibitmenu state
                   9875: 
                   9876: =cut
                   9877: 
                   9878: sub inhibit_menu_check {
                   9879:     my ($arg) = @_;
                   9880:     &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
                   9881:     if ($arg eq 'input') {
                   9882: 	if ($env{'form.inhibitmenu'}) {
                   9883: 	    return '<input type="hidden" name="inhibitmenu" value="'.$env{'form.inhibitmenu'}.'" />';
                   9884: 	} else {
                   9885: 	    return
                   9886: 	}
                   9887:     }
                   9888:     if ($env{'form.inhibitmenu'}) {
                   9889: 	if (ref($arg)) {
                   9890: 	    $$arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   9891: 	} elsif ($arg eq '') {
                   9892: 	    $arg .= 'inhibitmenu='.$env{'form.inhibitmenu'};
                   9893: 	} else {
                   9894: 	    $arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   9895: 	}
                   9896:     }
                   9897:     if (!ref($arg)) {
                   9898: 	return $arg;
                   9899:     }
                   9900: }
                   9901: 
1.251     albertel 9902: ###############################################
1.182     matthew  9903: 
                   9904: =pod
                   9905: 
1.549     albertel 9906: =back
                   9907: 
                   9908: =head1 User Information Routines
                   9909: 
                   9910: =over 4
                   9911: 
1.405     albertel 9912: =item * &get_users_function()
1.182     matthew  9913: 
                   9914: Used by &bodytag to determine the current users primary role.
                   9915: Returns either 'student','coordinator','admin', or 'author'.
                   9916: 
                   9917: =cut
                   9918: 
                   9919: ###############################################
                   9920: sub get_users_function {
1.815     tempelho 9921:     my $function = 'norole';
1.818     tempelho 9922:     if ($env{'request.role'}=~/^(st)/) {
                   9923:         $function='student';
                   9924:     }
1.907     raeburn  9925:     if ($env{'request.role'}=~/^(cc|co|in|ta|ep)/) {
1.182     matthew  9926:         $function='coordinator';
                   9927:     }
1.258     albertel 9928:     if ($env{'request.role'}=~/^(su|dc|ad|li)/) {
1.182     matthew  9929:         $function='admin';
                   9930:     }
1.826     bisitz   9931:     if (($env{'request.role'}=~/^(au|ca|aa)/) ||
1.1025    raeburn  9932:         ($ENV{'REQUEST_URI'}=~ m{/^(/priv)})) {
1.182     matthew  9933:         $function='author';
                   9934:     }
                   9935:     return $function;
1.54      www      9936: }
1.99      www      9937: 
                   9938: ###############################################
                   9939: 
1.233     raeburn  9940: =pod
                   9941: 
1.821     raeburn  9942: =item * &show_course()
                   9943: 
                   9944: Used by lonmenu.pm and lonroles.pm to determine whether to use the word
                   9945: 'Courses' or 'Roles' in inline navigation and on screen displaying user's roles.
                   9946: 
                   9947: Inputs:
                   9948: None
                   9949: 
                   9950: Outputs:
                   9951: Scalar: 1 if 'Course' to be used, 0 otherwise.
                   9952: 
                   9953: =cut
                   9954: 
                   9955: ###############################################
                   9956: sub show_course {
                   9957:     my $course = !$env{'user.adv'};
                   9958:     if (!$env{'user.adv'}) {
                   9959:         foreach my $env (keys(%env)) {
                   9960:             next if ($env !~ m/^user\.priv\./);
                   9961:             if ($env !~ m/^user\.priv\.(?:st|cm)/) {
                   9962:                 $course = 0;
                   9963:                 last;
                   9964:             }
                   9965:         }
                   9966:     }
                   9967:     return $course;
                   9968: }
                   9969: 
                   9970: ###############################################
                   9971: 
                   9972: =pod
                   9973: 
1.542     raeburn  9974: =item * &check_user_status()
1.274     raeburn  9975: 
                   9976: Determines current status of supplied role for a
                   9977: specific user. Roles can be active, previous or future.
                   9978: 
                   9979: Inputs: 
                   9980: user's domain, user's username, course's domain,
1.375     raeburn  9981: course's number, optional section ID.
1.274     raeburn  9982: 
                   9983: Outputs:
                   9984: role status: active, previous or future. 
                   9985: 
                   9986: =cut
                   9987: 
                   9988: sub check_user_status {
1.412     raeburn  9989:     my ($udom,$uname,$cdom,$crs,$role,$sec) = @_;
1.1073    raeburn  9990:     my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname);
1.1075.2.85  raeburn  9991:     my @uroles = keys(%userinfo);
1.274     raeburn  9992:     my $srchstr;
                   9993:     my $active_chk = 'none';
1.412     raeburn  9994:     my $now = time;
1.274     raeburn  9995:     if (@uroles > 0) {
1.908     raeburn  9996:         if (($role eq 'cc') || ($role eq 'co') || ($sec eq '') || (!defined($sec))) {
1.274     raeburn  9997:             $srchstr = '/'.$cdom.'/'.$crs.'_'.$role;
                   9998:         } else {
1.412     raeburn  9999:             $srchstr = '/'.$cdom.'/'.$crs.'/'.$sec.'_'.$role;
                   10000:         }
                   10001:         if (grep/^\Q$srchstr\E$/,@uroles) {
1.274     raeburn  10002:             my $role_end = 0;
                   10003:             my $role_start = 0;
                   10004:             $active_chk = 'active';
1.412     raeburn  10005:             if ($userinfo{$srchstr} =~ m/^\Q$role\E_(\d+)/) {
                   10006:                 $role_end = $1;
                   10007:                 if ($userinfo{$srchstr} =~ m/^\Q$role\E_\Q$role_end\E_(\d+)$/) {
                   10008:                     $role_start = $1;
1.274     raeburn  10009:                 }
                   10010:             }
                   10011:             if ($role_start > 0) {
1.412     raeburn  10012:                 if ($now < $role_start) {
1.274     raeburn  10013:                     $active_chk = 'future';
                   10014:                 }
                   10015:             }
                   10016:             if ($role_end > 0) {
1.412     raeburn  10017:                 if ($now > $role_end) {
1.274     raeburn  10018:                     $active_chk = 'previous';
                   10019:                 }
                   10020:             }
                   10021:         }
                   10022:     }
                   10023:     return $active_chk;
                   10024: }
                   10025: 
                   10026: ###############################################
                   10027: 
                   10028: =pod
                   10029: 
1.405     albertel 10030: =item * &get_sections()
1.233     raeburn  10031: 
                   10032: Determines all the sections for a course including
                   10033: sections with students and sections containing other roles.
1.419     raeburn  10034: Incoming parameters: 
                   10035: 
                   10036: 1. domain
                   10037: 2. course number 
                   10038: 3. reference to array containing roles for which sections should 
                   10039: be gathered (optional).
                   10040: 4. reference to array containing status types for which sections 
                   10041: should be gathered (optional).
                   10042: 
                   10043: If the third argument is undefined, sections are gathered for any role. 
                   10044: If the fourth argument is undefined, sections are gathered for any status.
                   10045: Permissible values are 'active' or 'future' or 'previous'.
1.233     raeburn  10046:  
1.374     raeburn  10047: Returns section hash (keys are section IDs, values are
                   10048: number of users in each section), subject to the
1.419     raeburn  10049: optional roles filter, optional status filter 
1.233     raeburn  10050: 
                   10051: =cut
                   10052: 
                   10053: ###############################################
                   10054: sub get_sections {
1.419     raeburn  10055:     my ($cdom,$cnum,$possible_roles,$possible_status) = @_;
1.366     albertel 10056:     if (!defined($cdom) || !defined($cnum)) {
                   10057:         my $cid =  $env{'request.course.id'};
                   10058: 
                   10059: 	return if (!defined($cid));
                   10060: 
                   10061:         $cdom = $env{'course.'.$cid.'.domain'};
                   10062:         $cnum = $env{'course.'.$cid.'.num'};
                   10063:     }
                   10064: 
                   10065:     my %sectioncount;
1.419     raeburn  10066:     my $now = time;
1.240     albertel 10067: 
1.1075.2.33  raeburn  10068:     my $check_students = 1;
                   10069:     my $only_students = 0;
                   10070:     if (ref($possible_roles) eq 'ARRAY') {
                   10071:         if (grep(/^st$/,@{$possible_roles})) {
                   10072:             if (@{$possible_roles} == 1) {
                   10073:                 $only_students = 1;
                   10074:             }
                   10075:         } else {
                   10076:             $check_students = 0;
                   10077:         }
                   10078:     }
                   10079: 
                   10080:     if ($check_students) {
1.276     albertel 10081: 	my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
1.240     albertel 10082: 	my $sec_index = &Apache::loncoursedata::CL_SECTION();
                   10083: 	my $status_index = &Apache::loncoursedata::CL_STATUS();
1.419     raeburn  10084:         my $start_index = &Apache::loncoursedata::CL_START();
                   10085:         my $end_index = &Apache::loncoursedata::CL_END();
                   10086:         my $status;
1.366     albertel 10087: 	while (my ($student,$data) = each(%$classlist)) {
1.419     raeburn  10088: 	    my ($section,$stu_status,$start,$end) = ($data->[$sec_index],
                   10089: 				                     $data->[$status_index],
                   10090:                                                      $data->[$start_index],
                   10091:                                                      $data->[$end_index]);
                   10092:             if ($stu_status eq 'Active') {
                   10093:                 $status = 'active';
                   10094:             } elsif ($end < $now) {
                   10095:                 $status = 'previous';
                   10096:             } elsif ($start > $now) {
                   10097:                 $status = 'future';
                   10098:             } 
                   10099: 	    if ($section ne '-1' && $section !~ /^\s*$/) {
                   10100:                 if ((!defined($possible_status)) || (($status ne '') && 
                   10101:                     (grep/^\Q$status\E$/,@{$possible_status}))) { 
                   10102: 		    $sectioncount{$section}++;
                   10103:                 }
1.240     albertel 10104: 	    }
                   10105: 	}
                   10106:     }
1.1075.2.33  raeburn  10107:     if ($only_students) {
                   10108:         return %sectioncount;
                   10109:     }
1.240     albertel 10110:     my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   10111:     foreach my $user (sort(keys(%courseroles))) {
                   10112: 	if ($user !~ /^(\w{2})/) { next; }
                   10113: 	my ($role) = ($user =~ /^(\w{2})/);
                   10114: 	if ($possible_roles && !(grep(/^$role$/,@$possible_roles))) { next; }
1.419     raeburn  10115: 	my ($section,$status);
1.240     albertel 10116: 	if ($role eq 'cr' &&
                   10117: 	    $user =~ m-^$role/[^/]*/[^/]*/[^/]*:[^:]*:[^:]*:(\w+)-) {
                   10118: 	    $section=$1;
                   10119: 	}
                   10120: 	if ($user =~ /^$role:[^:]*:[^:]*:(\w+)/) { $section=$1; }
                   10121: 	if (!defined($section) || $section eq '-1') { next; }
1.419     raeburn  10122:         my ($end,$start) = ($courseroles{$user} =~ /^([^:]*):([^:]*)$/);
                   10123:         if ($end == -1 && $start == -1) {
                   10124:             next; #deleted role
                   10125:         }
                   10126:         if (!defined($possible_status)) { 
                   10127:             $sectioncount{$section}++;
                   10128:         } else {
                   10129:             if ((!$end || $end >= $now) && (!$start || $start <= $now)) {
                   10130:                 $status = 'active';
                   10131:             } elsif ($end < $now) {
                   10132:                 $status = 'future';
                   10133:             } elsif ($start > $now) {
                   10134:                 $status = 'previous';
                   10135:             }
                   10136:             if (($status ne '') && (grep/^\Q$status\E$/,@{$possible_status})) {
                   10137:                 $sectioncount{$section}++;
                   10138:             }
                   10139:         }
1.233     raeburn  10140:     }
1.366     albertel 10141:     return %sectioncount;
1.233     raeburn  10142: }
                   10143: 
1.274     raeburn  10144: ###############################################
1.294     raeburn  10145: 
                   10146: =pod
1.405     albertel 10147: 
                   10148: =item * &get_course_users()
                   10149: 
1.275     raeburn  10150: Retrieves usernames:domains for users in the specified course
                   10151: with specific role(s), and access status. 
                   10152: 
                   10153: Incoming parameters:
1.277     albertel 10154: 1. course domain
                   10155: 2. course number
                   10156: 3. access status: users must have - either active, 
1.275     raeburn  10157: previous, future, or all.
1.277     albertel 10158: 4. reference to array of permissible roles
1.288     raeburn  10159: 5. reference to array of section restrictions (optional)
                   10160: 6. reference to results object (hash of hashes).
                   10161: 7. reference to optional userdata hash
1.609     raeburn  10162: 8. reference to optional statushash
1.630     raeburn  10163: 9. flag if privileged users (except those set to unhide in
                   10164:    course settings) should be excluded    
1.609     raeburn  10165: Keys of top level results hash are roles.
1.275     raeburn  10166: Keys of inner hashes are username:domain, with 
                   10167: values set to access type.
1.288     raeburn  10168: Optional userdata hash returns an array with arguments in the 
                   10169: same order as loncoursedata::get_classlist() for student data.
                   10170: 
1.609     raeburn  10171: Optional statushash returns
                   10172: 
1.288     raeburn  10173: Entries for end, start, section and status are blank because
                   10174: of the possibility of multiple values for non-student roles.
                   10175: 
1.275     raeburn  10176: =cut
1.405     albertel 10177: 
1.275     raeburn  10178: ###############################################
1.405     albertel 10179: 
1.275     raeburn  10180: sub get_course_users {
1.630     raeburn  10181:     my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata,$statushash,$hidepriv) = @_;
1.288     raeburn  10182:     my %idx = ();
1.419     raeburn  10183:     my %seclists;
1.288     raeburn  10184: 
                   10185:     $idx{udom} = &Apache::loncoursedata::CL_SDOM();
                   10186:     $idx{uname} =  &Apache::loncoursedata::CL_SNAME();
                   10187:     $idx{end} = &Apache::loncoursedata::CL_END();
                   10188:     $idx{start} = &Apache::loncoursedata::CL_START();
                   10189:     $idx{id} = &Apache::loncoursedata::CL_ID();
                   10190:     $idx{section} = &Apache::loncoursedata::CL_SECTION();
                   10191:     $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME();
                   10192:     $idx{status} = &Apache::loncoursedata::CL_STATUS();
                   10193: 
1.290     albertel 10194:     if (grep(/^st$/,@{$roles})) {
1.276     albertel 10195:         my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist($cdom,$cnum);
1.278     raeburn  10196:         my $now = time;
1.277     albertel 10197:         foreach my $student (keys(%{$classlist})) {
1.288     raeburn  10198:             my $match = 0;
1.412     raeburn  10199:             my $secmatch = 0;
1.419     raeburn  10200:             my $section = $$classlist{$student}[$idx{section}];
1.609     raeburn  10201:             my $status = $$classlist{$student}[$idx{status}];
1.419     raeburn  10202:             if ($section eq '') {
                   10203:                 $section = 'none';
                   10204:             }
1.291     albertel 10205:             if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 10206:                 if (grep(/^all$/,@{$sections})) {
1.412     raeburn  10207:                     $secmatch = 1;
                   10208:                 } elsif ($$classlist{$student}[$idx{section}] eq '') {
1.420     albertel 10209:                     if (grep(/^none$/,@{$sections})) {
1.412     raeburn  10210:                         $secmatch = 1;
                   10211:                     }
                   10212:                 } else {  
1.419     raeburn  10213: 		    if (grep(/^\Q$section\E$/,@{$sections})) {
1.412     raeburn  10214: 		        $secmatch = 1;
                   10215:                     }
1.290     albertel 10216: 		}
1.412     raeburn  10217:                 if (!$secmatch) {
                   10218:                     next;
                   10219:                 }
1.419     raeburn  10220:             }
1.275     raeburn  10221:             if (defined($$types{'active'})) {
1.288     raeburn  10222:                 if ($$classlist{$student}[$idx{status}] eq 'Active') {
1.275     raeburn  10223:                     push(@{$$users{st}{$student}},'active');
1.288     raeburn  10224:                     $match = 1;
1.275     raeburn  10225:                 }
                   10226:             }
                   10227:             if (defined($$types{'previous'})) {
1.609     raeburn  10228:                 if ($$classlist{$student}[$idx{status}] eq 'Expired') {
1.275     raeburn  10229:                     push(@{$$users{st}{$student}},'previous');
1.288     raeburn  10230:                     $match = 1;
1.275     raeburn  10231:                 }
                   10232:             }
                   10233:             if (defined($$types{'future'})) {
1.609     raeburn  10234:                 if ($$classlist{$student}[$idx{status}] eq 'Future') {
1.275     raeburn  10235:                     push(@{$$users{st}{$student}},'future');
1.288     raeburn  10236:                     $match = 1;
1.275     raeburn  10237:                 }
                   10238:             }
1.609     raeburn  10239:             if ($match) {
                   10240:                 push(@{$seclists{$student}},$section);
                   10241:                 if (ref($userdata) eq 'HASH') {
                   10242:                     $$userdata{$student} = $$classlist{$student};
                   10243:                 }
                   10244:                 if (ref($statushash) eq 'HASH') {
                   10245:                     $statushash->{$student}{'st'}{$section} = $status;
                   10246:                 }
1.288     raeburn  10247:             }
1.275     raeburn  10248:         }
                   10249:     }
1.412     raeburn  10250:     if ((@{$roles} > 1) || ((@{$roles} == 1) && ($$roles[0] ne "st"))) {
1.439     raeburn  10251:         my %coursepersonnel = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   10252:         my $now = time;
1.609     raeburn  10253:         my %displaystatus = ( previous => 'Expired',
                   10254:                               active   => 'Active',
                   10255:                               future   => 'Future',
                   10256:                             );
1.1075.2.36  raeburn  10257:         my (%nothide,@possdoms);
1.630     raeburn  10258:         if ($hidepriv) {
                   10259:             my %coursehash=&Apache::lonnet::coursedescription($cdom.'_'.$cnum);
                   10260:             foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
                   10261:                 if ($user !~ /:/) {
                   10262:                     $nothide{join(':',split(/[\@]/,$user))}=1;
                   10263:                 } else {
                   10264:                     $nothide{$user} = 1;
                   10265:                 }
                   10266:             }
1.1075.2.36  raeburn  10267:             my @possdoms = ($cdom);
                   10268:             if ($coursehash{'checkforpriv'}) {
                   10269:                 push(@possdoms,split(/,/,$coursehash{'checkforpriv'}));
                   10270:             }
1.630     raeburn  10271:         }
1.439     raeburn  10272:         foreach my $person (sort(keys(%coursepersonnel))) {
1.288     raeburn  10273:             my $match = 0;
1.412     raeburn  10274:             my $secmatch = 0;
1.439     raeburn  10275:             my $status;
1.412     raeburn  10276:             my ($role,$user,$usec) = ($person =~ /^([^:]*):([^:]+:[^:]+):([^:]*)/);
1.275     raeburn  10277:             $user =~ s/:$//;
1.439     raeburn  10278:             my ($end,$start) = split(/:/,$coursepersonnel{$person});
                   10279:             if ($end == -1 || $start == -1) {
                   10280:                 next;
                   10281:             }
                   10282:             if (($role) && ((grep(/^\Q$role\E$/,@{$roles})) ||
                   10283:                 (grep(/^cr$/,@{$roles}) && $role =~ /^cr\//))) {
1.412     raeburn  10284:                 my ($uname,$udom) = split(/:/,$user);
                   10285:                 if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 10286:                     if (grep(/^all$/,@{$sections})) {
1.412     raeburn  10287:                         $secmatch = 1;
                   10288:                     } elsif ($usec eq '') {
1.420     albertel 10289:                         if (grep(/^none$/,@{$sections})) {
1.412     raeburn  10290:                             $secmatch = 1;
                   10291:                         }
                   10292:                     } else {
                   10293:                         if (grep(/^\Q$usec\E$/,@{$sections})) {
                   10294:                             $secmatch = 1;
                   10295:                         }
                   10296:                     }
                   10297:                     if (!$secmatch) {
                   10298:                         next;
                   10299:                     }
1.288     raeburn  10300:                 }
1.419     raeburn  10301:                 if ($usec eq '') {
                   10302:                     $usec = 'none';
                   10303:                 }
1.275     raeburn  10304:                 if ($uname ne '' && $udom ne '') {
1.630     raeburn  10305:                     if ($hidepriv) {
1.1075.2.36  raeburn  10306:                         if ((&Apache::lonnet::privileged($uname,$udom,\@possdoms)) &&
1.630     raeburn  10307:                             (!$nothide{$uname.':'.$udom})) {
                   10308:                             next;
                   10309:                         }
                   10310:                     }
1.503     raeburn  10311:                     if ($end > 0 && $end < $now) {
1.439     raeburn  10312:                         $status = 'previous';
                   10313:                     } elsif ($start > $now) {
                   10314:                         $status = 'future';
                   10315:                     } else {
                   10316:                         $status = 'active';
                   10317:                     }
1.277     albertel 10318:                     foreach my $type (keys(%{$types})) { 
1.275     raeburn  10319:                         if ($status eq $type) {
1.420     albertel 10320:                             if (!grep(/^\Q$type\E$/,@{$$users{$role}{$user}})) {
1.419     raeburn  10321:                                 push(@{$$users{$role}{$user}},$type);
                   10322:                             }
1.288     raeburn  10323:                             $match = 1;
                   10324:                         }
                   10325:                     }
1.419     raeburn  10326:                     if (($match) && (ref($userdata) eq 'HASH')) {
                   10327:                         if (!exists($$userdata{$uname.':'.$udom})) {
                   10328: 			    &get_user_info($udom,$uname,\%idx,$userdata);
                   10329:                         }
1.420     albertel 10330:                         if (!grep(/^\Q$usec\E$/,@{$seclists{$uname.':'.$udom}})) {
1.419     raeburn  10331:                             push(@{$seclists{$uname.':'.$udom}},$usec);
                   10332:                         }
1.609     raeburn  10333:                         if (ref($statushash) eq 'HASH') {
                   10334:                             $statushash->{$uname.':'.$udom}{$role}{$usec} = $displaystatus{$status};
                   10335:                         }
1.275     raeburn  10336:                     }
                   10337:                 }
                   10338:             }
                   10339:         }
1.290     albertel 10340:         if (grep(/^ow$/,@{$roles})) {
1.279     raeburn  10341:             if ((defined($cdom)) && (defined($cnum))) {
                   10342:                 my %csettings = &Apache::lonnet::get('environment',['internal.courseowner'],$cdom,$cnum);
                   10343:                 if ( defined($csettings{'internal.courseowner'}) ) {
                   10344:                     my $owner = $csettings{'internal.courseowner'};
1.609     raeburn  10345:                     next if ($owner eq '');
                   10346:                     my ($ownername,$ownerdom);
                   10347:                     if ($owner =~ /^([^:]+):([^:]+)$/) {
                   10348:                         $ownername = $1;
                   10349:                         $ownerdom = $2;
                   10350:                     } else {
                   10351:                         $ownername = $owner;
                   10352:                         $ownerdom = $cdom;
                   10353:                         $owner = $ownername.':'.$ownerdom;
1.439     raeburn  10354:                     }
                   10355:                     @{$$users{'ow'}{$owner}} = 'any';
1.290     albertel 10356:                     if (defined($userdata) && 
1.609     raeburn  10357: 			!exists($$userdata{$owner})) {
                   10358: 			&get_user_info($ownerdom,$ownername,\%idx,$userdata);
                   10359:                         if (!grep(/^none$/,@{$seclists{$owner}})) {
                   10360:                             push(@{$seclists{$owner}},'none');
                   10361:                         }
                   10362:                         if (ref($statushash) eq 'HASH') {
                   10363:                             $statushash->{$owner}{'ow'}{'none'} = 'Any';
1.419     raeburn  10364:                         }
1.290     albertel 10365: 		    }
1.279     raeburn  10366:                 }
                   10367:             }
                   10368:         }
1.419     raeburn  10369:         foreach my $user (keys(%seclists)) {
                   10370:             @{$seclists{$user}} = (sort {$a <=> $b} @{$seclists{$user}});
                   10371:             $$userdata{$user}[$idx{section}] = join(',',@{$seclists{$user}});
                   10372:         }
1.275     raeburn  10373:     }
                   10374:     return;
                   10375: }
                   10376: 
1.288     raeburn  10377: sub get_user_info {
                   10378:     my ($udom,$uname,$idx,$userdata) = @_;
1.289     albertel 10379:     $$userdata{$uname.':'.$udom}[$$idx{fullname}] = 
                   10380: 	&plainname($uname,$udom,'lastname');
1.291     albertel 10381:     $$userdata{$uname.':'.$udom}[$$idx{uname}] = $uname;
1.297     raeburn  10382:     $$userdata{$uname.':'.$udom}[$$idx{udom}] = $udom;
1.609     raeburn  10383:     my %idhash =  &Apache::lonnet::idrget($udom,($uname));
                   10384:     $$userdata{$uname.':'.$udom}[$$idx{id}] = $idhash{$uname}; 
1.288     raeburn  10385:     return;
                   10386: }
1.275     raeburn  10387: 
1.472     raeburn  10388: ###############################################
                   10389: 
                   10390: =pod
                   10391: 
                   10392: =item * &get_user_quota()
                   10393: 
1.1075.2.41  raeburn  10394: Retrieves quota assigned for storage of user files.
                   10395: Default is to report quota for portfolio files.
1.472     raeburn  10396: 
                   10397: Incoming parameters:
                   10398: 1. user's username
                   10399: 2. user's domain
1.1075.2.41  raeburn  10400: 3. quota name - portfolio, author, or course
                   10401:    (if no quota name provided, defaults to portfolio).
1.1075.2.59  raeburn  10402: 4. crstype - official, unofficial, textbook or community, if quota name is
1.1075.2.42  raeburn  10403:    course
1.472     raeburn  10404: 
                   10405: Returns:
1.1075.2.58  raeburn  10406: 1. Disk quota (in MB) assigned to student.
1.536     raeburn  10407: 2. (Optional) Type of setting: custom or default
                   10408:    (individually assigned or default for user's 
                   10409:    institutional status).
                   10410: 3. (Optional) - User's institutional status (e.g., faculty, staff
                   10411:    or student - types as defined in localenroll::inst_usertypes 
                   10412:    for user's domain, which determines default quota for user.
                   10413: 4. (Optional) - Default quota which would apply to the user.
1.472     raeburn  10414: 
                   10415: If a value has been stored in the user's environment, 
1.536     raeburn  10416: it will return that, otherwise it returns the maximal default
1.1075.2.41  raeburn  10417: defined for the user's institutional status(es) in the domain.
1.472     raeburn  10418: 
                   10419: =cut
                   10420: 
                   10421: ###############################################
                   10422: 
                   10423: 
                   10424: sub get_user_quota {
1.1075.2.42  raeburn  10425:     my ($uname,$udom,$quotaname,$crstype) = @_;
1.536     raeburn  10426:     my ($quota,$quotatype,$settingstatus,$defquota);
1.472     raeburn  10427:     if (!defined($udom)) {
                   10428:         $udom = $env{'user.domain'};
                   10429:     }
                   10430:     if (!defined($uname)) {
                   10431:         $uname = $env{'user.name'};
                   10432:     }
                   10433:     if (($udom eq '' || $uname eq '') ||
                   10434:         ($udom eq 'public') && ($uname eq 'public')) {
                   10435:         $quota = 0;
1.536     raeburn  10436:         $quotatype = 'default';
                   10437:         $defquota = 0; 
1.472     raeburn  10438:     } else {
1.536     raeburn  10439:         my $inststatus;
1.1075.2.41  raeburn  10440:         if ($quotaname eq 'course') {
                   10441:             if (($env{'course.'.$udom.'_'.$uname.'.num'} eq $uname) &&
                   10442:                 ($env{'course.'.$udom.'_'.$uname.'.domain'} eq $udom)) {
                   10443:                 $quota = $env{'course.'.$udom.'_'.$uname.'.internal.uploadquota'};
                   10444:             } else {
                   10445:                 my %cenv = &Apache::lonnet::coursedescription("$udom/$uname");
                   10446:                 $quota = $cenv{'internal.uploadquota'};
                   10447:             }
1.536     raeburn  10448:         } else {
1.1075.2.41  raeburn  10449:             if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
                   10450:                 if ($quotaname eq 'author') {
                   10451:                     $quota = $env{'environment.authorquota'};
                   10452:                 } else {
                   10453:                     $quota = $env{'environment.portfolioquota'};
                   10454:                 }
                   10455:                 $inststatus = $env{'environment.inststatus'};
                   10456:             } else {
                   10457:                 my %userenv = 
                   10458:                     &Apache::lonnet::get('environment',['portfolioquota',
                   10459:                                          'authorquota','inststatus'],$udom,$uname);
                   10460:                 my ($tmp) = keys(%userenv);
                   10461:                 if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   10462:                     if ($quotaname eq 'author') {
                   10463:                         $quota = $userenv{'authorquota'};
                   10464:                     } else {
                   10465:                         $quota = $userenv{'portfolioquota'};
                   10466:                     }
                   10467:                     $inststatus = $userenv{'inststatus'};
                   10468:                 } else {
                   10469:                     undef(%userenv);
                   10470:                 }
                   10471:             }
                   10472:         }
                   10473:         if ($quota eq '' || wantarray) {
                   10474:             if ($quotaname eq 'course') {
                   10475:                 my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
1.1075.2.59  raeburn  10476:                 if (($crstype eq 'official') || ($crstype eq 'unofficial') ||
                   10477:                     ($crstype eq 'community') || ($crstype eq 'textbook')) {
1.1075.2.42  raeburn  10478:                     $defquota = $domdefs{$crstype.'quota'};
                   10479:                 }
                   10480:                 if ($defquota eq '') {
                   10481:                     $defquota = 500;
                   10482:                 }
1.1075.2.41  raeburn  10483:             } else {
                   10484:                 ($defquota,$settingstatus) = &default_quota($udom,$inststatus,$quotaname);
                   10485:             }
                   10486:             if ($quota eq '') {
                   10487:                 $quota = $defquota;
                   10488:                 $quotatype = 'default';
                   10489:             } else {
                   10490:                 $quotatype = 'custom';
                   10491:             }
1.472     raeburn  10492:         }
                   10493:     }
1.536     raeburn  10494:     if (wantarray) {
                   10495:         return ($quota,$quotatype,$settingstatus,$defquota);
                   10496:     } else {
                   10497:         return $quota;
                   10498:     }
1.472     raeburn  10499: }
                   10500: 
                   10501: ###############################################
                   10502: 
                   10503: =pod
                   10504: 
                   10505: =item * &default_quota()
                   10506: 
1.536     raeburn  10507: Retrieves default quota assigned for storage of user portfolio files,
                   10508: given an (optional) user's institutional status.
1.472     raeburn  10509: 
                   10510: Incoming parameters:
1.1075.2.42  raeburn  10511: 
1.472     raeburn  10512: 1. domain
1.536     raeburn  10513: 2. (Optional) institutional status(es).  This is a : separated list of 
                   10514:    status types (e.g., faculty, staff, student etc.)
                   10515:    which apply to the user for whom the default is being retrieved.
                   10516:    If the institutional status string in undefined, the domain
1.1075.2.41  raeburn  10517:    default quota will be returned.
                   10518: 3.  quota name - portfolio, author, or course
                   10519:    (if no quota name provided, defaults to portfolio).
1.472     raeburn  10520: 
                   10521: Returns:
1.1075.2.42  raeburn  10522: 
1.1075.2.58  raeburn  10523: 1. Default disk quota (in MB) for user portfolios in the domain.
1.536     raeburn  10524: 2. (Optional) institutional type which determined the value of the
                   10525:    default quota.
1.472     raeburn  10526: 
                   10527: If a value has been stored in the domain's configuration db,
                   10528: it will return that, otherwise it returns 20 (for backwards 
                   10529: compatibility with domains which have not set up a configuration
1.1075.2.58  raeburn  10530: db file; the original statically defined portfolio quota was 20 MB). 
1.472     raeburn  10531: 
1.536     raeburn  10532: If the user's status includes multiple types (e.g., staff and student),
                   10533: the largest default quota which applies to the user determines the
                   10534: default quota returned.
                   10535: 
1.472     raeburn  10536: =cut
                   10537: 
                   10538: ###############################################
                   10539: 
                   10540: 
                   10541: sub default_quota {
1.1075.2.41  raeburn  10542:     my ($udom,$inststatus,$quotaname) = @_;
1.536     raeburn  10543:     my ($defquota,$settingstatus);
                   10544:     my %quotahash = &Apache::lonnet::get_dom('configuration',
1.622     raeburn  10545:                                             ['quotas'],$udom);
1.1075.2.41  raeburn  10546:     my $key = 'defaultquota';
                   10547:     if ($quotaname eq 'author') {
                   10548:         $key = 'authorquota';
                   10549:     }
1.622     raeburn  10550:     if (ref($quotahash{'quotas'}) eq 'HASH') {
1.536     raeburn  10551:         if ($inststatus ne '') {
1.765     raeburn  10552:             my @statuses = map { &unescape($_); } split(/:/,$inststatus);
1.536     raeburn  10553:             foreach my $item (@statuses) {
1.1075.2.41  raeburn  10554:                 if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
                   10555:                     if ($quotahash{'quotas'}{$key}{$item} ne '') {
1.711     raeburn  10556:                         if ($defquota eq '') {
1.1075.2.41  raeburn  10557:                             $defquota = $quotahash{'quotas'}{$key}{$item};
1.711     raeburn  10558:                             $settingstatus = $item;
1.1075.2.41  raeburn  10559:                         } elsif ($quotahash{'quotas'}{$key}{$item} > $defquota) {
                   10560:                             $defquota = $quotahash{'quotas'}{$key}{$item};
1.711     raeburn  10561:                             $settingstatus = $item;
                   10562:                         }
                   10563:                     }
1.1075.2.41  raeburn  10564:                 } elsif ($key eq 'defaultquota') {
1.711     raeburn  10565:                     if ($quotahash{'quotas'}{$item} ne '') {
                   10566:                         if ($defquota eq '') {
                   10567:                             $defquota = $quotahash{'quotas'}{$item};
                   10568:                             $settingstatus = $item;
                   10569:                         } elsif ($quotahash{'quotas'}{$item} > $defquota) {
                   10570:                             $defquota = $quotahash{'quotas'}{$item};
                   10571:                             $settingstatus = $item;
                   10572:                         }
1.536     raeburn  10573:                     }
                   10574:                 }
                   10575:             }
                   10576:         }
                   10577:         if ($defquota eq '') {
1.1075.2.41  raeburn  10578:             if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
                   10579:                 $defquota = $quotahash{'quotas'}{$key}{'default'};
                   10580:             } elsif ($key eq 'defaultquota') {
1.711     raeburn  10581:                 $defquota = $quotahash{'quotas'}{'default'};
                   10582:             }
1.536     raeburn  10583:             $settingstatus = 'default';
1.1075.2.42  raeburn  10584:             if ($defquota eq '') {
                   10585:                 if ($quotaname eq 'author') {
                   10586:                     $defquota = 500;
                   10587:                 }
                   10588:             }
1.536     raeburn  10589:         }
                   10590:     } else {
                   10591:         $settingstatus = 'default';
1.1075.2.41  raeburn  10592:         if ($quotaname eq 'author') {
                   10593:             $defquota = 500;
                   10594:         } else {
                   10595:             $defquota = 20;
                   10596:         }
1.536     raeburn  10597:     }
                   10598:     if (wantarray) {
                   10599:         return ($defquota,$settingstatus);
1.472     raeburn  10600:     } else {
1.536     raeburn  10601:         return $defquota;
1.472     raeburn  10602:     }
                   10603: }
                   10604: 
1.1075.2.41  raeburn  10605: ###############################################
                   10606: 
                   10607: =pod
                   10608: 
1.1075.2.42  raeburn  10609: =item * &excess_filesize_warning()
1.1075.2.41  raeburn  10610: 
                   10611: Returns warning message if upload of file to authoring space, or copying
1.1075.2.42  raeburn  10612: of existing file within authoring space will cause quota for the authoring
                   10613: space to be exceeded.
                   10614: 
                   10615: Same, if upload of a file directly to a course/community via Course Editor
                   10616: will cause quota for uploaded content for the course to be exceeded.
1.1075.2.41  raeburn  10617: 
1.1075.2.61  raeburn  10618: Inputs: 7 
1.1075.2.42  raeburn  10619: 1. username or coursenum
1.1075.2.41  raeburn  10620: 2. domain
1.1075.2.42  raeburn  10621: 3. context ('author' or 'course')
1.1075.2.41  raeburn  10622: 4. filename of file for which action is being requested
                   10623: 5. filesize (kB) of file
                   10624: 6. action being taken: copy or upload.
1.1075.2.59  raeburn  10625: 7. quotatype (in course context -- official, unofficial, community or textbook).
1.1075.2.41  raeburn  10626: 
                   10627: Returns: 1 scalar: HTML to display containing warning if quota would be exceeded,
                   10628:          otherwise return null.
                   10629: 
1.1075.2.42  raeburn  10630: =back
                   10631: 
1.1075.2.41  raeburn  10632: =cut
                   10633: 
1.1075.2.42  raeburn  10634: sub excess_filesize_warning {
1.1075.2.59  raeburn  10635:     my ($uname,$udom,$context,$filename,$filesize,$action,$quotatype) = @_;
1.1075.2.42  raeburn  10636:     my $current_disk_usage = 0;
1.1075.2.59  raeburn  10637:     my $disk_quota = &get_user_quota($uname,$udom,$context,$quotatype); #expressed in MB
1.1075.2.42  raeburn  10638:     if ($context eq 'author') {
                   10639:         my $authorspace = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname";
                   10640:         $current_disk_usage = &Apache::lonnet::diskusage($udom,$uname,$authorspace);
                   10641:     } else {
                   10642:         foreach my $subdir ('docs','supplemental') {
                   10643:             $current_disk_usage += &Apache::lonnet::diskusage($udom,$uname,"userfiles/$subdir",1);
                   10644:         }
                   10645:     }
1.1075.2.41  raeburn  10646:     $disk_quota = int($disk_quota * 1000);
                   10647:     if (($current_disk_usage + $filesize) > $disk_quota) {
1.1075.2.69  raeburn  10648:         return '<p class="LC_warning">'.
1.1075.2.41  raeburn  10649:                 &mt("Unable to $action [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.",
1.1075.2.69  raeburn  10650:                     '<span class="LC_filename">'.$filename.'</span>',$filesize).'</p>'.
                   10651:                '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
1.1075.2.41  raeburn  10652:                             $disk_quota,$current_disk_usage).
                   10653:                '</p>';
                   10654:     }
                   10655:     return;
                   10656: }
                   10657: 
                   10658: ###############################################
                   10659: 
                   10660: 
1.384     raeburn  10661: sub get_secgrprole_info {
                   10662:     my ($cdom,$cnum,$needroles,$type)  = @_;
                   10663:     my %sections_count = &get_sections($cdom,$cnum);
                   10664:     my @sections =  (sort {$a <=> $b} keys(%sections_count));
                   10665:     my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
                   10666:     my @groups = sort(keys(%curr_groups));
                   10667:     my $allroles = [];
                   10668:     my $rolehash;
                   10669:     my $accesshash = {
                   10670:                      active => 'Currently has access',
                   10671:                      future => 'Will have future access',
                   10672:                      previous => 'Previously had access',
                   10673:                   };
                   10674:     if ($needroles) {
                   10675:         $rolehash = {'all' => 'all'};
1.385     albertel 10676:         my %user_roles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   10677: 	if (&Apache::lonnet::error(%user_roles)) {
                   10678: 	    undef(%user_roles);
                   10679: 	}
                   10680:         foreach my $item (keys(%user_roles)) {
1.384     raeburn  10681:             my ($role)=split(/\:/,$item,2);
                   10682:             if ($role eq 'cr') { next; }
                   10683:             if ($role =~ /^cr/) {
                   10684:                 $$rolehash{$role} = (split('/',$role))[3];
                   10685:             } else {
                   10686:                 $$rolehash{$role} = &Apache::lonnet::plaintext($role,$type);
                   10687:             }
                   10688:         }
                   10689:         foreach my $key (sort(keys(%{$rolehash}))) {
                   10690:             push(@{$allroles},$key);
                   10691:         }
                   10692:         push (@{$allroles},'st');
                   10693:         $$rolehash{'st'} = &Apache::lonnet::plaintext('st',$type);
                   10694:     }
                   10695:     return (\@sections,\@groups,$allroles,$rolehash,$accesshash);
                   10696: }
                   10697: 
1.555     raeburn  10698: sub user_picker {
1.1075.2.127  raeburn  10699:     my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype,$context,$fixeddom,$noinstd) = @_;
1.555     raeburn  10700:     my $currdom = $dom;
1.1075.2.114  raeburn  10701:     my @alldoms = &Apache::lonnet::all_domains();
                   10702:     if (@alldoms == 1) {
                   10703:         my %domsrch = &Apache::lonnet::get_dom('configuration',
                   10704:                                                ['directorysrch'],$alldoms[0]);
                   10705:         my $domdesc = &Apache::lonnet::domain($alldoms[0],'description');
                   10706:         my $showdom = $domdesc;
                   10707:         if ($showdom eq '') {
                   10708:             $showdom = $dom;
                   10709:         }
                   10710:         if (ref($domsrch{'directorysrch'}) eq 'HASH') {
                   10711:             if ((!$domsrch{'directorysrch'}{'available'}) &&
                   10712:                 ($domsrch{'directorysrch'}{'lcavailable'} eq '0')) {
                   10713:                 return (&mt('LON-CAPA directory search is not available in domain: [_1]',$showdom),0);
                   10714:             }
                   10715:         }
                   10716:     }
1.555     raeburn  10717:     my %curr_selected = (
                   10718:                         srchin => 'dom',
1.580     raeburn  10719:                         srchby => 'lastname',
1.555     raeburn  10720:                       );
                   10721:     my $srchterm;
1.625     raeburn  10722:     if ((ref($srch) eq 'HASH') && ($env{'form.origform'} ne 'crtusername')) {
1.555     raeburn  10723:         if ($srch->{'srchby'} ne '') {
                   10724:             $curr_selected{'srchby'} = $srch->{'srchby'};
                   10725:         }
                   10726:         if ($srch->{'srchin'} ne '') {
                   10727:             $curr_selected{'srchin'} = $srch->{'srchin'};
                   10728:         }
                   10729:         if ($srch->{'srchtype'} ne '') {
                   10730:             $curr_selected{'srchtype'} = $srch->{'srchtype'};
                   10731:         }
                   10732:         if ($srch->{'srchdomain'} ne '') {
                   10733:             $currdom = $srch->{'srchdomain'};
                   10734:         }
                   10735:         $srchterm = $srch->{'srchterm'};
                   10736:     }
1.1075.2.98  raeburn  10737:     my %html_lt=&Apache::lonlocal::texthash(
1.573     raeburn  10738:                     'usr'       => 'Search criteria',
1.563     raeburn  10739:                     'doma'      => 'Domain/institution to search',
1.558     albertel 10740:                     'uname'     => 'username',
                   10741:                     'lastname'  => 'last name',
1.555     raeburn  10742:                     'lastfirst' => 'last name, first name',
1.558     albertel 10743:                     'crs'       => 'in this course',
1.576     raeburn  10744:                     'dom'       => 'in selected LON-CAPA domain', 
1.558     albertel 10745:                     'alc'       => 'all LON-CAPA',
1.573     raeburn  10746:                     'instd'     => 'in institutional directory for selected domain',
1.558     albertel 10747:                     'exact'     => 'is',
                   10748:                     'contains'  => 'contains',
1.569     raeburn  10749:                     'begins'    => 'begins with',
1.1075.2.98  raeburn  10750:                                        );
                   10751:     my %js_lt=&Apache::lonlocal::texthash(
1.571     raeburn  10752:                     'youm'      => "You must include some text to search for.",
                   10753:                     'thte'      => "The text you are searching for must contain at least two characters when using a 'begins' type search.",
                   10754:                     'thet'      => "The text you are searching for must contain at least three characters when using a 'contains' type search.",
                   10755:                     'yomc'      => "You must choose a domain when using an institutional directory search.",
                   10756:                     'ymcd'      => "You must choose a domain when using a domain search.",
                   10757:                     'whus'      => "When using searching by last,first you must include a comma as separator between last name and first name.",
                   10758:                     'whse'      => "When searching by last,first you must include at least one character in the first name.",
                   10759:                      'thfo'     => "The following need to be corrected before the search can be run:",
1.555     raeburn  10760:                                        );
1.1075.2.98  raeburn  10761:     &html_escape(\%html_lt);
                   10762:     &js_escape(\%js_lt);
1.1075.2.115  raeburn  10763:     my $domform;
1.1075.2.126  raeburn  10764:     my $allow_blank = 1;
1.1075.2.115  raeburn  10765:     if ($fixeddom) {
1.1075.2.126  raeburn  10766:         $allow_blank = 0;
                   10767:         $domform = &select_dom_form($currdom,'srchdomain',$allow_blank,1,undef,[$currdom]);
1.1075.2.115  raeburn  10768:     } else {
1.1075.2.126  raeburn  10769:         $domform = &select_dom_form($currdom,'srchdomain',$allow_blank,1);
1.1075.2.115  raeburn  10770:     }
1.563     raeburn  10771:     my $srchinsel = ' <select name="srchin">';
1.555     raeburn  10772: 
                   10773:     my @srchins = ('crs','dom','alc','instd');
                   10774: 
                   10775:     foreach my $option (@srchins) {
                   10776:         # FIXME 'alc' option unavailable until 
                   10777:         #       loncreateuser::print_user_query_page()
                   10778:         #       has been completed.
                   10779:         next if ($option eq 'alc');
1.880     raeburn  10780:         next if (($option eq 'crs') && ($env{'form.form'} eq 'requestcrs'));  
1.555     raeburn  10781:         next if ($option eq 'crs' && !$env{'request.course.id'});
1.1075.2.127  raeburn  10782:         next if (($option eq 'instd') && ($noinstd));
1.563     raeburn  10783:         if ($curr_selected{'srchin'} eq $option) {
                   10784:             $srchinsel .= ' 
1.1075.2.98  raeburn  10785:    <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.563     raeburn  10786:         } else {
                   10787:             $srchinsel .= '
1.1075.2.98  raeburn  10788:    <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.563     raeburn  10789:         }
1.555     raeburn  10790:     }
1.563     raeburn  10791:     $srchinsel .= "\n  </select>\n";
1.555     raeburn  10792: 
                   10793:     my $srchbysel =  ' <select name="srchby">';
1.580     raeburn  10794:     foreach my $option ('lastname','lastfirst','uname') {
1.555     raeburn  10795:         if ($curr_selected{'srchby'} eq $option) {
                   10796:             $srchbysel .= '
1.1075.2.98  raeburn  10797:    <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.555     raeburn  10798:         } else {
                   10799:             $srchbysel .= '
1.1075.2.98  raeburn  10800:    <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.555     raeburn  10801:          }
                   10802:     }
                   10803:     $srchbysel .= "\n  </select>\n";
                   10804: 
                   10805:     my $srchtypesel = ' <select name="srchtype">';
1.580     raeburn  10806:     foreach my $option ('begins','contains','exact') {
1.555     raeburn  10807:         if ($curr_selected{'srchtype'} eq $option) {
                   10808:             $srchtypesel .= '
1.1075.2.98  raeburn  10809:    <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.555     raeburn  10810:         } else {
                   10811:             $srchtypesel .= '
1.1075.2.98  raeburn  10812:    <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.555     raeburn  10813:         }
                   10814:     }
                   10815:     $srchtypesel .= "\n  </select>\n";
                   10816: 
1.558     albertel 10817:     my ($newuserscript,$new_user_create);
1.994     raeburn  10818:     my $context_dom = $env{'request.role.domain'};
                   10819:     if ($context eq 'requestcrs') {
                   10820:         if ($env{'form.coursedom'} ne '') { 
                   10821:             $context_dom = $env{'form.coursedom'};
                   10822:         }
                   10823:     }
1.556     raeburn  10824:     if ($forcenewuser) {
1.576     raeburn  10825:         if (ref($srch) eq 'HASH') {
1.994     raeburn  10826:             if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $context_dom) {
1.627     raeburn  10827:                 if ($cancreate) {
                   10828:                     $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>';
                   10829:                 } else {
1.799     bisitz   10830:                     my $helplink = 'javascript:helpMenu('."'display'".')';
1.627     raeburn  10831:                     my %usertypetext = (
                   10832:                         official   => 'institutional',
                   10833:                         unofficial => 'non-institutional',
                   10834:                     );
1.799     bisitz   10835:                     $new_user_create = '<p class="LC_warning">'
                   10836:                                       .&mt("You are not authorized to create new $usertypetext{$usertype} users in this domain.")
                   10837:                                       .' '
                   10838:                                       .&mt('Please contact the [_1]helpdesk[_2] for assistance.'
                   10839:                                           ,'<a href="'.$helplink.'">','</a>')
                   10840:                                       .'</p><br />';
1.627     raeburn  10841:                 }
1.576     raeburn  10842:             }
                   10843:         }
                   10844: 
1.556     raeburn  10845:         $newuserscript = <<"ENDSCRIPT";
                   10846: 
1.570     raeburn  10847: function setSearch(createnew,callingForm) {
1.556     raeburn  10848:     if (createnew == 1) {
1.570     raeburn  10849:         for (var i=0; i<callingForm.srchby.length; i++) {
                   10850:             if (callingForm.srchby.options[i].value == 'uname') {
                   10851:                 callingForm.srchby.selectedIndex = i;
1.556     raeburn  10852:             }
                   10853:         }
1.570     raeburn  10854:         for (var i=0; i<callingForm.srchin.length; i++) {
                   10855:             if ( callingForm.srchin.options[i].value == 'dom') {
                   10856: 		callingForm.srchin.selectedIndex = i;
1.556     raeburn  10857:             }
                   10858:         }
1.570     raeburn  10859:         for (var i=0; i<callingForm.srchtype.length; i++) {
                   10860:             if (callingForm.srchtype.options[i].value == 'exact') {
                   10861:                 callingForm.srchtype.selectedIndex = i;
1.556     raeburn  10862:             }
                   10863:         }
1.570     raeburn  10864:         for (var i=0; i<callingForm.srchdomain.length; i++) {
1.994     raeburn  10865:             if (callingForm.srchdomain.options[i].value == '$context_dom') {
1.570     raeburn  10866:                 callingForm.srchdomain.selectedIndex = i;
1.556     raeburn  10867:             }
                   10868:         }
                   10869:     }
                   10870: }
                   10871: ENDSCRIPT
1.558     albertel 10872: 
1.556     raeburn  10873:     }
                   10874: 
1.555     raeburn  10875:     my $output = <<"END_BLOCK";
1.556     raeburn  10876: <script type="text/javascript">
1.824     bisitz   10877: // <![CDATA[
1.570     raeburn  10878: function validateEntry(callingForm) {
1.558     albertel 10879: 
1.556     raeburn  10880:     var checkok = 1;
1.558     albertel 10881:     var srchin;
1.570     raeburn  10882:     for (var i=0; i<callingForm.srchin.length; i++) {
                   10883: 	if ( callingForm.srchin[i].checked ) {
                   10884: 	    srchin = callingForm.srchin[i].value;
1.558     albertel 10885: 	}
                   10886:     }
                   10887: 
1.570     raeburn  10888:     var srchtype = callingForm.srchtype.options[callingForm.srchtype.selectedIndex].value;
                   10889:     var srchby = callingForm.srchby.options[callingForm.srchby.selectedIndex].value;
                   10890:     var srchdomain = callingForm.srchdomain.options[callingForm.srchdomain.selectedIndex].value;
                   10891:     var srchterm =  callingForm.srchterm.value;
                   10892:     var srchin = callingForm.srchin.options[callingForm.srchin.selectedIndex].value;
1.556     raeburn  10893:     var msg = "";
                   10894: 
                   10895:     if (srchterm == "") {
                   10896:         checkok = 0;
1.1075.2.98  raeburn  10897:         msg += "$js_lt{'youm'}\\n";
1.556     raeburn  10898:     }
                   10899: 
1.569     raeburn  10900:     if (srchtype== 'begins') {
                   10901:         if (srchterm.length < 2) {
                   10902:             checkok = 0;
1.1075.2.98  raeburn  10903:             msg += "$js_lt{'thte'}\\n";
1.569     raeburn  10904:         }
                   10905:     }
                   10906: 
1.556     raeburn  10907:     if (srchtype== 'contains') {
                   10908:         if (srchterm.length < 3) {
                   10909:             checkok = 0;
1.1075.2.98  raeburn  10910:             msg += "$js_lt{'thet'}\\n";
1.556     raeburn  10911:         }
                   10912:     }
                   10913:     if (srchin == 'instd') {
                   10914:         if (srchdomain == '') {
                   10915:             checkok = 0;
1.1075.2.98  raeburn  10916:             msg += "$js_lt{'yomc'}\\n";
1.556     raeburn  10917:         }
                   10918:     }
                   10919:     if (srchin == 'dom') {
                   10920:         if (srchdomain == '') {
                   10921:             checkok = 0;
1.1075.2.98  raeburn  10922:             msg += "$js_lt{'ymcd'}\\n";
1.556     raeburn  10923:         }
                   10924:     }
                   10925:     if (srchby == 'lastfirst') {
                   10926:         if (srchterm.indexOf(",") == -1) {
                   10927:             checkok = 0;
1.1075.2.98  raeburn  10928:             msg += "$js_lt{'whus'}\\n";
1.556     raeburn  10929:         }
                   10930:         if (srchterm.indexOf(",") == srchterm.length -1) {
                   10931:             checkok = 0;
1.1075.2.98  raeburn  10932:             msg += "$js_lt{'whse'}\\n";
1.556     raeburn  10933:         }
                   10934:     }
                   10935:     if (checkok == 0) {
1.1075.2.98  raeburn  10936:         alert("$js_lt{'thfo'}\\n"+msg);
1.556     raeburn  10937:         return;
                   10938:     }
                   10939:     if (checkok == 1) {
1.570     raeburn  10940:         callingForm.submit();
1.556     raeburn  10941:     }
                   10942: }
                   10943: 
                   10944: $newuserscript
                   10945: 
1.824     bisitz   10946: // ]]>
1.556     raeburn  10947: </script>
1.558     albertel 10948: 
                   10949: $new_user_create
                   10950: 
1.555     raeburn  10951: END_BLOCK
1.558     albertel 10952: 
1.876     raeburn  10953:     $output .= &Apache::lonhtmlcommon::start_pick_box().
1.1075.2.98  raeburn  10954:                &Apache::lonhtmlcommon::row_title($html_lt{'doma'}).
1.876     raeburn  10955:                $domform.
                   10956:                &Apache::lonhtmlcommon::row_closure().
1.1075.2.98  raeburn  10957:                &Apache::lonhtmlcommon::row_title($html_lt{'usr'}).
1.876     raeburn  10958:                $srchbysel.
                   10959:                $srchtypesel. 
                   10960:                '<input type="text" size="15" name="srchterm" value="'.$srchterm.'" />'.
                   10961:                $srchinsel.
                   10962:                &Apache::lonhtmlcommon::row_closure(1). 
                   10963:                &Apache::lonhtmlcommon::end_pick_box().
                   10964:                '<br />';
1.1075.2.114  raeburn  10965:     return ($output,1);
1.555     raeburn  10966: }
                   10967: 
1.612     raeburn  10968: sub user_rule_check {
1.615     raeburn  10969:     my ($usershash,$checks,$alerts,$rulematch,$inst_results,$curr_rules,$got_rules) = @_;
1.1075.2.99  raeburn  10970:     my ($response,%inst_response);
1.612     raeburn  10971:     if (ref($usershash) eq 'HASH') {
1.1075.2.99  raeburn  10972:         if (keys(%{$usershash}) > 1) {
                   10973:             my (%by_username,%by_id,%userdoms);
                   10974:             my $checkid;
1.612     raeburn  10975:             if (ref($checks) eq 'HASH') {
1.1075.2.99  raeburn  10976:                 if ((!defined($checks->{'username'})) && (defined($checks->{'id'}))) {
                   10977:                     $checkid = 1;
                   10978:                 }
                   10979:             }
                   10980:             foreach my $user (keys(%{$usershash})) {
                   10981:                 my ($uname,$udom) = split(/:/,$user);
                   10982:                 if ($checkid) {
                   10983:                     if (ref($usershash->{$user}) eq 'HASH') {
                   10984:                         if ($usershash->{$user}->{'id'} ne '') {
                   10985:                             $by_id{$udom}{$usershash->{$user}->{'id'}} = $uname;
                   10986:                             $userdoms{$udom} = 1;
                   10987:                             if (ref($inst_results) eq 'HASH') {
                   10988:                                 $inst_results->{$uname.':'.$udom} = {};
                   10989:                             }
                   10990:                         }
                   10991:                     }
                   10992:                 } else {
                   10993:                     $by_username{$udom}{$uname} = 1;
                   10994:                     $userdoms{$udom} = 1;
                   10995:                     if (ref($inst_results) eq 'HASH') {
                   10996:                         $inst_results->{$uname.':'.$udom} = {};
                   10997:                     }
                   10998:                 }
                   10999:             }
                   11000:             foreach my $udom (keys(%userdoms)) {
                   11001:                 if (!$got_rules->{$udom}) {
                   11002:                     my %domconfig = &Apache::lonnet::get_dom('configuration',
                   11003:                                                              ['usercreation'],$udom);
                   11004:                     if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   11005:                         foreach my $item ('username','id') {
                   11006:                             if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
                   11007:                                 $$curr_rules{$udom}{$item} =
                   11008:                                     $domconfig{'usercreation'}{$item.'_rule'};
                   11009:                             }
                   11010:                         }
                   11011:                     }
                   11012:                     $got_rules->{$udom} = 1;
                   11013:                 }
                   11014:             }
                   11015:             if ($checkid) {
                   11016:                 foreach my $udom (keys(%by_id)) {
                   11017:                     my ($outcome,$results) = &Apache::lonnet::get_multiple_instusers($udom,$by_id{$udom},'id');
                   11018:                     if ($outcome eq 'ok') {
                   11019:                         foreach my $id (keys(%{$by_id{$udom}})) {
                   11020:                             my $uname = $by_id{$udom}{$id};
                   11021:                             $inst_response{$uname.':'.$udom} = $outcome;
                   11022:                         }
                   11023:                         if (ref($results) eq 'HASH') {
                   11024:                             foreach my $uname (keys(%{$results})) {
                   11025:                                 if (exists($inst_response{$uname.':'.$udom})) {
                   11026:                                     $inst_response{$uname.':'.$udom} = $outcome;
                   11027:                                     $inst_results->{$uname.':'.$udom} = $results->{$uname};
                   11028:                                 }
                   11029:                             }
                   11030:                         }
                   11031:                     }
1.612     raeburn  11032:                 }
1.615     raeburn  11033:             } else {
1.1075.2.99  raeburn  11034:                 foreach my $udom (keys(%by_username)) {
                   11035:                     my ($outcome,$results) = &Apache::lonnet::get_multiple_instusers($udom,$by_username{$udom});
                   11036:                     if ($outcome eq 'ok') {
                   11037:                         foreach my $uname (keys(%{$by_username{$udom}})) {
                   11038:                             $inst_response{$uname.':'.$udom} = $outcome;
                   11039:                         }
                   11040:                         if (ref($results) eq 'HASH') {
                   11041:                             foreach my $uname (keys(%{$results})) {
                   11042:                                 $inst_results->{$uname.':'.$udom} = $results->{$uname};
                   11043:                             }
                   11044:                         }
                   11045:                     }
                   11046:                 }
1.612     raeburn  11047:             }
1.1075.2.99  raeburn  11048:         } elsif (keys(%{$usershash}) == 1) {
                   11049:             my $user = (keys(%{$usershash}))[0];
                   11050:             my ($uname,$udom) = split(/:/,$user);
                   11051:             if (($udom ne '') && ($uname ne '')) {
                   11052:                 if (ref($usershash->{$user}) eq 'HASH') {
                   11053:                     if (ref($checks) eq 'HASH') {
                   11054:                         if (defined($checks->{'username'})) {
                   11055:                             ($inst_response{$user},%{$inst_results->{$user}}) =
                   11056:                                 &Apache::lonnet::get_instuser($udom,$uname);
                   11057:                         } elsif (defined($checks->{'id'})) {
                   11058:                             if ($usershash->{$user}->{'id'} ne '') {
                   11059:                                 ($inst_response{$user},%{$inst_results->{$user}}) =
                   11060:                                     &Apache::lonnet::get_instuser($udom,undef,
                   11061:                                                                   $usershash->{$user}->{'id'});
                   11062:                             } else {
                   11063:                                 ($inst_response{$user},%{$inst_results->{$user}}) =
                   11064:                                     &Apache::lonnet::get_instuser($udom,$uname);
                   11065:                             }
                   11066:                         }
                   11067:                     } else {
                   11068:                        ($inst_response{$user},%{$inst_results->{$user}}) =
                   11069:                             &Apache::lonnet::get_instuser($udom,$uname);
                   11070:                        return;
                   11071:                     }
                   11072:                     if (!$got_rules->{$udom}) {
                   11073:                         my %domconfig = &Apache::lonnet::get_dom('configuration',
                   11074:                                                                  ['usercreation'],$udom);
                   11075:                         if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   11076:                             foreach my $item ('username','id') {
                   11077:                                 if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
                   11078:                                    $$curr_rules{$udom}{$item} =
                   11079:                                        $domconfig{'usercreation'}{$item.'_rule'};
                   11080:                                 }
                   11081:                             }
1.585     raeburn  11082:                         }
1.1075.2.99  raeburn  11083:                         $got_rules->{$udom} = 1;
1.585     raeburn  11084:                     }
                   11085:                 }
1.1075.2.99  raeburn  11086:             } else {
                   11087:                 return;
                   11088:             }
                   11089:         } else {
                   11090:             return;
                   11091:         }
                   11092:         foreach my $user (keys(%{$usershash})) {
                   11093:             my ($uname,$udom) = split(/:/,$user);
                   11094:             next if (($udom eq '') || ($uname eq ''));
                   11095:             my $id;
                   11096:             if (ref($inst_results) eq 'HASH') {
                   11097:                 if (ref($inst_results->{$user}) eq 'HASH') {
                   11098:                     $id = $inst_results->{$user}->{'id'};
                   11099:                 }
                   11100:             }
                   11101:             if ($id eq '') {
                   11102:                 if (ref($usershash->{$user})) {
                   11103:                     $id = $usershash->{$user}->{'id'};
                   11104:                 }
1.585     raeburn  11105:             }
1.612     raeburn  11106:             foreach my $item (keys(%{$checks})) {
                   11107:                 if (ref($$curr_rules{$udom}) eq 'HASH') {
                   11108:                     if (ref($$curr_rules{$udom}{$item}) eq 'ARRAY') {
                   11109:                         if (@{$$curr_rules{$udom}{$item}} > 0) {
1.1075.2.99  raeburn  11110:                             my %rule_check = &Apache::lonnet::inst_rulecheck($udom,$uname,$id,$item,
                   11111:                                                                              $$curr_rules{$udom}{$item});
1.612     raeburn  11112:                             foreach my $rule (@{$$curr_rules{$udom}{$item}}) {
                   11113:                                 if ($rule_check{$rule}) {
                   11114:                                     $$rulematch{$user}{$item} = $rule;
1.1075.2.99  raeburn  11115:                                     if ($inst_response{$user} eq 'ok') {
1.615     raeburn  11116:                                         if (ref($inst_results) eq 'HASH') {
                   11117:                                             if (ref($inst_results->{$user}) eq 'HASH') {
                   11118:                                                 if (keys(%{$inst_results->{$user}}) == 0) {
                   11119:                                                     $$alerts{$item}{$udom}{$uname} = 1;
1.1075.2.99  raeburn  11120:                                                 } elsif ($item eq 'id') {
                   11121:                                                     if ($inst_results->{$user}->{'id'} eq '') {
                   11122:                                                         $$alerts{$item}{$udom}{$uname} = 1;
                   11123:                                                     }
1.615     raeburn  11124:                                                 }
1.612     raeburn  11125:                                             }
                   11126:                                         }
1.615     raeburn  11127:                                     }
                   11128:                                     last;
1.585     raeburn  11129:                                 }
                   11130:                             }
                   11131:                         }
                   11132:                     }
                   11133:                 }
                   11134:             }
                   11135:         }
                   11136:     }
1.612     raeburn  11137:     return;
                   11138: }
                   11139: 
                   11140: sub user_rule_formats {
                   11141:     my ($domain,$domdesc,$curr_rules,$check) = @_;
                   11142:     my %text = ( 
                   11143:                  'username' => 'Usernames',
                   11144:                  'id'       => 'IDs',
                   11145:                );
                   11146:     my $output;
                   11147:     my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($domain,$check);
                   11148:     if ((ref($rules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
                   11149:         if (@{$ruleorder} > 0) {
1.1075.2.20  raeburn  11150:             $output = '<br />'.
                   11151:                       &mt($text{$check}.' with the following format(s) may [_1]only[_2] be used for verified users at [_3]:',
                   11152:                           '<span class="LC_cusr_emph">','</span>',$domdesc).
                   11153:                       ' <ul>';
1.612     raeburn  11154:             foreach my $rule (@{$ruleorder}) {
                   11155:                 if (ref($curr_rules) eq 'ARRAY') {
                   11156:                     if (grep(/^\Q$rule\E$/,@{$curr_rules})) {
                   11157:                         if (ref($rules->{$rule}) eq 'HASH') {
                   11158:                             $output .= '<li>'.$rules->{$rule}{'name'}.': '.
                   11159:                                         $rules->{$rule}{'desc'}.'</li>';
                   11160:                         }
                   11161:                     }
                   11162:                 }
                   11163:             }
                   11164:             $output .= '</ul>';
                   11165:         }
                   11166:     }
                   11167:     return $output;
                   11168: }
                   11169: 
                   11170: sub instrule_disallow_msg {
1.615     raeburn  11171:     my ($checkitem,$domdesc,$count,$mode) = @_;
1.612     raeburn  11172:     my $response;
                   11173:     my %text = (
                   11174:                   item   => 'username',
                   11175:                   items  => 'usernames',
                   11176:                   match  => 'matches',
                   11177:                   do     => 'does',
                   11178:                   action => 'a username',
                   11179:                   one    => 'one',
                   11180:                );
                   11181:     if ($count > 1) {
                   11182:         $text{'item'} = 'usernames';
                   11183:         $text{'match'} ='match';
                   11184:         $text{'do'} = 'do';
                   11185:         $text{'action'} = 'usernames',
                   11186:         $text{'one'} = 'ones';
                   11187:     }
                   11188:     if ($checkitem eq 'id') {
                   11189:         $text{'items'} = 'IDs';
                   11190:         $text{'item'} = 'ID';
                   11191:         $text{'action'} = 'an ID';
1.615     raeburn  11192:         if ($count > 1) {
                   11193:             $text{'item'} = 'IDs';
                   11194:             $text{'action'} = 'IDs';
                   11195:         }
1.612     raeburn  11196:     }
1.674     bisitz   11197:     $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  11198:     if ($mode eq 'upload') {
                   11199:         if ($checkitem eq 'username') {
                   11200:             $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'}.");
                   11201:         } elsif ($checkitem eq 'id') {
1.674     bisitz   11202:             $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  11203:         }
1.669     raeburn  11204:     } elsif ($mode eq 'selfcreate') {
                   11205:         if ($checkitem eq 'id') {
                   11206:             $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.");
                   11207:         }
1.615     raeburn  11208:     } else {
                   11209:         if ($checkitem eq 'username') {
                   11210:             $response .= &mt("You must choose $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
                   11211:         } elsif ($checkitem eq 'id') {
                   11212:             $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.");
                   11213:         }
1.612     raeburn  11214:     }
                   11215:     return $response;
1.585     raeburn  11216: }
                   11217: 
1.624     raeburn  11218: sub personal_data_fieldtitles {
                   11219:     my %fieldtitles = &Apache::lonlocal::texthash (
                   11220:                         id => 'Student/Employee ID',
                   11221:                         permanentemail => 'E-mail address',
                   11222:                         lastname => 'Last Name',
                   11223:                         firstname => 'First Name',
                   11224:                         middlename => 'Middle Name',
                   11225:                         generation => 'Generation',
                   11226:                         gen => 'Generation',
1.765     raeburn  11227:                         inststatus => 'Affiliation',
1.624     raeburn  11228:                    );
                   11229:     return %fieldtitles;
                   11230: }
                   11231: 
1.642     raeburn  11232: sub sorted_inst_types {
                   11233:     my ($dom) = @_;
1.1075.2.70  raeburn  11234:     my ($usertypes,$order);
                   11235:     my %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
                   11236:     if (ref($domdefaults{'inststatus'}) eq 'HASH') {
                   11237:         $usertypes = $domdefaults{'inststatus'}{'inststatustypes'};
                   11238:         $order = $domdefaults{'inststatus'}{'inststatusorder'};
                   11239:     } else {
                   11240:         ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom);
                   11241:     }
1.642     raeburn  11242:     my $othertitle = &mt('All users');
                   11243:     if ($env{'request.course.id'}) {
1.668     raeburn  11244:         $othertitle  = &mt('Any users');
1.642     raeburn  11245:     }
                   11246:     my @types;
                   11247:     if (ref($order) eq 'ARRAY') {
                   11248:         @types = @{$order};
                   11249:     }
                   11250:     if (@types == 0) {
                   11251:         if (ref($usertypes) eq 'HASH') {
                   11252:             @types = sort(keys(%{$usertypes}));
                   11253:         }
                   11254:     }
                   11255:     if (keys(%{$usertypes}) > 0) {
                   11256:         $othertitle = &mt('Other users');
                   11257:     }
                   11258:     return ($othertitle,$usertypes,\@types);
                   11259: }
                   11260: 
1.645     raeburn  11261: sub get_institutional_codes {
1.1075.2.157  raeburn  11262:     my ($cdom,$crs,$settings,$allcourses,$LC_code) = @_;
1.645     raeburn  11263: # Get complete list of course sections to update
                   11264:     my @currsections = ();
                   11265:     my @currxlists = ();
1.1075.2.157  raeburn  11266:     my (%unclutteredsec,%unclutteredlcsec);
1.645     raeburn  11267:     my $coursecode = $$settings{'internal.coursecode'};
1.1075.2.157  raeburn  11268:     my $crskey = $crs.':'.$coursecode;
                   11269:     @{$unclutteredsec{$crskey}} = ();
                   11270:     @{$unclutteredlcsec{$crskey}} = ();
1.645     raeburn  11271: 
                   11272:     if ($$settings{'internal.sectionnums'} ne '') {
                   11273:         @currsections = split(/,/,$$settings{'internal.sectionnums'});
                   11274:     }
                   11275: 
                   11276:     if ($$settings{'internal.crosslistings'} ne '') {
                   11277:         @currxlists = split(/,/,$$settings{'internal.crosslistings'});
                   11278:     }
                   11279: 
                   11280:     if (@currxlists > 0) {
1.1075.2.157  raeburn  11281:         foreach my $xl (@currxlists) {
                   11282:             if ($xl =~ /^([^:]+):(\w*)$/) {
1.645     raeburn  11283:                 unless (grep/^$1$/,@{$allcourses}) {
1.1075.2.119  raeburn  11284:                     push(@{$allcourses},$1);
1.645     raeburn  11285:                     $$LC_code{$1} = $2;
                   11286:                 }
                   11287:             }
                   11288:         }
                   11289:     }
1.1075.2.157  raeburn  11290: 
1.645     raeburn  11291:     if (@currsections > 0) {
1.1075.2.157  raeburn  11292:         foreach my $sec (@currsections) {
                   11293:             if ($sec =~ m/^(\w+):(\w*)$/ ) {
                   11294:                 my $instsec = $1;
1.645     raeburn  11295:                 my $lc_sec = $2;
1.1075.2.157  raeburn  11296:                 unless (grep/^\Q$instsec\E$/,@{$unclutteredsec{$crskey}}) {
                   11297:                     push(@{$unclutteredsec{$crskey}},$instsec);
                   11298:                     push(@{$unclutteredlcsec{$crskey}},$lc_sec);
                   11299:                 }
                   11300:             }
                   11301:         }
                   11302:     }
                   11303: 
                   11304:     if (@{$unclutteredsec{$crskey}} > 0) {
                   11305:         my %formattedsec = &Apache::lonnet::auto_instsec_reformat($cdom,'clutter',\%unclutteredsec);
                   11306:         if ((ref($formattedsec{$crskey}) eq 'ARRAY') && (ref($unclutteredlcsec{$crskey}) eq 'ARRAY')) {
                   11307:             for (my $i=0; $i<@{$formattedsec{$crskey}}; $i++) {
                   11308:                 my $sec = $coursecode.$formattedsec{$crskey}[$i];
                   11309:                 unless (grep/^\Q$sec\E$/,@{$allcourses}) {
1.1075.2.119  raeburn  11310:                     push(@{$allcourses},$sec);
1.1075.2.157  raeburn  11311:                     $$LC_code{$sec} = $unclutteredlcsec{$crskey}[$i];
1.645     raeburn  11312:                 }
                   11313:             }
                   11314:         }
                   11315:     }
                   11316:     return;
                   11317: }
                   11318: 
1.971     raeburn  11319: sub get_standard_codeitems {
                   11320:     return ('Year','Semester','Department','Number','Section');
                   11321: }
                   11322: 
1.112     bowersj2 11323: =pod
                   11324: 
1.780     raeburn  11325: =head1 Slot Helpers
                   11326: 
                   11327: =over 4
                   11328: 
                   11329: =item * sorted_slots()
                   11330: 
1.1040    raeburn  11331: Sorts an array of slot names in order of an optional sort key,
                   11332: default sort is by slot start time (earliest first). 
1.780     raeburn  11333: 
                   11334: Inputs:
                   11335: 
                   11336: =over 4
                   11337: 
                   11338: slotsarr  - Reference to array of unsorted slot names.
                   11339: 
                   11340: slots     - Reference to hash of hash, where outer hash keys are slot names.
                   11341: 
1.1040    raeburn  11342: sortkey   - Name of key in inner hash to be sorted on (e.g., starttime).
                   11343: 
1.549     albertel 11344: =back
                   11345: 
1.780     raeburn  11346: Returns:
                   11347: 
                   11348: =over 4
                   11349: 
1.1040    raeburn  11350: sorted   - An array of slot names sorted by a specified sort key 
                   11351:            (default sort key is start time of the slot).
1.780     raeburn  11352: 
                   11353: =back
                   11354: 
                   11355: =cut
                   11356: 
                   11357: 
                   11358: sub sorted_slots {
1.1040    raeburn  11359:     my ($slotsarr,$slots,$sortkey) = @_;
                   11360:     if ($sortkey eq '') {
                   11361:         $sortkey = 'starttime';
                   11362:     }
1.780     raeburn  11363:     my @sorted;
                   11364:     if ((ref($slotsarr) eq 'ARRAY') && (ref($slots) eq 'HASH')) {
                   11365:         @sorted =
                   11366:             sort {
                   11367:                      if (ref($slots->{$a}) && ref($slots->{$b})) {
1.1040    raeburn  11368:                          return $slots->{$a}{$sortkey} <=> $slots->{$b}{$sortkey}
1.780     raeburn  11369:                      }
                   11370:                      if (ref($slots->{$a})) { return -1;}
                   11371:                      if (ref($slots->{$b})) { return 1;}
                   11372:                      return 0;
                   11373:                  } @{$slotsarr};
                   11374:     }
                   11375:     return @sorted;
                   11376: }
                   11377: 
1.1040    raeburn  11378: =pod
                   11379: 
                   11380: =item * get_future_slots()
                   11381: 
                   11382: Inputs:
                   11383: 
                   11384: =over 4
                   11385: 
                   11386: cnum - course number
                   11387: 
                   11388: cdom - course domain
                   11389: 
                   11390: now - current UNIX time
                   11391: 
                   11392: symb - optional symb
                   11393: 
                   11394: =back
                   11395: 
                   11396: Returns:
                   11397: 
                   11398: =over 4
                   11399: 
                   11400: sorted_reservable - ref to array of student_schedulable slots currently 
                   11401:                     reservable, ordered by end date of reservation period.
                   11402: 
                   11403: reservable_now - ref to hash of student_schedulable slots currently
                   11404:                  reservable.
                   11405: 
                   11406:     Keys in inner hash are:
                   11407:     (a) symb: either blank or symb to which slot use is restricted.
1.1075.2.104  raeburn  11408:     (b) endreserve: end date of reservation period.
                   11409:     (c) uniqueperiod: start,end dates when slot is to be uniquely
                   11410:         selected.
1.1040    raeburn  11411: 
                   11412: sorted_future - ref to array of student_schedulable slots reservable in
                   11413:                 the future, ordered by start date of reservation period.
                   11414: 
                   11415: future_reservable - ref to hash of student_schedulable slots reservable
                   11416:                     in the future.
                   11417: 
                   11418:     Keys in inner hash are:
                   11419:     (a) symb: either blank or symb to which slot use is restricted.
                   11420:     (b) startreserve:  start date of reservation period.
1.1075.2.104  raeburn  11421:     (c) uniqueperiod: start,end dates when slot is to be uniquely
                   11422:         selected.
1.1040    raeburn  11423: 
                   11424: =back
                   11425: 
                   11426: =cut
                   11427: 
                   11428: sub get_future_slots {
                   11429:     my ($cnum,$cdom,$now,$symb) = @_;
                   11430:     my (%reservable_now,%future_reservable,@sorted_reservable,@sorted_future);
                   11431:     my %slots = &Apache::lonnet::get_course_slots($cnum,$cdom);
                   11432:     foreach my $slot (keys(%slots)) {
                   11433:         next unless($slots{$slot}->{'type'} eq 'schedulable_student');
                   11434:         if ($symb) {
                   11435:             next if (($slots{$slot}->{'symb'} ne '') && 
                   11436:                      ($slots{$slot}->{'symb'} ne $symb));
                   11437:         }
                   11438:         if (($slots{$slot}->{'starttime'} > $now) &&
                   11439:             ($slots{$slot}->{'endtime'} > $now)) {
                   11440:             if (($slots{$slot}->{'allowedsections'}) || ($slots{$slot}->{'allowedusers'})) {
                   11441:                 my $userallowed = 0;
                   11442:                 if ($slots{$slot}->{'allowedsections'}) {
                   11443:                     my @allowed_sec = split(',',$slots{$slot}->{'allowedsections'});
                   11444:                     if (!defined($env{'request.role.sec'})
                   11445:                         && grep(/^No section assigned$/,@allowed_sec)) {
                   11446:                         $userallowed=1;
                   11447:                     } else {
                   11448:                         if (grep(/^\Q$env{'request.role.sec'}\E$/,@allowed_sec)) {
                   11449:                             $userallowed=1;
                   11450:                         }
                   11451:                     }
                   11452:                     unless ($userallowed) {
                   11453:                         if (defined($env{'request.course.groups'})) {
                   11454:                             my @groups = split(/:/,$env{'request.course.groups'});
                   11455:                             foreach my $group (@groups) {
                   11456:                                 if (grep(/^\Q$group\E$/,@allowed_sec)) {
                   11457:                                     $userallowed=1;
                   11458:                                     last;
                   11459:                                 }
                   11460:                             }
                   11461:                         }
                   11462:                     }
                   11463:                 }
                   11464:                 if ($slots{$slot}->{'allowedusers'}) {
                   11465:                     my @allowed_users = split(',',$slots{$slot}->{'allowedusers'});
                   11466:                     my $user = $env{'user.name'}.':'.$env{'user.domain'};
                   11467:                     if (grep(/^\Q$user\E$/,@allowed_users)) {
                   11468:                         $userallowed = 1;
                   11469:                     }
                   11470:                 }
                   11471:                 next unless($userallowed);
                   11472:             }
                   11473:             my $startreserve = $slots{$slot}->{'startreserve'};
                   11474:             my $endreserve = $slots{$slot}->{'endreserve'};
                   11475:             my $symb = $slots{$slot}->{'symb'};
1.1075.2.104  raeburn  11476:             my $uniqueperiod;
                   11477:             if (ref($slots{$slot}->{'uniqueperiod'}) eq 'ARRAY') {
                   11478:                 $uniqueperiod = join(',',@{$slots{$slot}->{'uniqueperiod'}});
                   11479:             }
1.1040    raeburn  11480:             if (($startreserve < $now) &&
                   11481:                 (!$endreserve || $endreserve > $now)) {
                   11482:                 my $lastres = $endreserve;
                   11483:                 if (!$lastres) {
                   11484:                     $lastres = $slots{$slot}->{'starttime'};
                   11485:                 }
                   11486:                 $reservable_now{$slot} = {
                   11487:                                            symb       => $symb,
1.1075.2.104  raeburn  11488:                                            endreserve => $lastres,
                   11489:                                            uniqueperiod => $uniqueperiod,   
1.1040    raeburn  11490:                                          };
                   11491:             } elsif (($startreserve > $now) &&
                   11492:                      (!$endreserve || $endreserve > $startreserve)) {
                   11493:                 $future_reservable{$slot} = {
                   11494:                                               symb         => $symb,
1.1075.2.104  raeburn  11495:                                               startreserve => $startreserve,
                   11496:                                               uniqueperiod => $uniqueperiod,
1.1040    raeburn  11497:                                             };
                   11498:             }
                   11499:         }
                   11500:     }
                   11501:     my @unsorted_reservable = keys(%reservable_now);
                   11502:     if (@unsorted_reservable > 0) {
                   11503:         @sorted_reservable = 
                   11504:             &sorted_slots(\@unsorted_reservable,\%reservable_now,'endreserve');
                   11505:     }
                   11506:     my @unsorted_future = keys(%future_reservable);
                   11507:     if (@unsorted_future > 0) {
                   11508:         @sorted_future =
                   11509:             &sorted_slots(\@unsorted_future,\%future_reservable,'startreserve');
                   11510:     }
                   11511:     return (\@sorted_reservable,\%reservable_now,\@sorted_future,\%future_reservable);
                   11512: }
1.780     raeburn  11513: 
                   11514: =pod
                   11515: 
1.1057    foxr     11516: =back
                   11517: 
1.549     albertel 11518: =head1 HTTP Helpers
                   11519: 
                   11520: =over 4
                   11521: 
1.648     raeburn  11522: =item * &get_unprocessed_cgi($query,$possible_names)
1.112     bowersj2 11523: 
1.258     albertel 11524: Modify the %env hash to contain unprocessed CGI form parameters held in
1.112     bowersj2 11525: $query.  The parameters listed in $possible_names (an array reference),
1.258     albertel 11526: will be set in $env{'form.name'} if they do not already exist.
1.112     bowersj2 11527: 
                   11528: Typically called with $ENV{'QUERY_STRING'} as the first parameter.  
                   11529: $possible_names is an ref to an array of form element names.  As an example:
                   11530: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']);
1.258     albertel 11531: will result in $env{'form.uname'} and $env{'form.udom'} being set.
1.112     bowersj2 11532: 
                   11533: =cut
1.1       albertel 11534: 
1.6       albertel 11535: sub get_unprocessed_cgi {
1.25      albertel 11536:   my ($query,$possible_names)= @_;
1.26      matthew  11537:   # $Apache::lonxml::debug=1;
1.356     albertel 11538:   foreach my $pair (split(/&/,$query)) {
                   11539:     my ($name, $value) = split(/=/,$pair);
1.369     www      11540:     $name = &unescape($name);
1.25      albertel 11541:     if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
                   11542:       $value =~ tr/+/ /;
                   11543:       $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.258     albertel 11544:       unless (defined($env{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25      albertel 11545:     }
1.16      harris41 11546:   }
1.6       albertel 11547: }
                   11548: 
1.112     bowersj2 11549: =pod
                   11550: 
1.648     raeburn  11551: =item * &cacheheader() 
1.112     bowersj2 11552: 
                   11553: returns cache-controlling header code
                   11554: 
                   11555: =cut
                   11556: 
1.7       albertel 11557: sub cacheheader {
1.258     albertel 11558:     unless ($env{'request.method'} eq 'GET') { return ''; }
1.216     albertel 11559:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
                   11560:     my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
1.7       albertel 11561:                 <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
                   11562:                 <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
1.216     albertel 11563:     return $output;
1.7       albertel 11564: }
                   11565: 
1.112     bowersj2 11566: =pod
                   11567: 
1.648     raeburn  11568: =item * &no_cache($r) 
1.112     bowersj2 11569: 
                   11570: specifies header code to not have cache
                   11571: 
                   11572: =cut
                   11573: 
1.9       albertel 11574: sub no_cache {
1.216     albertel 11575:     my ($r) = @_;
                   11576:     if ($ENV{'REQUEST_METHOD'} ne 'GET' &&
1.258     albertel 11577: 	$env{'request.method'} ne 'GET') { return ''; }
1.216     albertel 11578:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime(time));
                   11579:     $r->no_cache(1);
                   11580:     $r->header_out("Expires" => $date);
                   11581:     $r->header_out("Pragma" => "no-cache");
1.123     www      11582: }
                   11583: 
                   11584: sub content_type {
1.181     albertel 11585:     my ($r,$type,$charset) = @_;
1.299     foxr     11586:     if ($r) {
                   11587: 	#  Note that printout.pl calls this with undef for $r.
                   11588: 	&no_cache($r);
                   11589:     }
1.258     albertel 11590:     if ($env{'browser.mathml'} && $type eq 'text/html') { $type='text/xml'; }
1.181     albertel 11591:     unless ($charset) {
                   11592: 	$charset=&Apache::lonlocal::current_encoding;
                   11593:     }
                   11594:     if ($charset) { $type.='; charset='.$charset; }
                   11595:     if ($r) {
                   11596: 	$r->content_type($type);
                   11597:     } else {
                   11598: 	print("Content-type: $type\n\n");
                   11599:     }
1.9       albertel 11600: }
1.25      albertel 11601: 
1.112     bowersj2 11602: =pod
                   11603: 
1.648     raeburn  11604: =item * &add_to_env($name,$value) 
1.112     bowersj2 11605: 
1.258     albertel 11606: adds $name to the %env hash with value
1.112     bowersj2 11607: $value, if $name already exists, the entry is converted to an array
                   11608: reference and $value is added to the array.
                   11609: 
                   11610: =cut
                   11611: 
1.25      albertel 11612: sub add_to_env {
                   11613:   my ($name,$value)=@_;
1.258     albertel 11614:   if (defined($env{$name})) {
                   11615:     if (ref($env{$name})) {
1.25      albertel 11616:       #already have multiple values
1.258     albertel 11617:       push(@{ $env{$name} },$value);
1.25      albertel 11618:     } else {
                   11619:       #first time seeing multiple values, convert hash entry to an arrayref
1.258     albertel 11620:       my $first=$env{$name};
                   11621:       undef($env{$name});
                   11622:       push(@{ $env{$name} },$first,$value);
1.25      albertel 11623:     }
                   11624:   } else {
1.258     albertel 11625:     $env{$name}=$value;
1.25      albertel 11626:   }
1.31      albertel 11627: }
1.149     albertel 11628: 
                   11629: =pod
                   11630: 
1.648     raeburn  11631: =item * &get_env_multiple($name) 
1.149     albertel 11632: 
1.258     albertel 11633: gets $name from the %env hash, it seemlessly handles the cases where multiple
1.149     albertel 11634: values may be defined and end up as an array ref.
                   11635: 
                   11636: returns an array of values
                   11637: 
                   11638: =cut
                   11639: 
                   11640: sub get_env_multiple {
                   11641:     my ($name) = @_;
                   11642:     my @values;
1.258     albertel 11643:     if (defined($env{$name})) {
1.149     albertel 11644:         # exists is it an array
1.258     albertel 11645:         if (ref($env{$name})) {
                   11646:             @values=@{ $env{$name} };
1.149     albertel 11647:         } else {
1.258     albertel 11648:             $values[0]=$env{$name};
1.149     albertel 11649:         }
                   11650:     }
                   11651:     return(@values);
                   11652: }
                   11653: 
1.660     raeburn  11654: sub ask_for_embedded_content {
                   11655:     my ($actionurl,$state,$allfiles,$codebase,$args)=@_;
1.1071    raeburn  11656:     my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges,
1.1075.2.11  raeburn  11657:         %currsubfile,%unused,$rem);
1.1071    raeburn  11658:     my $counter = 0;
                   11659:     my $numnew = 0;
1.987     raeburn  11660:     my $numremref = 0;
                   11661:     my $numinvalid = 0;
                   11662:     my $numpathchg = 0;
                   11663:     my $numexisting = 0;
1.1071    raeburn  11664:     my $numunused = 0;
                   11665:     my ($output,$upload_output,$toplevel,$url,$udom,$uname,$getpropath,$cdom,$cnum,
1.1075.2.53  raeburn  11666:         $fileloc,$filename,$delete_output,$modify_output,$title,$symb,$path,$navmap);
1.1071    raeburn  11667:     my $heading = &mt('Upload embedded files');
                   11668:     my $buttontext = &mt('Upload');
                   11669: 
1.1075.2.11  raeburn  11670:     if ($env{'request.course.id'}) {
1.1075.2.35  raeburn  11671:         if ($actionurl eq '/adm/dependencies') {
                   11672:             $navmap = Apache::lonnavmaps::navmap->new();
                   11673:         }
                   11674:         $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   11675:         $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1075.2.11  raeburn  11676:     }
1.1075.2.35  raeburn  11677:     if (($actionurl eq '/adm/portfolio') ||
                   11678:         ($actionurl eq '/adm/coursegrp_portfolio')) {
1.984     raeburn  11679:         my $current_path='/';
                   11680:         if ($env{'form.currentpath'}) {
                   11681:             $current_path = $env{'form.currentpath'};
                   11682:         }
                   11683:         if ($actionurl eq '/adm/coursegrp_portfolio') {
1.1075.2.35  raeburn  11684:             $udom = $cdom;
                   11685:             $uname = $cnum;
1.984     raeburn  11686:             $url = '/userfiles/groups/'.$env{'form.group'}.'/portfolio';
                   11687:         } else {
                   11688:             $udom = $env{'user.domain'};
                   11689:             $uname = $env{'user.name'};
                   11690:             $url = '/userfiles/portfolio';
                   11691:         }
1.987     raeburn  11692:         $toplevel = $url.'/';
1.984     raeburn  11693:         $url .= $current_path;
                   11694:         $getpropath = 1;
1.987     raeburn  11695:     } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
                   11696:              ($actionurl eq '/adm/imsimport')) { 
1.1022    www      11697:         my ($udom,$uname,$rest) = ($args->{'current_path'} =~ m{/priv/($match_domain)/($match_username)/?(.*)$});
1.1026    raeburn  11698:         $url = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname/";
1.987     raeburn  11699:         $toplevel = $url;
1.984     raeburn  11700:         if ($rest ne '') {
1.987     raeburn  11701:             $url .= $rest;
                   11702:         }
                   11703:     } elsif ($actionurl eq '/adm/coursedocs') {
                   11704:         if (ref($args) eq 'HASH') {
1.1071    raeburn  11705:             $url = $args->{'docs_url'};
                   11706:             $toplevel = $url;
1.1075.2.11  raeburn  11707:             if ($args->{'context'} eq 'paste') {
                   11708:                 ($cdom,$cnum) = ($url =~ m{^\Q/uploaded/\E($match_domain)/($match_courseid)/});
                   11709:                 ($path) =
                   11710:                     ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
                   11711:                 $fileloc = &Apache::lonnet::filelocation('',$toplevel);
                   11712:                 $fileloc =~ s{^/}{};
                   11713:             }
1.1071    raeburn  11714:         }
                   11715:     } elsif ($actionurl eq '/adm/dependencies') {
                   11716:         if ($env{'request.course.id'} ne '') {
                   11717:             if (ref($args) eq 'HASH') {
                   11718:                 $url = $args->{'docs_url'};
                   11719:                 $title = $args->{'docs_title'};
1.1075.2.35  raeburn  11720:                 $toplevel = $url;
                   11721:                 unless ($toplevel =~ m{^/}) {
                   11722:                     $toplevel = "/$url";
                   11723:                 }
1.1075.2.11  raeburn  11724:                 ($rem) = ($toplevel =~ m{^(.+/)[^/]+$});
1.1075.2.35  raeburn  11725:                 if ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E)}) {
                   11726:                     $path = $1;
                   11727:                 } else {
                   11728:                     ($path) =
                   11729:                         ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
                   11730:                 }
1.1075.2.79  raeburn  11731:                 if ($toplevel=~/^\/*(uploaded|editupload)/) {
                   11732:                     $fileloc = $toplevel;
                   11733:                     $fileloc=~ s/^\s*(\S+)\s*$/$1/;
                   11734:                     my ($udom,$uname,$fname) =
                   11735:                         ($fileloc=~ m{^/+(?:uploaded|editupload)/+($match_domain)/+($match_name)/+(.*)$});
                   11736:                     $fileloc = propath($udom,$uname).'/userfiles/'.$fname;
                   11737:                 } else {
                   11738:                     $fileloc = &Apache::lonnet::filelocation('',$toplevel);
                   11739:                 }
1.1071    raeburn  11740:                 $fileloc =~ s{^/}{};
                   11741:                 ($filename) = ($fileloc =~ m{.+/([^/]+)$});
                   11742:                 $heading = &mt('Status of dependencies in [_1]',"$title ($filename)");
                   11743:             }
1.987     raeburn  11744:         }
1.1075.2.35  raeburn  11745:     } elsif ($actionurl eq "/public/$cdom/$cnum/syllabus") {
                   11746:         $udom = $cdom;
                   11747:         $uname = $cnum;
                   11748:         $url = "/uploaded/$cdom/$cnum/portfolio/syllabus";
                   11749:         $toplevel = $url;
                   11750:         $path = $url;
                   11751:         $fileloc = &Apache::lonnet::filelocation('',$toplevel).'/';
                   11752:         $fileloc =~ s{^/}{};
                   11753:     }
                   11754:     foreach my $file (keys(%{$allfiles})) {
                   11755:         my $embed_file;
                   11756:         if (($path eq "/uploaded/$cdom/$cnum/portfolio/syllabus") && ($file =~ m{^\Q$path/\E(.+)$})) {
                   11757:             $embed_file = $1;
                   11758:         } else {
                   11759:             $embed_file = $file;
                   11760:         }
1.1075.2.55  raeburn  11761:         my ($absolutepath,$cleaned_file);
                   11762:         if ($embed_file =~ m{^\w+://}) {
                   11763:             $cleaned_file = $embed_file;
1.1075.2.47  raeburn  11764:             $newfiles{$cleaned_file} = 1;
                   11765:             $mapping{$cleaned_file} = $embed_file;
1.987     raeburn  11766:         } else {
1.1075.2.55  raeburn  11767:             $cleaned_file = &clean_path($embed_file);
1.987     raeburn  11768:             if ($embed_file =~ m{^/}) {
                   11769:                 $absolutepath = $embed_file;
                   11770:             }
1.1075.2.47  raeburn  11771:             if ($cleaned_file =~ m{/}) {
                   11772:                 my ($path,$fname) = ($cleaned_file =~ m{^(.+)/([^/]*)$});
1.987     raeburn  11773:                 $path = &check_for_traversal($path,$url,$toplevel);
                   11774:                 my $item = $fname;
                   11775:                 if ($path ne '') {
                   11776:                     $item = $path.'/'.$fname;
                   11777:                     $subdependencies{$path}{$fname} = 1;
                   11778:                 } else {
                   11779:                     $dependencies{$item} = 1;
                   11780:                 }
                   11781:                 if ($absolutepath) {
                   11782:                     $mapping{$item} = $absolutepath;
                   11783:                 } else {
                   11784:                     $mapping{$item} = $embed_file;
                   11785:                 }
                   11786:             } else {
                   11787:                 $dependencies{$embed_file} = 1;
                   11788:                 if ($absolutepath) {
1.1075.2.47  raeburn  11789:                     $mapping{$cleaned_file} = $absolutepath;
1.987     raeburn  11790:                 } else {
1.1075.2.47  raeburn  11791:                     $mapping{$cleaned_file} = $embed_file;
1.987     raeburn  11792:                 }
                   11793:             }
1.984     raeburn  11794:         }
                   11795:     }
1.1071    raeburn  11796:     my $dirptr = 16384;
1.984     raeburn  11797:     foreach my $path (keys(%subdependencies)) {
1.1071    raeburn  11798:         $currsubfile{$path} = {};
1.1075.2.35  raeburn  11799:         if (($actionurl eq '/adm/portfolio') ||
                   11800:             ($actionurl eq '/adm/coursegrp_portfolio')) { 
1.1021    raeburn  11801:             my ($sublistref,$listerror) =
                   11802:                 &Apache::lonnet::dirlist($url.$path,$udom,$uname,$getpropath);
                   11803:             if (ref($sublistref) eq 'ARRAY') {
                   11804:                 foreach my $line (@{$sublistref}) {
                   11805:                     my ($file_name,$rest) = split(/\&/,$line,2);
1.1071    raeburn  11806:                     $currsubfile{$path}{$file_name} = 1;
1.1021    raeburn  11807:                 }
1.984     raeburn  11808:             }
1.987     raeburn  11809:         } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984     raeburn  11810:             if (opendir(my $dir,$url.'/'.$path)) {
                   11811:                 my @subdir_list = grep(!/^\./,readdir($dir));
1.1071    raeburn  11812:                 map {$currsubfile{$path}{$_} = 1;} @subdir_list;
                   11813:             }
1.1075.2.11  raeburn  11814:         } elsif (($actionurl eq '/adm/dependencies') ||
                   11815:                  (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
1.1075.2.35  raeburn  11816:                   ($args->{'context'} eq 'paste')) ||
                   11817:                  ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
1.1071    raeburn  11818:             if ($env{'request.course.id'} ne '') {
1.1075.2.35  raeburn  11819:                 my $dir;
                   11820:                 if ($actionurl eq "/public/$cdom/$cnum/syllabus") {
                   11821:                     $dir = $fileloc;
                   11822:                 } else {
                   11823:                     ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
                   11824:                 }
1.1071    raeburn  11825:                 if ($dir ne '') {
                   11826:                     my ($sublistref,$listerror) =
                   11827:                         &Apache::lonnet::dirlist($dir.$path,$cdom,$cnum,$getpropath,undef,'/');
                   11828:                     if (ref($sublistref) eq 'ARRAY') {
                   11829:                         foreach my $line (@{$sublistref}) {
                   11830:                             my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,$size,
                   11831:                                 undef,$mtime)=split(/\&/,$line,12);
                   11832:                             unless (($testdir&$dirptr) ||
                   11833:                                     ($file_name =~ /^\.\.?$/)) {
                   11834:                                 $currsubfile{$path}{$file_name} = [$size,$mtime];
                   11835:                             }
                   11836:                         }
                   11837:                     }
                   11838:                 }
1.984     raeburn  11839:             }
                   11840:         }
                   11841:         foreach my $file (keys(%{$subdependencies{$path}})) {
1.1071    raeburn  11842:             if (exists($currsubfile{$path}{$file})) {
1.987     raeburn  11843:                 my $item = $path.'/'.$file;
                   11844:                 unless ($mapping{$item} eq $item) {
                   11845:                     $pathchanges{$item} = 1;
                   11846:                 }
                   11847:                 $existing{$item} = 1;
                   11848:                 $numexisting ++;
                   11849:             } else {
                   11850:                 $newfiles{$path.'/'.$file} = 1;
1.984     raeburn  11851:             }
                   11852:         }
1.1071    raeburn  11853:         if ($actionurl eq '/adm/dependencies') {
                   11854:             foreach my $path (keys(%currsubfile)) {
                   11855:                 if (ref($currsubfile{$path}) eq 'HASH') {
                   11856:                     foreach my $file (keys(%{$currsubfile{$path}})) {
                   11857:                          unless ($subdependencies{$path}{$file}) {
1.1075.2.11  raeburn  11858:                              next if (($rem ne '') &&
                   11859:                                       (($env{"httpref.$rem"."$path/$file"} ne '') ||
                   11860:                                        (ref($navmap) &&
                   11861:                                        (($navmap->getResourceByUrl($rem."$path/$file") ne '') ||
                   11862:                                         (($file =~ /^(.*\.s?html?)\.bak$/i) &&
                   11863:                                          ($navmap->getResourceByUrl($rem."$path/$1")))))));
1.1071    raeburn  11864:                              $unused{$path.'/'.$file} = 1; 
                   11865:                          }
                   11866:                     }
                   11867:                 }
                   11868:             }
                   11869:         }
1.984     raeburn  11870:     }
1.987     raeburn  11871:     my %currfile;
1.1075.2.35  raeburn  11872:     if (($actionurl eq '/adm/portfolio') ||
                   11873:         ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021    raeburn  11874:         my ($dirlistref,$listerror) =
                   11875:             &Apache::lonnet::dirlist($url,$udom,$uname,$getpropath);
                   11876:         if (ref($dirlistref) eq 'ARRAY') {
                   11877:             foreach my $line (@{$dirlistref}) {
                   11878:                 my ($file_name,$rest) = split(/\&/,$line,2);
                   11879:                 $currfile{$file_name} = 1;
                   11880:             }
1.984     raeburn  11881:         }
1.987     raeburn  11882:     } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984     raeburn  11883:         if (opendir(my $dir,$url)) {
1.987     raeburn  11884:             my @dir_list = grep(!/^\./,readdir($dir));
1.984     raeburn  11885:             map {$currfile{$_} = 1;} @dir_list;
                   11886:         }
1.1075.2.11  raeburn  11887:     } elsif (($actionurl eq '/adm/dependencies') ||
                   11888:              (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
1.1075.2.35  raeburn  11889:               ($args->{'context'} eq 'paste')) ||
                   11890:              ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
1.1071    raeburn  11891:         if ($env{'request.course.id'} ne '') {
                   11892:             my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
                   11893:             if ($dir ne '') {
                   11894:                 my ($dirlistref,$listerror) =
                   11895:                     &Apache::lonnet::dirlist($dir,$cdom,$cnum,$getpropath,undef,'/');
                   11896:                 if (ref($dirlistref) eq 'ARRAY') {
                   11897:                     foreach my $line (@{$dirlistref}) {
                   11898:                         my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,
                   11899:                             $size,undef,$mtime)=split(/\&/,$line,12);
                   11900:                         unless (($testdir&$dirptr) ||
                   11901:                                 ($file_name =~ /^\.\.?$/)) {
                   11902:                             $currfile{$file_name} = [$size,$mtime];
                   11903:                         }
                   11904:                     }
                   11905:                 }
                   11906:             }
                   11907:         }
1.984     raeburn  11908:     }
                   11909:     foreach my $file (keys(%dependencies)) {
1.1071    raeburn  11910:         if (exists($currfile{$file})) {
1.987     raeburn  11911:             unless ($mapping{$file} eq $file) {
                   11912:                 $pathchanges{$file} = 1;
                   11913:             }
                   11914:             $existing{$file} = 1;
                   11915:             $numexisting ++;
                   11916:         } else {
1.984     raeburn  11917:             $newfiles{$file} = 1;
                   11918:         }
                   11919:     }
1.1071    raeburn  11920:     foreach my $file (keys(%currfile)) {
                   11921:         unless (($file eq $filename) ||
                   11922:                 ($file eq $filename.'.bak') ||
                   11923:                 ($dependencies{$file})) {
1.1075.2.11  raeburn  11924:             if ($actionurl eq '/adm/dependencies') {
1.1075.2.35  raeburn  11925:                 unless ($toplevel =~ m{^\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E}) {
                   11926:                     next if (($rem ne '') &&
                   11927:                              (($env{"httpref.$rem".$file} ne '') ||
                   11928:                               (ref($navmap) &&
                   11929:                               (($navmap->getResourceByUrl($rem.$file) ne '') ||
                   11930:                                (($file =~ /^(.*\.s?html?)\.bak$/i) &&
                   11931:                                 ($navmap->getResourceByUrl($rem.$1)))))));
                   11932:                 }
1.1075.2.11  raeburn  11933:             }
1.1071    raeburn  11934:             $unused{$file} = 1;
                   11935:         }
                   11936:     }
1.1075.2.11  raeburn  11937:     if (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
                   11938:         ($args->{'context'} eq 'paste')) {
                   11939:         $counter = scalar(keys(%existing));
                   11940:         $numpathchg = scalar(keys(%pathchanges));
                   11941:         return ($output,$counter,$numpathchg,\%existing);
1.1075.2.35  raeburn  11942:     } elsif (($actionurl eq "/public/$cdom/$cnum/syllabus") &&
                   11943:              (ref($args) eq 'HASH') && ($args->{'context'} eq 'rewrites')) {
                   11944:         $counter = scalar(keys(%existing));
                   11945:         $numpathchg = scalar(keys(%pathchanges));
                   11946:         return ($output,$counter,$numpathchg,\%existing,\%mapping);
1.1075.2.11  raeburn  11947:     }
1.984     raeburn  11948:     foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%newfiles)) {
1.1071    raeburn  11949:         if ($actionurl eq '/adm/dependencies') {
                   11950:             next if ($embed_file =~ m{^\w+://});
                   11951:         }
1.660     raeburn  11952:         $upload_output .= &start_data_table_row().
1.1075.2.35  raeburn  11953:                           '<td valign="top"><img src="'.&icon($embed_file).'" />&nbsp;'.
1.1071    raeburn  11954:                           '<span class="LC_filename">'.$embed_file.'</span>';
1.987     raeburn  11955:         unless ($mapping{$embed_file} eq $embed_file) {
1.1075.2.35  raeburn  11956:             $upload_output .= '<br /><span class="LC_info" style="font-size:smaller;">'.
                   11957:                               &mt('changed from: [_1]',$mapping{$embed_file}).'</span>';
1.987     raeburn  11958:         }
1.1075.2.35  raeburn  11959:         $upload_output .= '</td>';
1.1071    raeburn  11960:         if ($args->{'ignore_remote_references'} && $embed_file =~ m{^\w+://}) { 
1.1075.2.35  raeburn  11961:             $upload_output.='<td align="right">'.
                   11962:                             '<span class="LC_info LC_fontsize_medium">'.
                   11963:                             &mt("URL points to web address").'</span>';
1.987     raeburn  11964:             $numremref++;
1.660     raeburn  11965:         } elsif ($args->{'error_on_invalid_names'}
                   11966:             && $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) {
1.1075.2.35  raeburn  11967:             $upload_output.='<td align="right"><span class="LC_warning">'.
                   11968:                             &mt('Invalid characters').'</span>';
1.987     raeburn  11969:             $numinvalid++;
1.660     raeburn  11970:         } else {
1.1075.2.35  raeburn  11971:             $upload_output .= '<td>'.
                   11972:                               &embedded_file_element('upload_embedded',$counter,
1.987     raeburn  11973:                                                      $embed_file,\%mapping,
1.1071    raeburn  11974:                                                      $allfiles,$codebase,'upload');
                   11975:             $counter ++;
                   11976:             $numnew ++;
1.987     raeburn  11977:         }
                   11978:         $upload_output .= '</td>'.&Apache::loncommon::end_data_table_row()."\n";
                   11979:     }
                   11980:     foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%existing)) {
1.1071    raeburn  11981:         if ($actionurl eq '/adm/dependencies') {
                   11982:             my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$embed_file);
                   11983:             $modify_output .= &start_data_table_row().
                   11984:                               '<td><a href="'.$path.'/'.$embed_file.'" style="text-decoration:none;">'.
                   11985:                               '<img src="'.&icon($embed_file).'" border="0" />'.
                   11986:                               '&nbsp;<span class="LC_filename">'.$embed_file.'</span></a></td>'.
                   11987:                               '<td>'.$size.'</td>'.
                   11988:                               '<td>'.$mtime.'</td>'.
                   11989:                               '<td><label><input type="checkbox" name="mod_upload_dep" '.
                   11990:                               'onclick="toggleBrowse('."'$counter'".')" id="mod_upload_dep_'.
                   11991:                               $counter.'" value="'.$counter.'" />'.&mt('Yes').'</label>'.
                   11992:                               '<div id="moduploaddep_'.$counter.'" style="display:none;">'.
                   11993:                               &embedded_file_element('upload_embedded',$counter,
                   11994:                                                      $embed_file,\%mapping,
                   11995:                                                      $allfiles,$codebase,'modify').
                   11996:                               '</div></td>'.
                   11997:                               &end_data_table_row()."\n";
                   11998:             $counter ++;
                   11999:         } else {
                   12000:             $upload_output .= &start_data_table_row().
1.1075.2.35  raeburn  12001:                               '<td valign="top"><img src="'.&icon($embed_file).'" />&nbsp;'.
                   12002:                               '<span class="LC_filename">'.$embed_file.'</span></td>'.
                   12003:                               '<td align="right"><span class="LC_info LC_fontsize_medium">'.&mt('Already exists').'</span></td>'.
1.1071    raeburn  12004:                               &Apache::loncommon::end_data_table_row()."\n";
                   12005:         }
                   12006:     }
                   12007:     my $delidx = $counter;
                   12008:     foreach my $oldfile (sort {lc($a) cmp lc($b)} keys(%unused)) {
                   12009:         my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$oldfile);
                   12010:         $delete_output .= &start_data_table_row().
                   12011:                           '<td><img src="'.&icon($oldfile).'" />'.
                   12012:                           '&nbsp;<span class="LC_filename">'.$oldfile.'</span></td>'.
                   12013:                           '<td>'.$size.'</td>'.
                   12014:                           '<td>'.$mtime.'</td>'.
                   12015:                           '<td><label><input type="checkbox" name="del_upload_dep" '.
                   12016:                           ' value="'.$delidx.'" />'.&mt('Yes').'</label>'.
                   12017:                           &embedded_file_element('upload_embedded',$delidx,
                   12018:                                                  $oldfile,\%mapping,$allfiles,
                   12019:                                                  $codebase,'delete').'</td>'.
                   12020:                           &end_data_table_row()."\n"; 
                   12021:         $numunused ++;
                   12022:         $delidx ++;
1.987     raeburn  12023:     }
                   12024:     if ($upload_output) {
                   12025:         $upload_output = &start_data_table().
                   12026:                          $upload_output.
                   12027:                          &end_data_table()."\n";
                   12028:     }
1.1071    raeburn  12029:     if ($modify_output) {
                   12030:         $modify_output = &start_data_table().
                   12031:                          &start_data_table_header_row().
                   12032:                          '<th>'.&mt('File').'</th>'.
                   12033:                          '<th>'.&mt('Size (KB)').'</th>'.
                   12034:                          '<th>'.&mt('Modified').'</th>'.
                   12035:                          '<th>'.&mt('Upload replacement?').'</th>'.
                   12036:                          &end_data_table_header_row().
                   12037:                          $modify_output.
                   12038:                          &end_data_table()."\n";
                   12039:     }
                   12040:     if ($delete_output) {
                   12041:         $delete_output = &start_data_table().
                   12042:                          &start_data_table_header_row().
                   12043:                          '<th>'.&mt('File').'</th>'.
                   12044:                          '<th>'.&mt('Size (KB)').'</th>'.
                   12045:                          '<th>'.&mt('Modified').'</th>'.
                   12046:                          '<th>'.&mt('Delete?').'</th>'.
                   12047:                          &end_data_table_header_row().
                   12048:                          $delete_output.
                   12049:                          &end_data_table()."\n";
                   12050:     }
1.987     raeburn  12051:     my $applies = 0;
                   12052:     if ($numremref) {
                   12053:         $applies ++;
                   12054:     }
                   12055:     if ($numinvalid) {
                   12056:         $applies ++;
                   12057:     }
                   12058:     if ($numexisting) {
                   12059:         $applies ++;
                   12060:     }
1.1071    raeburn  12061:     if ($counter || $numunused) {
1.987     raeburn  12062:         $output = '<form name="upload_embedded" action="'.$actionurl.'"'.
                   12063:                   ' method="post" enctype="multipart/form-data">'."\n".
1.1071    raeburn  12064:                   $state.'<h3>'.$heading.'</h3>'; 
                   12065:         if ($actionurl eq '/adm/dependencies') {
                   12066:             if ($numnew) {
                   12067:                 $output .= '<h4>'.&mt('Missing dependencies').'</h4>'.
                   12068:                            '<p>'.&mt('The following files need to be uploaded.').'</p>'."\n".
                   12069:                            $upload_output.'<br />'."\n";
                   12070:             }
                   12071:             if ($numexisting) {
                   12072:                 $output .= '<h4>'.&mt('Uploaded dependencies (in use)').'</h4>'.
                   12073:                            '<p>'.&mt('Upload a new file to replace the one currently in use.').'</p>'."\n".
                   12074:                            $modify_output.'<br />'."\n";
                   12075:                            $buttontext = &mt('Save changes');
                   12076:             }
                   12077:             if ($numunused) {
                   12078:                 $output .= '<h4>'.&mt('Unused files').'</h4>'.
                   12079:                            '<p>'.&mt('The following uploaded files are no longer used.').'</p>'."\n".
                   12080:                            $delete_output.'<br />'."\n";
                   12081:                            $buttontext = &mt('Save changes');
                   12082:             }
                   12083:         } else {
                   12084:             $output .= $upload_output.'<br />'."\n";
                   12085:         }
                   12086:         $output .= '<input type ="hidden" name="number_embedded_items" value="'.
                   12087:                    $counter.'" />'."\n";
                   12088:         if ($actionurl eq '/adm/dependencies') { 
                   12089:             $output .= '<input type ="hidden" name="number_newemb_items" value="'.
                   12090:                        $numnew.'" />'."\n";
                   12091:         } elsif ($actionurl eq '') {
1.987     raeburn  12092:             $output .=  '<input type="hidden" name="phase" value="three" />';
                   12093:         }
                   12094:     } elsif ($applies) {
                   12095:         $output = '<b>'.&mt('Referenced files').'</b>:<br />';
                   12096:         if ($applies > 1) {
                   12097:             $output .=  
1.1075.2.35  raeburn  12098:                 &mt('No dependencies need to be uploaded, as one of the following applies to each reference:').'<ul>';
1.987     raeburn  12099:             if ($numremref) {
                   12100:                 $output .= '<li>'.&mt('reference is to a URL which points to another server').'</li>'."\n";
                   12101:             }
                   12102:             if ($numinvalid) {
                   12103:                 $output .= '<li>'.&mt('reference is to file with a name containing invalid characters').'</li>'."\n";
                   12104:             }
                   12105:             if ($numexisting) {
                   12106:                 $output .= '<li>'.&mt('reference is to an existing file at the specified location').'</li>'."\n";
                   12107:             }
                   12108:             $output .= '</ul><br />';
                   12109:         } elsif ($numremref) {
                   12110:             $output .= '<p>'.&mt('None to upload, as all references are to URLs pointing to another server.').'</p>';
                   12111:         } elsif ($numinvalid) {
                   12112:             $output .= '<p>'.&mt('None to upload, as all references are to files with names containing invalid characters.').'</p>';
                   12113:         } elsif ($numexisting) {
                   12114:             $output .= '<p>'.&mt('None to upload, as all references are to existing files.').'</p>';
                   12115:         }
                   12116:         $output .= $upload_output.'<br />';
                   12117:     }
                   12118:     my ($pathchange_output,$chgcount);
1.1071    raeburn  12119:     $chgcount = $counter;
1.987     raeburn  12120:     if (keys(%pathchanges) > 0) {
                   12121:         foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%pathchanges)) {
1.1071    raeburn  12122:             if ($counter) {
1.987     raeburn  12123:                 $output .= &embedded_file_element('pathchange',$chgcount,
                   12124:                                                   $embed_file,\%mapping,
1.1071    raeburn  12125:                                                   $allfiles,$codebase,'change');
1.987     raeburn  12126:             } else {
                   12127:                 $pathchange_output .= 
                   12128:                     &start_data_table_row().
                   12129:                     '<td><input type ="checkbox" name="namechange" value="'.
                   12130:                     $chgcount.'" checked="checked" /></td>'.
                   12131:                     '<td>'.$mapping{$embed_file}.'</td>'.
                   12132:                     '<td>'.$embed_file.
                   12133:                     &embedded_file_element('pathchange',$numpathchg,$embed_file,
1.1071    raeburn  12134:                                            \%mapping,$allfiles,$codebase,'change').
1.987     raeburn  12135:                     '</td>'.&end_data_table_row();
1.660     raeburn  12136:             }
1.987     raeburn  12137:             $numpathchg ++;
                   12138:             $chgcount ++;
1.660     raeburn  12139:         }
                   12140:     }
1.1075.2.35  raeburn  12141:     if (($counter) || ($numunused)) {
1.987     raeburn  12142:         if ($numpathchg) {
                   12143:             $output .= '<input type ="hidden" name="number_pathchange_items" value="'.
                   12144:                        $numpathchg.'" />'."\n";
                   12145:         }
                   12146:         if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') || 
                   12147:             ($actionurl eq '/adm/imsimport')) {
                   12148:             $output .= '<input type="hidden" name="phase" value="three" />'."\n";
                   12149:         } elsif ($actionurl eq '/adm/portfolio' || $actionurl eq '/adm/coursegrp_portfolio') {
                   12150:             $output .= '<input type="hidden" name="action" value="upload_embedded" />';
1.1071    raeburn  12151:         } elsif ($actionurl eq '/adm/dependencies') {
                   12152:             $output .= '<input type="hidden" name="action" value="process_changes" />';
1.987     raeburn  12153:         }
1.1075.2.35  raeburn  12154:         $output .= '<input type ="submit" value="'.$buttontext.'" />'."\n".'</form>'."\n";
1.987     raeburn  12155:     } elsif ($numpathchg) {
                   12156:         my %pathchange = ();
                   12157:         $output .= &modify_html_form('pathchange',$actionurl,$state,\%pathchange,$pathchange_output);
                   12158:         if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
                   12159:             $output .= '<p>'.&mt('or').'</p>'; 
1.1075.2.35  raeburn  12160:         }
1.987     raeburn  12161:     }
1.1071    raeburn  12162:     return ($output,$counter,$numpathchg);
1.987     raeburn  12163: }
                   12164: 
1.1075.2.47  raeburn  12165: =pod
                   12166: 
                   12167: =item * clean_path($name)
                   12168: 
                   12169: Performs clean-up of directories, subdirectories and filename in an
                   12170: embedded object, referenced in an HTML file which is being uploaded
                   12171: to a course or portfolio, where
                   12172: "Upload embedded images/multimedia files if HTML file" checkbox was
                   12173: checked.
                   12174: 
                   12175: Clean-up is similar to replacements in lonnet::clean_filename()
                   12176: except each / between sub-directory and next level is preserved.
                   12177: 
                   12178: =cut
                   12179: 
                   12180: sub clean_path {
                   12181:     my ($embed_file) = @_;
                   12182:     $embed_file =~s{^/+}{};
                   12183:     my @contents;
                   12184:     if ($embed_file =~ m{/}) {
                   12185:         @contents = split(/\//,$embed_file);
                   12186:     } else {
                   12187:         @contents = ($embed_file);
                   12188:     }
                   12189:     my $lastidx = scalar(@contents)-1;
                   12190:     for (my $i=0; $i<=$lastidx; $i++) {
                   12191:         $contents[$i]=~s{\\}{/}g;
                   12192:         $contents[$i]=~s/\s+/\_/g;
                   12193:         $contents[$i]=~s{[^/\w\.\-]}{}g;
                   12194:         if ($i == $lastidx) {
                   12195:             $contents[$i]=~s/\.(\d+)(?=\.)/_$1/g;
                   12196:         }
                   12197:     }
                   12198:     if ($lastidx > 0) {
                   12199:         return join('/',@contents);
                   12200:     } else {
                   12201:         return $contents[0];
                   12202:     }
                   12203: }
                   12204: 
1.987     raeburn  12205: sub embedded_file_element {
1.1071    raeburn  12206:     my ($context,$num,$embed_file,$mapping,$allfiles,$codebase,$type) = @_;
1.987     raeburn  12207:     return unless ((ref($mapping) eq 'HASH') && (ref($allfiles) eq 'HASH') &&
                   12208:                    (ref($codebase) eq 'HASH'));
                   12209:     my $output;
1.1071    raeburn  12210:     if (($context eq 'upload_embedded') && ($type ne 'delete')) {
1.987     raeburn  12211:        $output = '<input name="embedded_item_'.$num.'" type="file" value="" />'."\n";
                   12212:     }
                   12213:     $output .= '<input name="embedded_orig_'.$num.'" type="hidden" value="'.
                   12214:                &escape($embed_file).'" />';
                   12215:     unless (($context eq 'upload_embedded') && 
                   12216:             ($mapping->{$embed_file} eq $embed_file)) {
                   12217:         $output .='
                   12218:         <input name="embedded_ref_'.$num.'" type="hidden" value="'.&escape($mapping->{$embed_file}).'" />';
                   12219:     }
                   12220:     my $attrib;
                   12221:     if (ref($allfiles->{$mapping->{$embed_file}}) eq 'ARRAY') {
                   12222:         $attrib = &escape(join(':',@{$allfiles->{$mapping->{$embed_file}}}));
                   12223:     }
                   12224:     $output .=
                   12225:         "\n\t\t".
                   12226:         '<input name="embedded_attrib_'.$num.'" type="hidden" value="'.
                   12227:         $attrib.'" />';
                   12228:     if (exists($codebase->{$mapping->{$embed_file}})) {
                   12229:         $output .=
                   12230:             "\n\t\t".
                   12231:             '<input name="codebase_'.$num.'" type="hidden" value="'.
                   12232:             &escape($codebase->{$mapping->{$embed_file}}).'" />';
1.984     raeburn  12233:     }
1.987     raeburn  12234:     return $output;
1.660     raeburn  12235: }
                   12236: 
1.1071    raeburn  12237: sub get_dependency_details {
                   12238:     my ($currfile,$currsubfile,$embed_file) = @_;
                   12239:     my ($size,$mtime,$showsize,$showmtime);
                   12240:     if ((ref($currfile) eq 'HASH') && (ref($currsubfile))) {
                   12241:         if ($embed_file =~ m{/}) {
                   12242:             my ($path,$fname) = split(/\//,$embed_file);
                   12243:             if (ref($currsubfile->{$path}{$fname}) eq 'ARRAY') {
                   12244:                 ($size,$mtime) = @{$currsubfile->{$path}{$fname}};
                   12245:             }
                   12246:         } else {
                   12247:             if (ref($currfile->{$embed_file}) eq 'ARRAY') {
                   12248:                 ($size,$mtime) = @{$currfile->{$embed_file}};
                   12249:             }
                   12250:         }
                   12251:         $showsize = $size/1024.0;
                   12252:         $showsize = sprintf("%.1f",$showsize);
                   12253:         if ($mtime > 0) {
                   12254:             $showmtime = &Apache::lonlocal::locallocaltime($mtime);
                   12255:         }
                   12256:     }
                   12257:     return ($showsize,$showmtime);
                   12258: }
                   12259: 
                   12260: sub ask_embedded_js {
                   12261:     return <<"END";
                   12262: <script type="text/javascript"">
                   12263: // <![CDATA[
                   12264: function toggleBrowse(counter) {
                   12265:     var chkboxid = document.getElementById('mod_upload_dep_'+counter);
                   12266:     var fileid = document.getElementById('embedded_item_'+counter);
                   12267:     var uploaddivid = document.getElementById('moduploaddep_'+counter);
                   12268:     if (chkboxid.checked == true) {
                   12269:         uploaddivid.style.display='block';
                   12270:     } else {
                   12271:         uploaddivid.style.display='none';
                   12272:         fileid.value = '';
                   12273:     }
                   12274: }
                   12275: // ]]>
                   12276: </script>
                   12277: 
                   12278: END
                   12279: }
                   12280: 
1.661     raeburn  12281: sub upload_embedded {
                   12282:     my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota,
1.987     raeburn  12283:         $current_disk_usage,$hiddenstate,$actionurl) = @_;
                   12284:     my (%pathchange,$output,$modifyform,$footer,$returnflag);
1.661     raeburn  12285:     for (my $i=0; $i<$env{'form.number_embedded_items'}; $i++) {
                   12286:         next if (!exists($env{'form.embedded_item_'.$i.'.filename'}));
                   12287:         my $orig_uploaded_filename =
                   12288:             $env{'form.embedded_item_'.$i.'.filename'};
1.987     raeburn  12289:         foreach my $type ('orig','ref','attrib','codebase') {
                   12290:             if ($env{'form.embedded_'.$type.'_'.$i} ne '') {
                   12291:                 $env{'form.embedded_'.$type.'_'.$i} =
                   12292:                     &unescape($env{'form.embedded_'.$type.'_'.$i});
                   12293:             }
                   12294:         }
1.661     raeburn  12295:         my ($path,$fname) =
                   12296:             ($env{'form.embedded_orig_'.$i} =~ m{(.*/)([^/]*)});
                   12297:         # no path, whole string is fname
                   12298:         if (!$fname) { $fname = $env{'form.embedded_orig_'.$i} };
                   12299:         $fname = &Apache::lonnet::clean_filename($fname);
                   12300:         # See if there is anything left
                   12301:         next if ($fname eq '');
                   12302: 
                   12303:         # Check if file already exists as a file or directory.
                   12304:         my ($state,$msg);
                   12305:         if ($context eq 'portfolio') {
                   12306:             my $port_path = $dirpath;
                   12307:             if ($group ne '') {
                   12308:                 $port_path = "groups/$group/$port_path";
                   12309:             }
1.987     raeburn  12310:             ($state,$msg) = &check_for_upload($env{'form.currentpath'}.$path,
                   12311:                                               $fname,$group,'embedded_item_'.$i,
1.661     raeburn  12312:                                               $dir_root,$port_path,$disk_quota,
                   12313:                                               $current_disk_usage,$uname,$udom);
                   12314:             if ($state eq 'will_exceed_quota'
1.984     raeburn  12315:                 || $state eq 'file_locked') {
1.661     raeburn  12316:                 $output .= $msg;
                   12317:                 next;
                   12318:             }
                   12319:         } elsif (($context eq 'author') || ($context eq 'testbank')) {
                   12320:             ($state,$msg) = &check_for_existing($path,$fname,'embedded_item_'.$i);
                   12321:             if ($state eq 'exists') {
                   12322:                 $output .= $msg;
                   12323:                 next;
                   12324:             }
                   12325:         }
                   12326:         # Check if extension is valid
                   12327:         if (($fname =~ /\.(\w+)$/) &&
                   12328:             (&Apache::loncommon::fileembstyle($1) eq 'hdn')) {
1.1075.2.53  raeburn  12329:             $output .= &mt('Invalid file extension ([_1]) - reserved for internal use.',$1)
                   12330:                       .' '.&mt('Rename the file with a different extension and re-upload.').'<br />';
1.661     raeburn  12331:             next;
                   12332:         } elsif (($fname =~ /\.(\w+)$/) &&
                   12333:                  (!defined(&Apache::loncommon::fileembstyle($1)))) {
1.987     raeburn  12334:             $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1).'<br />';
1.661     raeburn  12335:             next;
                   12336:         } elsif ($fname=~/\.(\d+)\.(\w+)$/) {
1.1075.2.34  raeburn  12337:             $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  12338:             next;
                   12339:         }
                   12340:         $env{'form.embedded_item_'.$i.'.filename'}=$fname;
1.1075.2.35  raeburn  12341:         my $subdir = $path;
                   12342:         $subdir =~ s{/+$}{};
1.661     raeburn  12343:         if ($context eq 'portfolio') {
1.984     raeburn  12344:             my $result;
                   12345:             if ($state eq 'existingfile') {
                   12346:                 $result=
                   12347:                     &Apache::lonnet::userfileupload('embedded_item_'.$i,'existingfile',
1.1075.2.35  raeburn  12348:                                                     $dirpath.$env{'form.currentpath'}.$subdir);
1.661     raeburn  12349:             } else {
1.984     raeburn  12350:                 $result=
                   12351:                     &Apache::lonnet::userfileupload('embedded_item_'.$i,'',
1.987     raeburn  12352:                                                     $dirpath.
1.1075.2.35  raeburn  12353:                                                     $env{'form.currentpath'}.$subdir);
1.984     raeburn  12354:                 if ($result !~ m|^/uploaded/|) {
                   12355:                     $output .= '<span class="LC_error">'
                   12356:                                .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
                   12357:                                ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
                   12358:                                .'</span><br />';
                   12359:                     next;
                   12360:                 } else {
1.987     raeburn  12361:                     $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
                   12362:                                $path.$fname.'</span>').'<br />';     
1.984     raeburn  12363:                 }
1.661     raeburn  12364:             }
1.1075.2.35  raeburn  12365:         } elsif (($context eq 'coursedoc') || ($context eq 'syllabus')) {
                   12366:             my $extendedsubdir = $dirpath.'/'.$subdir;
                   12367:             $extendedsubdir =~ s{/+$}{};
1.987     raeburn  12368:             my $result =
1.1075.2.35  raeburn  12369:                 &Apache::lonnet::userfileupload('embedded_item_'.$i,$context,$extendedsubdir);
1.987     raeburn  12370:             if ($result !~ m|^/uploaded/|) {
                   12371:                 $output .= '<span class="LC_error">'
                   12372:                            .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
                   12373:                            ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
                   12374:                            .'</span><br />';
                   12375:                     next;
                   12376:             } else {
                   12377:                 $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
                   12378:                            $path.$fname.'</span>').'<br />';
1.1075.2.35  raeburn  12379:                 if ($context eq 'syllabus') {
                   12380:                     &Apache::lonnet::make_public_indefinitely($result);
                   12381:                 }
1.987     raeburn  12382:             }
1.661     raeburn  12383:         } else {
                   12384: # Save the file
                   12385:             my $target = $env{'form.embedded_item_'.$i};
                   12386:             my $fullpath = $dir_root.$dirpath.'/'.$path;
                   12387:             my $dest = $fullpath.$fname;
                   12388:             my $url = $url_root.$dirpath.'/'.$path.$fname;
1.1027    raeburn  12389:             my @parts=split(/\//,"$dirpath/$path");
1.661     raeburn  12390:             my $count;
                   12391:             my $filepath = $dir_root;
1.1027    raeburn  12392:             foreach my $subdir (@parts) {
                   12393:                 $filepath .= "/$subdir";
                   12394:                 if (!-e $filepath) {
1.661     raeburn  12395:                     mkdir($filepath,0770);
                   12396:                 }
                   12397:             }
                   12398:             my $fh;
                   12399:             if (!open($fh,'>'.$dest)) {
                   12400:                 &Apache::lonnet::logthis('Failed to create '.$dest);
                   12401:                 $output .= '<span class="LC_error">'.
1.1071    raeburn  12402:                            &mt('An error occurred while trying to upload [_1] for embedded element [_2].',
                   12403:                                $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661     raeburn  12404:                            '</span><br />';
                   12405:             } else {
                   12406:                 if (!print $fh $env{'form.embedded_item_'.$i}) {
                   12407:                     &Apache::lonnet::logthis('Failed to write to '.$dest);
                   12408:                     $output .= '<span class="LC_error">'.
1.1071    raeburn  12409:                               &mt('An error occurred while writing the file [_1] for embedded element [_2].',
                   12410:                                   $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661     raeburn  12411:                               '</span><br />';
                   12412:                 } else {
1.987     raeburn  12413:                     $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
                   12414:                                $url.'</span>').'<br />';
                   12415:                     unless ($context eq 'testbank') {
                   12416:                         $footer .= &mt('View embedded file: [_1]',
                   12417:                                        '<a href="'.$url.'">'.$fname.'</a>').'<br />';
                   12418:                     }
                   12419:                 }
                   12420:                 close($fh);
                   12421:             }
                   12422:         }
                   12423:         if ($env{'form.embedded_ref_'.$i}) {
                   12424:             $pathchange{$i} = 1;
                   12425:         }
                   12426:     }
                   12427:     if ($output) {
                   12428:         $output = '<p>'.$output.'</p>';
                   12429:     }
                   12430:     $output .= &modify_html_form('upload_embedded',$actionurl,$hiddenstate,\%pathchange);
                   12431:     $returnflag = 'ok';
1.1071    raeburn  12432:     my $numpathchgs = scalar(keys(%pathchange));
                   12433:     if ($numpathchgs > 0) {
1.987     raeburn  12434:         if ($context eq 'portfolio') {
                   12435:             $output .= '<p>'.&mt('or').'</p>';
                   12436:         } elsif ($context eq 'testbank') {
1.1071    raeburn  12437:             $output .=  '<p>'.&mt('Or [_1]continue[_2] the testbank import without modifying the reference(s).',
                   12438:                                   '<a href="javascript:document.testbankForm.submit();">','</a>').'</p>';
1.987     raeburn  12439:             $returnflag = 'modify_orightml';
                   12440:         }
                   12441:     }
1.1071    raeburn  12442:     return ($output.$footer,$returnflag,$numpathchgs);
1.987     raeburn  12443: }
                   12444: 
                   12445: sub modify_html_form {
                   12446:     my ($context,$actionurl,$hiddenstate,$pathchange,$pathchgtable) = @_;
                   12447:     my $end = 0;
                   12448:     my $modifyform;
                   12449:     if ($context eq 'upload_embedded') {
                   12450:         return unless (ref($pathchange) eq 'HASH');
                   12451:         if ($env{'form.number_embedded_items'}) {
                   12452:             $end += $env{'form.number_embedded_items'};
                   12453:         }
                   12454:         if ($env{'form.number_pathchange_items'}) {
                   12455:             $end += $env{'form.number_pathchange_items'};
                   12456:         }
                   12457:         if ($end) {
                   12458:             for (my $i=0; $i<$end; $i++) {
                   12459:                 if ($i < $env{'form.number_embedded_items'}) {
                   12460:                     next unless($pathchange->{$i});
                   12461:                 }
                   12462:                 $modifyform .=
                   12463:                     &start_data_table_row().
                   12464:                     '<td><input type ="checkbox" name="namechange" value="'.$i.'" '.
                   12465:                     'checked="checked" /></td>'.
                   12466:                     '<td>'.$env{'form.embedded_ref_'.$i}.
                   12467:                     '<input type="hidden" name="embedded_ref_'.$i.'" value="'.
                   12468:                     &escape($env{'form.embedded_ref_'.$i}).'" />'.
                   12469:                     '<input type="hidden" name="embedded_codebase_'.$i.'" value="'.
                   12470:                     &escape($env{'form.embedded_codebase_'.$i}).'" />'.
                   12471:                     '<input type="hidden" name="embedded_attrib_'.$i.'" value="'.
                   12472:                     &escape($env{'form.embedded_attrib_'.$i}).'" /></td>'.
                   12473:                     '<td>'.$env{'form.embedded_orig_'.$i}.
                   12474:                     '<input type="hidden" name="embedded_orig_'.$i.'" value="'.
                   12475:                     &escape($env{'form.embedded_orig_'.$i}).'" /></td>'.
                   12476:                     &end_data_table_row();
1.1071    raeburn  12477:             }
1.987     raeburn  12478:         }
                   12479:     } else {
                   12480:         $modifyform = $pathchgtable;
                   12481:         if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
                   12482:             $hiddenstate .= '<input type="hidden" name="phase" value="four" />';
                   12483:         } elsif (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
                   12484:             $hiddenstate .= '<input type="hidden" name="action" value="modify_orightml" />';
                   12485:         }
                   12486:     }
                   12487:     if ($modifyform) {
1.1071    raeburn  12488:         if ($actionurl eq '/adm/dependencies') {
                   12489:             $hiddenstate .= '<input type="hidden" name="action" value="modifyhrefs" />';
                   12490:         }
1.987     raeburn  12491:         return '<h3>'.&mt('Changes in content of HTML file required').'</h3>'."\n".
                   12492:                '<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".
                   12493:                '<li>'.&mt('For consistency between the reference(s) and the location of the corresponding stored file within LON-CAPA.').'</li>'."\n".
                   12494:                '<li>'.&mt('To change absolute paths to relative paths, or replace directory traversal via "../" within the original reference.').'</li>'."\n".
                   12495:                '</ol></p>'."\n".'<p>'.
                   12496:                &mt('LON-CAPA can make the required changes to your HTML file.').'</p>'."\n".
                   12497:                '<form method="post" name="refchanger" action="'.$actionurl.'">'.
                   12498:                &start_data_table()."\n".
                   12499:                &start_data_table_header_row().
                   12500:                '<th>'.&mt('Change?').'</th>'.
                   12501:                '<th>'.&mt('Current reference').'</th>'.
                   12502:                '<th>'.&mt('Required reference').'</th>'.
                   12503:                &end_data_table_header_row()."\n".
                   12504:                $modifyform.
                   12505:                &end_data_table().'<br />'."\n".$hiddenstate.
                   12506:                '<input type="submit" name="pathchanges" value="'.&mt('Modify HTML file').'" />'.
                   12507:                '</form>'."\n";
                   12508:     }
                   12509:     return;
                   12510: }
                   12511: 
                   12512: sub modify_html_refs {
1.1075.2.35  raeburn  12513:     my ($context,$dirpath,$uname,$udom,$dir_root,$url) = @_;
1.987     raeburn  12514:     my $container;
                   12515:     if ($context eq 'portfolio') {
                   12516:         $container = $env{'form.container'};
                   12517:     } elsif ($context eq 'coursedoc') {
                   12518:         $container = $env{'form.primaryurl'};
1.1071    raeburn  12519:     } elsif ($context eq 'manage_dependencies') {
                   12520:         (undef,undef,$container) = &Apache::lonnet::decode_symb($env{'form.symb'});
                   12521:         $container = "/$container";
1.1075.2.35  raeburn  12522:     } elsif ($context eq 'syllabus') {
                   12523:         $container = $url;
1.987     raeburn  12524:     } else {
1.1027    raeburn  12525:         $container = $Apache::lonnet::perlvar{'lonDocRoot'}.$env{'form.filename'};
1.987     raeburn  12526:     }
                   12527:     my (%allfiles,%codebase,$output,$content);
                   12528:     my @changes = &get_env_multiple('form.namechange');
1.1075.2.35  raeburn  12529:     unless ((@changes > 0)  || ($context eq 'syllabus')) {
1.1071    raeburn  12530:         if (wantarray) {
                   12531:             return ('',0,0); 
                   12532:         } else {
                   12533:             return;
                   12534:         }
                   12535:     }
                   12536:     if (($context eq 'portfolio') || ($context eq 'coursedoc') || 
1.1075.2.35  raeburn  12537:         ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
1.1071    raeburn  12538:         unless ($container =~ m{^/uploaded/\Q$udom\E/\Q$uname\E/}) {
                   12539:             if (wantarray) {
                   12540:                 return ('',0,0);
                   12541:             } else {
                   12542:                 return;
                   12543:             }
                   12544:         } 
1.987     raeburn  12545:         $content = &Apache::lonnet::getfile($container);
1.1071    raeburn  12546:         if ($content eq '-1') {
                   12547:             if (wantarray) {
                   12548:                 return ('',0,0);
                   12549:             } else {
                   12550:                 return;
                   12551:             }
                   12552:         }
1.987     raeburn  12553:     } else {
1.1071    raeburn  12554:         unless ($container =~ /^\Q$dir_root\E/) {
                   12555:             if (wantarray) {
                   12556:                 return ('',0,0);
                   12557:             } else {
                   12558:                 return;
                   12559:             }
                   12560:         } 
1.1075.2.128  raeburn  12561:         if (open(my $fh,'<',$container)) {
1.987     raeburn  12562:             $content = join('', <$fh>);
                   12563:             close($fh);
                   12564:         } else {
1.1071    raeburn  12565:             if (wantarray) {
                   12566:                 return ('',0,0);
                   12567:             } else {
                   12568:                 return;
                   12569:             }
1.987     raeburn  12570:         }
                   12571:     }
                   12572:     my ($count,$codebasecount) = (0,0);
                   12573:     my $mm = new File::MMagic;
                   12574:     my $mime_type = $mm->checktype_contents($content);
                   12575:     if ($mime_type eq 'text/html') {
                   12576:         my $parse_result = 
                   12577:             &Apache::lonnet::extract_embedded_items($container,\%allfiles,
                   12578:                                                     \%codebase,\$content);
                   12579:         if ($parse_result eq 'ok') {
                   12580:             foreach my $i (@changes) {
                   12581:                 my $orig = &unescape($env{'form.embedded_orig_'.$i});
                   12582:                 my $ref = &unescape($env{'form.embedded_ref_'.$i});
                   12583:                 if ($allfiles{$ref}) {
                   12584:                     my $newname =  $orig;
                   12585:                     my ($attrib_regexp,$codebase);
1.1006    raeburn  12586:                     $attrib_regexp = &unescape($env{'form.embedded_attrib_'.$i});
1.987     raeburn  12587:                     if ($attrib_regexp =~ /:/) {
                   12588:                         $attrib_regexp =~ s/\:/|/g;
                   12589:                     }
                   12590:                     if ($content =~ m{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
                   12591:                         my $numchg = ($content =~ s{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
                   12592:                         $count += $numchg;
1.1075.2.35  raeburn  12593:                         $allfiles{$newname} = $allfiles{$ref};
1.1075.2.48  raeburn  12594:                         delete($allfiles{$ref});
1.987     raeburn  12595:                     }
                   12596:                     if ($env{'form.embedded_codebase_'.$i} ne '') {
1.1006    raeburn  12597:                         $codebase = &unescape($env{'form.embedded_codebase_'.$i});
1.987     raeburn  12598:                         my $numchg = ($content =~ s/(codebase\s*=\s*["']?)\Q$codebase\E(["']?)/$1.$2/i); #' stupid emacs
                   12599:                         $codebasecount ++;
                   12600:                     }
                   12601:                 }
                   12602:             }
1.1075.2.35  raeburn  12603:             my $skiprewrites;
1.987     raeburn  12604:             if ($count || $codebasecount) {
                   12605:                 my $saveresult;
1.1071    raeburn  12606:                 if (($context eq 'portfolio') || ($context eq 'coursedoc') || 
1.1075.2.35  raeburn  12607:                     ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
1.987     raeburn  12608:                     my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
                   12609:                     if ($url eq $container) {
                   12610:                         my ($fname) = ($container =~ m{/([^/]+)$});
                   12611:                         $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
                   12612:                                             $count,'<span class="LC_filename">'.
1.1071    raeburn  12613:                                             $fname.'</span>').'</p>';
1.987     raeburn  12614:                     } else {
                   12615:                          $output = '<p class="LC_error">'.
                   12616:                                    &mt('Error: update failed for: [_1].',
                   12617:                                    '<span class="LC_filename">'.
                   12618:                                    $container.'</span>').'</p>';
                   12619:                     }
1.1075.2.35  raeburn  12620:                     if ($context eq 'syllabus') {
                   12621:                         unless ($saveresult eq 'ok') {
                   12622:                             $skiprewrites = 1;
                   12623:                         }
                   12624:                     }
1.987     raeburn  12625:                 } else {
1.1075.2.128  raeburn  12626:                     if (open(my $fh,'>',$container)) {
1.987     raeburn  12627:                         print $fh $content;
                   12628:                         close($fh);
                   12629:                         $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
                   12630:                                   $count,'<span class="LC_filename">'.
                   12631:                                   $container.'</span>').'</p>';
1.661     raeburn  12632:                     } else {
1.987     raeburn  12633:                          $output = '<p class="LC_error">'.
                   12634:                                    &mt('Error: could not update [_1].',
                   12635:                                    '<span class="LC_filename">'.
                   12636:                                    $container.'</span>').'</p>';
1.661     raeburn  12637:                     }
                   12638:                 }
                   12639:             }
1.1075.2.35  raeburn  12640:             if (($context eq 'syllabus') && (!$skiprewrites)) {
                   12641:                 my ($actionurl,$state);
                   12642:                 $actionurl = "/public/$udom/$uname/syllabus";
                   12643:                 my ($ignore,$num,$numpathchanges,$existing,$mapping) =
                   12644:                     &ask_for_embedded_content($actionurl,$state,\%allfiles,
                   12645:                                               \%codebase,
                   12646:                                               {'context' => 'rewrites',
                   12647:                                                'ignore_remote_references' => 1,});
                   12648:                 if (ref($mapping) eq 'HASH') {
                   12649:                     my $rewrites = 0;
                   12650:                     foreach my $key (keys(%{$mapping})) {
                   12651:                         next if ($key =~ m{^https?://});
                   12652:                         my $ref = $mapping->{$key};
                   12653:                         my $newname = "/uploaded/$udom/$uname/portfolio/syllabus/$key";
                   12654:                         my $attrib;
                   12655:                         if (ref($allfiles{$mapping->{$key}}) eq 'ARRAY') {
                   12656:                             $attrib = join('|',@{$allfiles{$mapping->{$key}}});
                   12657:                         }
                   12658:                         if ($content =~ m{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
                   12659:                             my $numchg = ($content =~ s{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
                   12660:                             $rewrites += $numchg;
                   12661:                         }
                   12662:                     }
                   12663:                     if ($rewrites) {
                   12664:                         my $saveresult;
                   12665:                         my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
                   12666:                         if ($url eq $container) {
                   12667:                             my ($fname) = ($container =~ m{/([^/]+)$});
                   12668:                             $output .= '<p>'.&mt('Rewrote [quant,_1,link] as [quant,_1,absolute link] in [_2].',
                   12669:                                             $count,'<span class="LC_filename">'.
                   12670:                                             $fname.'</span>').'</p>';
                   12671:                         } else {
                   12672:                             $output .= '<p class="LC_error">'.
                   12673:                                        &mt('Error: could not update links in [_1].',
                   12674:                                        '<span class="LC_filename">'.
                   12675:                                        $container.'</span>').'</p>';
                   12676: 
                   12677:                         }
                   12678:                     }
                   12679:                 }
                   12680:             }
1.987     raeburn  12681:         } else {
                   12682:             &logthis('Failed to parse '.$container.
                   12683:                      ' to modify references: '.$parse_result);
1.661     raeburn  12684:         }
                   12685:     }
1.1071    raeburn  12686:     if (wantarray) {
                   12687:         return ($output,$count,$codebasecount);
                   12688:     } else {
                   12689:         return $output;
                   12690:     }
1.661     raeburn  12691: }
                   12692: 
                   12693: sub check_for_existing {
                   12694:     my ($path,$fname,$element) = @_;
                   12695:     my ($state,$msg);
                   12696:     if (-d $path.'/'.$fname) {
                   12697:         $state = 'exists';
                   12698:         $msg = &mt('Unable to upload [_1]. A directory by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
                   12699:     } elsif (-e $path.'/'.$fname) {
                   12700:         $state = 'exists';
                   12701:         $msg = &mt('Unable to upload [_1]. A file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
                   12702:     }
                   12703:     if ($state eq 'exists') {
                   12704:         $msg = '<span class="LC_error">'.$msg.'</span><br />';
                   12705:     }
                   12706:     return ($state,$msg);
                   12707: }
                   12708: 
                   12709: sub check_for_upload {
                   12710:     my ($path,$fname,$group,$element,$portfolio_root,$port_path,
                   12711:         $disk_quota,$current_disk_usage,$uname,$udom) = @_;
1.985     raeburn  12712:     my $filesize = length($env{'form.'.$element});
                   12713:     if (!$filesize) {
                   12714:         my $msg = '<span class="LC_error">'.
                   12715:                   &mt('Unable to upload [_1]. (size = [_2] bytes)', 
                   12716:                       '<span class="LC_filename">'.$fname.'</span>',
                   12717:                       $filesize).'<br />'.
1.1007    raeburn  12718:                   &mt('Either the file you attempted to upload was empty, or your web browser was unable to read its contents.').'<br />'.
1.985     raeburn  12719:                   '</span>';
                   12720:         return ('zero_bytes',$msg);
                   12721:     }
                   12722:     $filesize =  $filesize/1000; #express in k (1024?)
1.661     raeburn  12723:     my $getpropath = 1;
1.1021    raeburn  12724:     my ($dirlistref,$listerror) =
                   12725:          &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname,$getpropath);
1.661     raeburn  12726:     my $found_file = 0;
                   12727:     my $locked_file = 0;
1.991     raeburn  12728:     my @lockers;
                   12729:     my $navmap;
                   12730:     if ($env{'request.course.id'}) {
                   12731:         $navmap = Apache::lonnavmaps::navmap->new();
                   12732:     }
1.1021    raeburn  12733:     if (ref($dirlistref) eq 'ARRAY') {
                   12734:         foreach my $line (@{$dirlistref}) {
                   12735:             my ($file_name,$rest)=split(/\&/,$line,2);
                   12736:             if ($file_name eq $fname){
                   12737:                 $file_name = $path.$file_name;
                   12738:                 if ($group ne '') {
                   12739:                     $file_name = $group.$file_name;
                   12740:                 }
                   12741:                 $found_file = 1;
                   12742:                 if (&Apache::lonnet::is_locked($file_name,$udom,$uname,\@lockers) eq 'true') {
                   12743:                     foreach my $lock (@lockers) {
                   12744:                         if (ref($lock) eq 'ARRAY') {
                   12745:                             my ($symb,$crsid) = @{$lock};
                   12746:                             if ($crsid eq $env{'request.course.id'}) {
                   12747:                                 if (ref($navmap)) {
                   12748:                                     my $res = $navmap->getBySymb($symb);
                   12749:                                     foreach my $part (@{$res->parts()}) { 
                   12750:                                         my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part);
                   12751:                                         unless (($slot_status == $res->RESERVED) ||
                   12752:                                                 ($slot_status == $res->RESERVED_LOCATION)) {
                   12753:                                             $locked_file = 1;
                   12754:                                         }
1.991     raeburn  12755:                                     }
1.1021    raeburn  12756:                                 } else {
                   12757:                                     $locked_file = 1;
1.991     raeburn  12758:                                 }
                   12759:                             } else {
                   12760:                                 $locked_file = 1;
                   12761:                             }
                   12762:                         }
1.1021    raeburn  12763:                    }
                   12764:                 } else {
                   12765:                     my @info = split(/\&/,$rest);
                   12766:                     my $currsize = $info[6]/1000;
                   12767:                     if ($currsize < $filesize) {
                   12768:                         my $extra = $filesize - $currsize;
                   12769:                         if (($current_disk_usage + $extra) > $disk_quota) {
1.1075.2.69  raeburn  12770:                             my $msg = '<p class="LC_warning">'.
1.1021    raeburn  12771:                                       &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.1075.2.69  raeburn  12772:                                           '<span class="LC_filename">'.$fname.'</span>',$filesize,$currsize).'</p>'.
                   12773:                                       '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
                   12774:                                                    $disk_quota,$current_disk_usage).'</p>';
1.1021    raeburn  12775:                             return ('will_exceed_quota',$msg);
                   12776:                         }
1.984     raeburn  12777:                     }
                   12778:                 }
1.661     raeburn  12779:             }
                   12780:         }
                   12781:     }
                   12782:     if (($current_disk_usage + $filesize) > $disk_quota){
1.1075.2.69  raeburn  12783:         my $msg = '<p class="LC_warning">'.
                   12784:                 &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.','<span class="LC_filename">'.$fname.'</span>',$filesize).'</p>'.
                   12785:                   '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',$disk_quota,$current_disk_usage).'</p>';
1.661     raeburn  12786:         return ('will_exceed_quota',$msg);
                   12787:     } elsif ($found_file) {
                   12788:         if ($locked_file) {
1.1075.2.69  raeburn  12789:             my $msg = '<p class="LC_warning">';
1.661     raeburn  12790:             $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.1075.2.69  raeburn  12791:             $msg .= '</p>';
1.661     raeburn  12792:             $msg .= &mt('You will be able to rename or delete existing [_1] after a grade has been assigned.','<span class="LC_filename">'.$fname.'</span>');
                   12793:             return ('file_locked',$msg);
                   12794:         } else {
1.1075.2.69  raeburn  12795:             my $msg = '<p class="LC_error">';
1.984     raeburn  12796:             $msg .= &mt(' A file by that name: [_1] was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$port_path.$env{'form.currentpath'});
1.1075.2.69  raeburn  12797:             $msg .= '</p>';
1.984     raeburn  12798:             return ('existingfile',$msg);
1.661     raeburn  12799:         }
                   12800:     }
                   12801: }
                   12802: 
1.987     raeburn  12803: sub check_for_traversal {
                   12804:     my ($path,$url,$toplevel) = @_;
                   12805:     my @parts=split(/\//,$path);
                   12806:     my $cleanpath;
                   12807:     my $fullpath = $url;
                   12808:     for (my $i=0;$i<@parts;$i++) {
                   12809:         next if ($parts[$i] eq '.');
                   12810:         if ($parts[$i] eq '..') {
                   12811:             $fullpath =~ s{([^/]+/)$}{};
                   12812:         } else {
                   12813:             $fullpath .= $parts[$i].'/';
                   12814:         }
                   12815:     }
                   12816:     if ($fullpath =~ /^\Q$url\E(.*)$/) {
                   12817:         $cleanpath = $1;
                   12818:     } elsif ($fullpath =~ /^\Q$toplevel\E(.*)$/) {
                   12819:         my $curr_toprel = $1;
                   12820:         my @parts = split(/\//,$curr_toprel);
                   12821:         my ($url_toprel) = ($url =~ /^\Q$toplevel\E(.*)$/);
                   12822:         my @urlparts = split(/\//,$url_toprel);
                   12823:         my $doubledots;
                   12824:         my $startdiff = -1;
                   12825:         for (my $i=0; $i<@urlparts; $i++) {
                   12826:             if ($startdiff == -1) {
                   12827:                 unless ($urlparts[$i] eq $parts[$i]) {
                   12828:                     $startdiff = $i;
                   12829:                     $doubledots .= '../';
                   12830:                 }
                   12831:             } else {
                   12832:                 $doubledots .= '../';
                   12833:             }
                   12834:         }
                   12835:         if ($startdiff > -1) {
                   12836:             $cleanpath = $doubledots;
                   12837:             for (my $i=$startdiff; $i<@parts; $i++) {
                   12838:                 $cleanpath .= $parts[$i].'/';
                   12839:             }
                   12840:         }
                   12841:     }
                   12842:     $cleanpath =~ s{(/)$}{};
                   12843:     return $cleanpath;
                   12844: }
1.31      albertel 12845: 
1.1053    raeburn  12846: sub is_archive_file {
                   12847:     my ($mimetype) = @_;
                   12848:     if (($mimetype eq 'application/octet-stream') ||
                   12849:         ($mimetype eq 'application/x-stuffit') ||
                   12850:         ($mimetype =~ m{^application/(x\-)?(compressed|tar|zip|tgz|gz|gtar|gzip|gunzip|bz|bz2|bzip2)})) {
                   12851:         return 1;
                   12852:     }
                   12853:     return;
                   12854: }
                   12855: 
                   12856: sub decompress_form {
1.1065    raeburn  12857:     my ($mimetype,$archiveurl,$action,$noextract,$hiddenelements,$dirlist) = @_;
1.1053    raeburn  12858:     my %lt = &Apache::lonlocal::texthash (
                   12859:         this => 'This file is an archive file.',
1.1067    raeburn  12860:         camt => 'This file is a Camtasia archive file.',
1.1065    raeburn  12861:         itsc => 'Its contents are as follows:',
1.1053    raeburn  12862:         youm => 'You may wish to extract its contents.',
                   12863:         extr => 'Extract contents',
1.1067    raeburn  12864:         auto => 'LON-CAPA can process the files automatically, or you can decide how each should be handled.',
                   12865:         proa => 'Process automatically?',
1.1053    raeburn  12866:         yes  => 'Yes',
                   12867:         no   => 'No',
1.1067    raeburn  12868:         fold => 'Title for folder containing movie',
                   12869:         movi => 'Title for page containing embedded movie', 
1.1053    raeburn  12870:     );
1.1065    raeburn  12871:     my $fileloc = &Apache::lonnet::filelocation(undef,$archiveurl);
1.1067    raeburn  12872:     my ($is_camtasia,$topdir,%toplevel,@paths);
1.1065    raeburn  12873:     my $info = &list_archive_contents($fileloc,\@paths);
                   12874:     if (@paths) {
                   12875:         foreach my $path (@paths) {
                   12876:             $path =~ s{^/}{};
1.1067    raeburn  12877:             if ($path =~ m{^([^/]+)/$}) {
                   12878:                 $topdir = $1;
                   12879:             }
1.1065    raeburn  12880:             if ($path =~ m{^([^/]+)/}) {
                   12881:                 $toplevel{$1} = $path;
                   12882:             } else {
                   12883:                 $toplevel{$path} = $path;
                   12884:             }
                   12885:         }
                   12886:     }
1.1067    raeburn  12887:     if ($mimetype =~ m{^application/(x\-)?(compressed|zip)}) {
1.1075.2.59  raeburn  12888:         my @camtasia6 = ("$topdir/","$topdir/index.html",
1.1067    raeburn  12889:                         "$topdir/media/",
                   12890:                         "$topdir/media/$topdir.mp4",
                   12891:                         "$topdir/media/FirstFrame.png",
                   12892:                         "$topdir/media/player.swf",
                   12893:                         "$topdir/media/swfobject.js",
                   12894:                         "$topdir/media/expressInstall.swf");
1.1075.2.81  raeburn  12895:         my @camtasia8_1 = ("$topdir/","$topdir/$topdir.html",
1.1075.2.59  raeburn  12896:                          "$topdir/$topdir.mp4",
                   12897:                          "$topdir/$topdir\_config.xml",
                   12898:                          "$topdir/$topdir\_controller.swf",
                   12899:                          "$topdir/$topdir\_embed.css",
                   12900:                          "$topdir/$topdir\_First_Frame.png",
                   12901:                          "$topdir/$topdir\_player.html",
                   12902:                          "$topdir/$topdir\_Thumbnails.png",
                   12903:                          "$topdir/playerProductInstall.swf",
                   12904:                          "$topdir/scripts/",
                   12905:                          "$topdir/scripts/config_xml.js",
                   12906:                          "$topdir/scripts/handlebars.js",
                   12907:                          "$topdir/scripts/jquery-1.7.1.min.js",
                   12908:                          "$topdir/scripts/jquery-ui-1.8.15.custom.min.js",
                   12909:                          "$topdir/scripts/modernizr.js",
                   12910:                          "$topdir/scripts/player-min.js",
                   12911:                          "$topdir/scripts/swfobject.js",
                   12912:                          "$topdir/skins/",
                   12913:                          "$topdir/skins/configuration_express.xml",
                   12914:                          "$topdir/skins/express_show/",
                   12915:                          "$topdir/skins/express_show/player-min.css",
                   12916:                          "$topdir/skins/express_show/spritesheet.png");
1.1075.2.81  raeburn  12917:         my @camtasia8_4 = ("$topdir/","$topdir/$topdir.html",
                   12918:                          "$topdir/$topdir.mp4",
                   12919:                          "$topdir/$topdir\_config.xml",
                   12920:                          "$topdir/$topdir\_controller.swf",
                   12921:                          "$topdir/$topdir\_embed.css",
                   12922:                          "$topdir/$topdir\_First_Frame.png",
                   12923:                          "$topdir/$topdir\_player.html",
                   12924:                          "$topdir/$topdir\_Thumbnails.png",
                   12925:                          "$topdir/playerProductInstall.swf",
                   12926:                          "$topdir/scripts/",
                   12927:                          "$topdir/scripts/config_xml.js",
                   12928:                          "$topdir/scripts/techsmith-smart-player.min.js",
                   12929:                          "$topdir/skins/",
                   12930:                          "$topdir/skins/configuration_express.xml",
                   12931:                          "$topdir/skins/express_show/",
                   12932:                          "$topdir/skins/express_show/spritesheet.min.css",
                   12933:                          "$topdir/skins/express_show/spritesheet.png",
                   12934:                          "$topdir/skins/express_show/techsmith-smart-player.min.css");
1.1075.2.59  raeburn  12935:         my @diffs = &compare_arrays(\@paths,\@camtasia6);
1.1067    raeburn  12936:         if (@diffs == 0) {
1.1075.2.59  raeburn  12937:             $is_camtasia = 6;
                   12938:         } else {
1.1075.2.81  raeburn  12939:             @diffs = &compare_arrays(\@paths,\@camtasia8_1);
1.1075.2.59  raeburn  12940:             if (@diffs == 0) {
                   12941:                 $is_camtasia = 8;
1.1075.2.81  raeburn  12942:             } else {
                   12943:                 @diffs = &compare_arrays(\@paths,\@camtasia8_4);
                   12944:                 if (@diffs == 0) {
                   12945:                     $is_camtasia = 8;
                   12946:                 }
1.1075.2.59  raeburn  12947:             }
1.1067    raeburn  12948:         }
                   12949:     }
                   12950:     my $output;
                   12951:     if ($is_camtasia) {
                   12952:         $output = <<"ENDCAM";
                   12953: <script type="text/javascript" language="Javascript">
                   12954: // <![CDATA[
                   12955: 
                   12956: function camtasiaToggle() {
                   12957:     for (var i=0; i<document.uploaded_decompress.autoextract_camtasia.length; i++) {
                   12958:         if (document.uploaded_decompress.autoextract_camtasia[i].checked) {
1.1075.2.59  raeburn  12959:             if (document.uploaded_decompress.autoextract_camtasia[i].value == $is_camtasia) {
1.1067    raeburn  12960:                 document.getElementById('camtasia_titles').style.display='block';
                   12961:             } else {
                   12962:                 document.getElementById('camtasia_titles').style.display='none';
                   12963:             }
                   12964:         }
                   12965:     }
                   12966:     return;
                   12967: }
                   12968: 
                   12969: // ]]>
                   12970: </script>
                   12971: <p>$lt{'camt'}</p>
                   12972: ENDCAM
1.1065    raeburn  12973:     } else {
1.1067    raeburn  12974:         $output = '<p>'.$lt{'this'};
                   12975:         if ($info eq '') {
                   12976:             $output .= ' '.$lt{'youm'}.'</p>'."\n";
                   12977:         } else {
                   12978:             $output .= ' '.$lt{'itsc'}.'</p>'."\n".
                   12979:                        '<div><pre>'.$info.'</pre></div>';
                   12980:         }
1.1065    raeburn  12981:     }
1.1067    raeburn  12982:     $output .= '<form name="uploaded_decompress" action="'.$action.'" method="post">'."\n";
1.1065    raeburn  12983:     my $duplicates;
                   12984:     my $num = 0;
                   12985:     if (ref($dirlist) eq 'ARRAY') {
                   12986:         foreach my $item (@{$dirlist}) {
                   12987:             if (ref($item) eq 'ARRAY') {
                   12988:                 if (exists($toplevel{$item->[0]})) {
                   12989:                     $duplicates .= 
                   12990:                         &start_data_table_row().
                   12991:                         '<td><label><input type="radio" name="archive_overwrite_'.$num.'" '.
                   12992:                         'value="0" checked="checked" />'.&mt('No').'</label>'.
                   12993:                         '&nbsp;<label><input type="radio" name="archive_overwrite_'.$num.'" '.
                   12994:                         'value="1" />'.&mt('Yes').'</label>'.
                   12995:                         '<input type="hidden" name="archive_overwrite_name_'.$num.'" value="'.$item->[0].'" /></td>'."\n".
                   12996:                         '<td>'.$item->[0].'</td>';
                   12997:                     if ($item->[2]) {
                   12998:                         $duplicates .= '<td>'.&mt('Directory').'</td>';
                   12999:                     } else {
                   13000:                         $duplicates .= '<td>'.&mt('File').'</td>';
                   13001:                     }
                   13002:                     $duplicates .= '<td>'.$item->[3].'</td>'.
                   13003:                                    '<td>'.
                   13004:                                    &Apache::lonlocal::locallocaltime($item->[4]).
                   13005:                                    '</td>'.
                   13006:                                    &end_data_table_row();
                   13007:                     $num ++;
                   13008:                 }
                   13009:             }
                   13010:         }
                   13011:     }
                   13012:     my $itemcount;
                   13013:     if (@paths > 0) {
                   13014:         $itemcount = scalar(@paths);
                   13015:     } else {
                   13016:         $itemcount = 1;
                   13017:     }
1.1067    raeburn  13018:     if ($is_camtasia) {
                   13019:         $output .= $lt{'auto'}.'<br />'.
                   13020:                    '<span class="LC_nobreak">'.$lt{'proa'}.'<label>'.
1.1075.2.59  raeburn  13021:                    '<input type="radio" name="autoextract_camtasia" value="'.$is_camtasia.'" onclick="javascript:camtasiaToggle();" checked="checked" />'.
1.1067    raeburn  13022:                    $lt{'yes'}.'</label>&nbsp;<label>'.
                   13023:                    '<input type="radio" name="autoextract_camtasia" value="0" onclick="javascript:camtasiaToggle();" />'.
                   13024:                    $lt{'no'}.'</label></span><br />'.
                   13025:                    '<div id="camtasia_titles" style="display:block">'.
                   13026:                    &Apache::lonhtmlcommon::start_pick_box().
                   13027:                    &Apache::lonhtmlcommon::row_title($lt{'fold'}).
                   13028:                    '<input type="textbox" name="camtasia_foldername" value="'.$env{'form.comment'}.'" />'."\n".
                   13029:                    &Apache::lonhtmlcommon::row_closure().
                   13030:                    &Apache::lonhtmlcommon::row_title($lt{'movi'}).
                   13031:                    '<input type="textbox" name="camtasia_moviename" value="" />'."\n".
                   13032:                    &Apache::lonhtmlcommon::row_closure(1).
                   13033:                    &Apache::lonhtmlcommon::end_pick_box().
                   13034:                    '</div>';
                   13035:     }
1.1065    raeburn  13036:     $output .= 
                   13037:         '<input type="hidden" name="archive_overwrite_total" value="'.$num.'" />'.
1.1067    raeburn  13038:         '<input type="hidden" name="archive_itemcount" value="'.$itemcount.'" />'.
                   13039:         "\n";
1.1065    raeburn  13040:     if ($duplicates ne '') {
                   13041:         $output .= '<p><span class="LC_warning">'.
                   13042:                    &mt('Warning: decompression of the archive will overwrite the following items which already exist:').'</span><br />'.  
                   13043:                    &start_data_table().
                   13044:                    &start_data_table_header_row().
                   13045:                    '<th>'.&mt('Overwrite?').'</th>'.
                   13046:                    '<th>'.&mt('Name').'</th>'.
                   13047:                    '<th>'.&mt('Type').'</th>'.
                   13048:                    '<th>'.&mt('Size').'</th>'.
                   13049:                    '<th>'.&mt('Last modified').'</th>'.
                   13050:                    &end_data_table_header_row().
                   13051:                    $duplicates.
                   13052:                    &end_data_table().
                   13053:                    '</p>';
                   13054:     }
1.1067    raeburn  13055:     $output .= '<input type="hidden" name="archiveurl" value="'.$archiveurl.'" />'."\n";
1.1053    raeburn  13056:     if (ref($hiddenelements) eq 'HASH') {
                   13057:         foreach my $hidden (sort(keys(%{$hiddenelements}))) {
                   13058:             $output .= '<input type="hidden" name="'.$hidden.'" value="'.$hiddenelements->{$hidden}.'" />'."\n";
                   13059:         }
                   13060:     }
                   13061:     $output .= <<"END";
1.1067    raeburn  13062: <br />
1.1053    raeburn  13063: <input type="submit" name="decompress" value="$lt{'extr'}" />
                   13064: </form>
                   13065: $noextract
                   13066: END
                   13067:     return $output;
                   13068: }
                   13069: 
1.1065    raeburn  13070: sub decompression_utility {
                   13071:     my ($program) = @_;
                   13072:     my @utilities = ('tar','gunzip','bunzip2','unzip'); 
                   13073:     my $location;
                   13074:     if (grep(/^\Q$program\E$/,@utilities)) { 
                   13075:         foreach my $dir ('/bin/','/usr/bin/','/usr/local/bin/','/sbin/',
                   13076:                          '/usr/sbin/') {
                   13077:             if (-x $dir.$program) {
                   13078:                 $location = $dir.$program;
                   13079:                 last;
                   13080:             }
                   13081:         }
                   13082:     }
                   13083:     return $location;
                   13084: }
                   13085: 
                   13086: sub list_archive_contents {
                   13087:     my ($file,$pathsref) = @_;
                   13088:     my (@cmd,$output);
                   13089:     my $needsregexp;
                   13090:     if ($file =~ /\.zip$/) {
                   13091:         @cmd = (&decompression_utility('unzip'),"-l");
                   13092:         $needsregexp = 1;
                   13093:     } elsif (($file =~ m/\.tar\.gz$/) ||
                   13094:              ($file =~ /\.tgz$/)) {
                   13095:         @cmd = (&decompression_utility('tar'),"-ztf");
                   13096:     } elsif ($file =~ /\.tar\.bz2$/) {
                   13097:         @cmd = (&decompression_utility('tar'),"-jtf");
                   13098:     } elsif ($file =~ m|\.tar$|) {
                   13099:         @cmd = (&decompression_utility('tar'),"-tf");
                   13100:     }
                   13101:     if (@cmd) {
                   13102:         undef($!);
                   13103:         undef($@);
                   13104:         if (open(my $fh,"-|", @cmd, $file)) {
                   13105:             while (my $line = <$fh>) {
                   13106:                 $output .= $line;
                   13107:                 chomp($line);
                   13108:                 my $item;
                   13109:                 if ($needsregexp) {
                   13110:                     ($item) = ($line =~ /^\s*\d+\s+[\d\-]+\s+[\d:]+\s*(.+)$/); 
                   13111:                 } else {
                   13112:                     $item = $line;
                   13113:                 }
                   13114:                 if ($item ne '') {
                   13115:                     unless (grep(/^\Q$item\E$/,@{$pathsref})) {
                   13116:                         push(@{$pathsref},$item);
                   13117:                     } 
                   13118:                 }
                   13119:             }
                   13120:             close($fh);
                   13121:         }
                   13122:     }
                   13123:     return $output;
                   13124: }
                   13125: 
1.1053    raeburn  13126: sub decompress_uploaded_file {
                   13127:     my ($file,$dir) = @_;
                   13128:     &Apache::lonnet::appenv({'cgi.file' => $file});
                   13129:     &Apache::lonnet::appenv({'cgi.dir' => $dir});
                   13130:     my $result = &Apache::lonnet::ssi_body('/cgi-bin/decompress.pl');
                   13131:     my ($handle) = ($env{'user.environment'} =~m{/([^/]+)\.id$});
                   13132:     my $lonidsdir = $Apache::lonnet::perlvar{'lonIDsDir'};
                   13133:     &Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle,1);
                   13134:     my $decompressed = $env{'cgi.decompressed'};
                   13135:     &Apache::lonnet::delenv('cgi.file');
                   13136:     &Apache::lonnet::delenv('cgi.dir');
                   13137:     &Apache::lonnet::delenv('cgi.decompressed');
                   13138:     return ($decompressed,$result);
                   13139: }
                   13140: 
1.1055    raeburn  13141: sub process_decompression {
                   13142:     my ($docudom,$docuname,$file,$destination,$dir_root,$hiddenelem) = @_;
1.1075.2.128  raeburn  13143:     unless (($dir_root eq '/userfiles') && ($destination =~ m{^(docs|supplemental)/(default|\d+)/\d+$})) {
                   13144:         return '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
                   13145:                &mt('Unexpected file path.').'</p>'."\n";
                   13146:     }
                   13147:     unless (($docudom =~ /^$match_domain$/) && ($docuname =~ /^$match_courseid$/)) {
                   13148:         return '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
                   13149:                &mt('Unexpected course context.').'</p>'."\n";
                   13150:     }
                   13151:     unless ($file eq &Apache::lonnet::clean_filename($file)) {
                   13152:         return '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
                   13153:                &mt('Filename contained unexpected characters.').'</p>'."\n";
                   13154:     }
1.1055    raeburn  13155:     my ($dir,$error,$warning,$output);
1.1075.2.69  raeburn  13156:     if ($file !~ /\.(zip|tar|bz2|gz|tar.gz|tar.bz2|tgz)$/i) {
1.1075.2.34  raeburn  13157:         $error = &mt('Filename not a supported archive file type.').
                   13158:                  '<br />'.&mt('Filename should end with one of: [_1].',
1.1055    raeburn  13159:                               '.zip, .tar, .bz2, .gz, .tar.gz, .tar.bz2, .tgz');
                   13160:     } else {
                   13161:         my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
                   13162:         if ($docuhome eq 'no_host') {
                   13163:             $error = &mt('Could not determine home server for course.');
                   13164:         } else {
                   13165:             my @ids=&Apache::lonnet::current_machine_ids();
                   13166:             my $currdir = "$dir_root/$destination";
                   13167:             if (grep(/^\Q$docuhome\E$/,@ids)) {
                   13168:                 $dir = &LONCAPA::propath($docudom,$docuname).
                   13169:                        "$dir_root/$destination";
                   13170:             } else {
                   13171:                 $dir = $Apache::lonnet::perlvar{'lonDocRoot'}.
                   13172:                        "$dir_root/$docudom/$docuname/$destination";
                   13173:                 unless (&Apache::lonnet::repcopy_userfile("$dir/$file") eq 'ok') {
                   13174:                     $error = &mt('Archive file not found.');
                   13175:                 }
                   13176:             }
1.1065    raeburn  13177:             my (@to_overwrite,@to_skip);
                   13178:             if ($env{'form.archive_overwrite_total'} > 0) {
                   13179:                 my $total = $env{'form.archive_overwrite_total'};
                   13180:                 for (my $i=0; $i<$total; $i++) {
                   13181:                     if ($env{'form.archive_overwrite_'.$i} == 1) {
                   13182:                         push(@to_overwrite,$env{'form.archive_overwrite_name_'.$i});
                   13183:                     } elsif ($env{'form.archive_overwrite_'.$i} == 0) {
                   13184:                         push(@to_skip,$env{'form.archive_overwrite_name_'.$i});
                   13185:                     }
                   13186:                 }
                   13187:             }
                   13188:             my $numskip = scalar(@to_skip);
1.1075.2.128  raeburn  13189:             my $numoverwrite = scalar(@to_overwrite);
                   13190:             if (($numskip) && (!$numoverwrite)) {
1.1065    raeburn  13191:                 $warning = &mt('All items in the archive file already exist, and no overwriting of existing files has been requested.');         
                   13192:             } elsif ($dir eq '') {
1.1055    raeburn  13193:                 $error = &mt('Directory containing archive file unavailable.');
                   13194:             } elsif (!$error) {
1.1065    raeburn  13195:                 my ($decompressed,$display);
1.1075.2.128  raeburn  13196:                 if (($numskip) || ($numoverwrite)) {
1.1065    raeburn  13197:                     my $tempdir = time.'_'.$$.int(rand(10000));
                   13198:                     mkdir("$dir/$tempdir",0755);
1.1075.2.128  raeburn  13199:                     if (&File::Copy::move("$dir/$file","$dir/$tempdir/$file")) {
                   13200:                         ($decompressed,$display) =
                   13201:                             &decompress_uploaded_file($file,"$dir/$tempdir");
                   13202:                         foreach my $item (@to_skip) {
                   13203:                             if (($item ne '') && ($item !~ /\.\./)) {
                   13204:                                 if (-f "$dir/$tempdir/$item") {
                   13205:                                     unlink("$dir/$tempdir/$item");
                   13206:                                 } elsif (-d "$dir/$tempdir/$item") {
                   13207:                                     &File::Path::remove_tree("$dir/$tempdir/$item",{ safe => 1 });
                   13208:                                 }
                   13209:                             }
                   13210:                         }
                   13211:                         foreach my $item (@to_overwrite) {
                   13212:                             if ((-e "$dir/$tempdir/$item") && (-e "$dir/$item")) {
                   13213:                                 if (($item ne '') && ($item !~ /\.\./)) {
                   13214:                                     if (-f "$dir/$item") {
                   13215:                                         unlink("$dir/$item");
                   13216:                                     } elsif (-d "$dir/$item") {
                   13217:                                         &File::Path::remove_tree("$dir/$item",{ safe => 1 });
                   13218:                                     }
                   13219:                                     &File::Copy::move("$dir/$tempdir/$item","$dir/$item");
                   13220:                                 }
1.1065    raeburn  13221:                             }
                   13222:                         }
1.1075.2.128  raeburn  13223:                         if (&File::Copy::move("$dir/$tempdir/$file","$dir/$file")) {
                   13224:                             &File::Path::remove_tree("$dir/$tempdir",{ safe => 1 });
                   13225:                         }
1.1065    raeburn  13226:                     }
                   13227:                 } else {
                   13228:                     ($decompressed,$display) = 
                   13229:                         &decompress_uploaded_file($file,$dir);
                   13230:                 }
1.1055    raeburn  13231:                 if ($decompressed eq 'ok') {
1.1065    raeburn  13232:                     $output = '<p class="LC_info">'.
                   13233:                               &mt('Files extracted successfully from archive.').
                   13234:                               '</p>'."\n";
1.1055    raeburn  13235:                     my ($warning,$result,@contents);
                   13236:                     my ($newdirlistref,$newlisterror) =
                   13237:                         &Apache::lonnet::dirlist($currdir,$docudom,
                   13238:                                                  $docuname,1);
                   13239:                     my (%is_dir,%changes,@newitems);
                   13240:                     my $dirptr = 16384;
1.1065    raeburn  13241:                     if (ref($newdirlistref) eq 'ARRAY') {
1.1055    raeburn  13242:                         foreach my $dir_line (@{$newdirlistref}) {
                   13243:                             my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
1.1075.2.128  raeburn  13244:                             unless (($item =~ /^\.+$/) || ($item eq $file)) { 
1.1055    raeburn  13245:                                 push(@newitems,$item);
                   13246:                                 if ($dirptr&$testdir) {
                   13247:                                     $is_dir{$item} = 1;
                   13248:                                 }
                   13249:                                 $changes{$item} = 1;
                   13250:                             }
                   13251:                         }
                   13252:                     }
                   13253:                     if (keys(%changes) > 0) {
                   13254:                         foreach my $item (sort(@newitems)) {
                   13255:                             if ($changes{$item}) {
                   13256:                                 push(@contents,$item);
                   13257:                             }
                   13258:                         }
                   13259:                     }
                   13260:                     if (@contents > 0) {
1.1067    raeburn  13261:                         my $wantform;
                   13262:                         unless ($env{'form.autoextract_camtasia'}) {
                   13263:                             $wantform = 1;
                   13264:                         }
1.1056    raeburn  13265:                         my (%children,%parent,%dirorder,%titles);
1.1055    raeburn  13266:                         my ($count,$datatable) = &get_extracted($docudom,$docuname,
                   13267:                                                                 $currdir,\%is_dir,
                   13268:                                                                 \%children,\%parent,
1.1056    raeburn  13269:                                                                 \@contents,\%dirorder,
                   13270:                                                                 \%titles,$wantform);
1.1055    raeburn  13271:                         if ($datatable ne '') {
                   13272:                             $output .= &archive_options_form('decompressed',$datatable,
                   13273:                                                              $count,$hiddenelem);
1.1065    raeburn  13274:                             my $startcount = 6;
1.1055    raeburn  13275:                             $output .= &archive_javascript($startcount,$count,
1.1056    raeburn  13276:                                                            \%titles,\%children);
1.1055    raeburn  13277:                         }
1.1067    raeburn  13278:                         if ($env{'form.autoextract_camtasia'}) {
1.1075.2.59  raeburn  13279:                             my $version = $env{'form.autoextract_camtasia'};
1.1067    raeburn  13280:                             my %displayed;
                   13281:                             my $total = 1;
                   13282:                             $env{'form.archive_directory'} = [];
                   13283:                             foreach my $i (sort { $a <=> $b } keys(%dirorder)) {
                   13284:                                 my $path = join('/',map { $titles{$_}; } @{$dirorder{$i}});
                   13285:                                 $path =~ s{/$}{};
                   13286:                                 my $item;
                   13287:                                 if ($path ne '') {
                   13288:                                     $item = "$path/$titles{$i}";
                   13289:                                 } else {
                   13290:                                     $item = $titles{$i};
                   13291:                                 }
                   13292:                                 $env{'form.archive_content_'.$i} = "$dir_root/$destination/$item";
                   13293:                                 if ($item eq $contents[0]) {
                   13294:                                     push(@{$env{'form.archive_directory'}},$i);
                   13295:                                     $env{'form.archive_'.$i} = 'display';
                   13296:                                     $env{'form.archive_title_'.$i} = $env{'form.camtasia_foldername'};
                   13297:                                     $displayed{'folder'} = $i;
1.1075.2.59  raeburn  13298:                                 } elsif ((($item eq "$contents[0]/index.html") && ($version == 6)) ||
                   13299:                                          (($item eq "$contents[0]/$contents[0]".'.html') && ($version == 8))) {
1.1067    raeburn  13300:                                     $env{'form.archive_'.$i} = 'display';
                   13301:                                     $env{'form.archive_title_'.$i} = $env{'form.camtasia_moviename'};
                   13302:                                     $displayed{'web'} = $i;
                   13303:                                 } else {
1.1075.2.59  raeburn  13304:                                     if ((($item eq "$contents[0]/media") && ($version == 6)) ||
                   13305:                                         ((($item eq "$contents[0]/scripts") || ($item eq "$contents[0]/skins") ||
                   13306:                                              ($item eq "$contents[0]/skins/express_show")) && ($version == 8))) {
1.1067    raeburn  13307:                                         push(@{$env{'form.archive_directory'}},$i);
                   13308:                                     }
                   13309:                                     $env{'form.archive_'.$i} = 'dependency';
                   13310:                                 }
                   13311:                                 $total ++;
                   13312:                             }
                   13313:                             for (my $i=1; $i<$total; $i++) {
                   13314:                                 next if ($i == $displayed{'web'});
                   13315:                                 next if ($i == $displayed{'folder'});
                   13316:                                 $env{'form.archive_dependent_on_'.$i} = $displayed{'web'};
                   13317:                             }
                   13318:                             $env{'form.phase'} = 'decompress_cleanup';
                   13319:                             $env{'form.archivedelete'} = 1;
                   13320:                             $env{'form.archive_count'} = $total-1;
                   13321:                             $output .=
                   13322:                                 &process_extracted_files('coursedocs',$docudom,
                   13323:                                                          $docuname,$destination,
                   13324:                                                          $dir_root,$hiddenelem);
                   13325:                         }
1.1055    raeburn  13326:                     } else {
                   13327:                         $warning = &mt('No new items extracted from archive file.');
                   13328:                     }
                   13329:                 } else {
                   13330:                     $output = $display;
                   13331:                     $error = &mt('An error occurred during extraction from the archive file.');
                   13332:                 }
                   13333:             }
                   13334:         }
                   13335:     }
                   13336:     if ($error) {
                   13337:         $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
                   13338:                    $error.'</p>'."\n";
                   13339:     }
                   13340:     if ($warning) {
                   13341:         $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
                   13342:     }
                   13343:     return $output;
                   13344: }
                   13345: 
                   13346: sub get_extracted {
1.1056    raeburn  13347:     my ($docudom,$docuname,$currdir,$is_dir,$children,$parent,$contents,$dirorder,
                   13348:         $titles,$wantform) = @_;
1.1055    raeburn  13349:     my $count = 0;
                   13350:     my $depth = 0;
                   13351:     my $datatable;
1.1056    raeburn  13352:     my @hierarchy;
1.1055    raeburn  13353:     return unless ((ref($is_dir) eq 'HASH') && (ref($children) eq 'HASH') &&
1.1056    raeburn  13354:                    (ref($parent) eq 'HASH') && (ref($contents) eq 'ARRAY') &&
                   13355:                    (ref($dirorder) eq 'HASH') && (ref($titles) eq 'HASH'));
1.1055    raeburn  13356:     foreach my $item (@{$contents}) {
                   13357:         $count ++;
1.1056    raeburn  13358:         @{$dirorder->{$count}} = @hierarchy;
                   13359:         $titles->{$count} = $item;
1.1055    raeburn  13360:         &archive_hierarchy($depth,$count,$parent,$children);
                   13361:         if ($wantform) {
                   13362:             $datatable .= &archive_row($is_dir->{$item},$item,
                   13363:                                        $currdir,$depth,$count);
                   13364:         }
                   13365:         if ($is_dir->{$item}) {
                   13366:             $depth ++;
1.1056    raeburn  13367:             push(@hierarchy,$count);
                   13368:             $parent->{$depth} = $count;
1.1055    raeburn  13369:             $datatable .=
                   13370:                 &recurse_extracted_archive("$currdir/$item",$docudom,$docuname,
1.1056    raeburn  13371:                                            \$depth,\$count,\@hierarchy,$dirorder,
                   13372:                                            $children,$parent,$titles,$wantform);
1.1055    raeburn  13373:             $depth --;
1.1056    raeburn  13374:             pop(@hierarchy);
1.1055    raeburn  13375:         }
                   13376:     }
                   13377:     return ($count,$datatable);
                   13378: }
                   13379: 
                   13380: sub recurse_extracted_archive {
1.1056    raeburn  13381:     my ($currdir,$docudom,$docuname,$depth,$count,$hierarchy,$dirorder,
                   13382:         $children,$parent,$titles,$wantform) = @_;
1.1055    raeburn  13383:     my $result='';
1.1056    raeburn  13384:     unless ((ref($depth)) && (ref($count)) && (ref($hierarchy) eq 'ARRAY') &&
                   13385:             (ref($children) eq 'HASH') && (ref($parent) eq 'HASH') &&
                   13386:             (ref($dirorder) eq 'HASH')) {
1.1055    raeburn  13387:         return $result;
                   13388:     }
                   13389:     my $dirptr = 16384;
                   13390:     my ($newdirlistref,$newlisterror) =
                   13391:         &Apache::lonnet::dirlist($currdir,$docudom,$docuname,1);
                   13392:     if (ref($newdirlistref) eq 'ARRAY') {
                   13393:         foreach my $dir_line (@{$newdirlistref}) {
                   13394:             my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
                   13395:             unless ($item =~ /^\.+$/) {
                   13396:                 $$count ++;
1.1056    raeburn  13397:                 @{$dirorder->{$$count}} = @{$hierarchy};
                   13398:                 $titles->{$$count} = $item;
1.1055    raeburn  13399:                 &archive_hierarchy($$depth,$$count,$parent,$children);
1.1056    raeburn  13400: 
1.1055    raeburn  13401:                 my $is_dir;
                   13402:                 if ($dirptr&$testdir) {
                   13403:                     $is_dir = 1;
                   13404:                 }
                   13405:                 if ($wantform) {
                   13406:                     $result .= &archive_row($is_dir,$item,$currdir,$$depth,$$count);
                   13407:                 }
                   13408:                 if ($is_dir) {
                   13409:                     $$depth ++;
1.1056    raeburn  13410:                     push(@{$hierarchy},$$count);
                   13411:                     $parent->{$$depth} = $$count;
1.1055    raeburn  13412:                     $result .=
                   13413:                         &recurse_extracted_archive("$currdir/$item",$docudom,
                   13414:                                                    $docuname,$depth,$count,
1.1056    raeburn  13415:                                                    $hierarchy,$dirorder,$children,
                   13416:                                                    $parent,$titles,$wantform);
1.1055    raeburn  13417:                     $$depth --;
1.1056    raeburn  13418:                     pop(@{$hierarchy});
1.1055    raeburn  13419:                 }
                   13420:             }
                   13421:         }
                   13422:     }
                   13423:     return $result;
                   13424: }
                   13425: 
                   13426: sub archive_hierarchy {
                   13427:     my ($depth,$count,$parent,$children) =@_;
                   13428:     if ((ref($parent) eq 'HASH') && (ref($children) eq 'HASH')) {
                   13429:         if (exists($parent->{$depth})) {
                   13430:              $children->{$parent->{$depth}} .= $count.':';
                   13431:         }
                   13432:     }
                   13433:     return;
                   13434: }
                   13435: 
                   13436: sub archive_row {
                   13437:     my ($is_dir,$item,$currdir,$depth,$count) = @_;
                   13438:     my ($name) = ($item =~ m{([^/]+)$});
                   13439:     my %choices = &Apache::lonlocal::texthash (
1.1059    raeburn  13440:                                        'display'    => 'Add as file',
1.1055    raeburn  13441:                                        'dependency' => 'Include as dependency',
                   13442:                                        'discard'    => 'Discard',
                   13443:                                       );
                   13444:     if ($is_dir) {
1.1059    raeburn  13445:         $choices{'display'} = &mt('Add as folder'); 
1.1055    raeburn  13446:     }
1.1056    raeburn  13447:     my $output = &start_data_table_row().'<td align="right">'.$count.'</td>'."\n";
                   13448:     my $offset = 0;
1.1055    raeburn  13449:     foreach my $action ('display','dependency','discard') {
1.1056    raeburn  13450:         $offset ++;
1.1065    raeburn  13451:         if ($action ne 'display') {
                   13452:             $offset ++;
                   13453:         }  
1.1055    raeburn  13454:         $output .= '<td><span class="LC_nobreak">'.
                   13455:                    '<label><input type="radio" name="archive_'.$count.
                   13456:                    '" id="archive_'.$action.'_'.$count.'" value="'.$action.'"';
                   13457:         my $text = $choices{$action};
                   13458:         if ($is_dir) {
                   13459:             $output .= ' onclick="javascript:propagateCheck(this.form,'."'$count'".');"';
                   13460:             if ($action eq 'display') {
1.1059    raeburn  13461:                 $text = &mt('Add as folder');
1.1055    raeburn  13462:             }
1.1056    raeburn  13463:         } else {
                   13464:             $output .= ' onclick="javascript:dependencyCheck(this.form,'."$count,$offset".');"';
                   13465: 
                   13466:         }
                   13467:         $output .= ' />&nbsp;'.$choices{$action}.'</label></span>';
                   13468:         if ($action eq 'dependency') {
                   13469:             $output .= '<div id="arc_depon_'.$count.'" style="display:none;">'."\n".
                   13470:                        &mt('Used by:').'&nbsp;<select name="archive_dependent_on_'.$count.'" '.
                   13471:                        'onchange="propagateSelect(this.form,'."$count,$offset".')">'."\n".
                   13472:                        '<option value=""></option>'."\n".
                   13473:                        '</select>'."\n".
                   13474:                        '</div>';
1.1059    raeburn  13475:         } elsif ($action eq 'display') {
                   13476:             $output .= '<div id="arc_title_'.$count.'" style="display:none;">'."\n".
                   13477:                        &mt('Title:').'&nbsp;<input type="text" name="archive_title_'.$count.'" id="archive_title_'.$count.'" />'."\n".
                   13478:                        '</div>';
1.1055    raeburn  13479:         }
1.1056    raeburn  13480:         $output .= '</td>';
1.1055    raeburn  13481:     }
                   13482:     $output .= '<td><input type="hidden" name="archive_content_'.$count.'" value="'.
                   13483:                &HTML::Entities::encode("$currdir/$item",'"<>&').'" />'.('&nbsp;' x 2);
                   13484:     for (my $i=0; $i<$depth; $i++) {
                   13485:         $output .= ('<img src="/adm/lonIcons/whitespace1.gif" class="LC_docs_spacer" alt="" />' x2)."\n";
                   13486:     }
                   13487:     if ($is_dir) {
                   13488:         $output .= '<img src="/adm/lonIcons/navmap.folder.open.gif" alt="" />&nbsp;'."\n".
                   13489:                    '<input type="hidden" name="archive_directory" value="'.$count.'" />'."\n";
                   13490:     } else {
                   13491:         $output .= '<input type="hidden" name="archive_file" value="'.$count.'" />'."\n";
                   13492:     }
                   13493:     $output .= '&nbsp;'.$name.'</td>'."\n".
                   13494:                &end_data_table_row();
                   13495:     return $output;
                   13496: }
                   13497: 
                   13498: sub archive_options_form {
1.1065    raeburn  13499:     my ($form,$display,$count,$hiddenelem) = @_;
                   13500:     my %lt = &Apache::lonlocal::texthash(
                   13501:                perm => 'Permanently remove archive file?',
                   13502:                hows => 'How should each extracted item be incorporated in the course?',
                   13503:                cont => 'Content actions for all',
                   13504:                addf => 'Add as folder/file',
                   13505:                incd => 'Include as dependency for a displayed file',
                   13506:                disc => 'Discard',
                   13507:                no   => 'No',
                   13508:                yes  => 'Yes',
                   13509:                save => 'Save',
                   13510:     );
                   13511:     my $output = <<"END";
                   13512: <form name="$form" method="post" action="">
                   13513: <p><span class="LC_nobreak">$lt{'perm'}&nbsp;
                   13514: <label>
                   13515:   <input type="radio" name="archivedelete" value="0" checked="checked" />$lt{'no'}
                   13516: </label>
                   13517: &nbsp;
                   13518: <label>
                   13519:   <input type="radio" name="archivedelete" value="1" />$lt{'yes'}</label>
                   13520: </span>
                   13521: </p>
                   13522: <input type="hidden" name="phase" value="decompress_cleanup" />
                   13523: <br />$lt{'hows'}
                   13524: <div class="LC_columnSection">
                   13525:   <fieldset>
                   13526:     <legend>$lt{'cont'}</legend>
                   13527:     <input type="button" value="$lt{'addf'}" onclick="javascript:checkAll(document.$form,'display');" /> 
                   13528:     &nbsp;&nbsp;<input type="button" value="$lt{'incd'}" onclick="javascript:checkAll(document.$form,'dependency');" />
                   13529:     &nbsp;&nbsp;<input type="button" value="$lt{'disc'}" onclick="javascript:checkAll(document.$form,'discard');" />
                   13530:   </fieldset>
                   13531: </div>
                   13532: END
                   13533:     return $output.
1.1055    raeburn  13534:            &start_data_table()."\n".
1.1065    raeburn  13535:            $display."\n".
1.1055    raeburn  13536:            &end_data_table()."\n".
                   13537:            '<input type="hidden" name="archive_count" value="'.$count.'" />'.
                   13538:            $hiddenelem.
1.1065    raeburn  13539:            '<br /><input type="submit" name="archive_submit" value="'.$lt{'save'}.'" />'.
1.1055    raeburn  13540:            '</form>';
                   13541: }
                   13542: 
                   13543: sub archive_javascript {
1.1056    raeburn  13544:     my ($startcount,$numitems,$titles,$children) = @_;
                   13545:     return unless ((ref($titles) eq 'HASH') && (ref($children) eq 'HASH'));
1.1059    raeburn  13546:     my $maintitle = $env{'form.comment'};
1.1055    raeburn  13547:     my $scripttag = <<START;
                   13548: <script type="text/javascript">
                   13549: // <![CDATA[
                   13550: 
                   13551: function checkAll(form,prefix) {
                   13552:     var idstr =  new RegExp("^archive_"+prefix+"_\\\\d+\$");
                   13553:     for (var i=0; i < form.elements.length; i++) {
                   13554:         var id = form.elements[i].id;
                   13555:         if ((id != '') && (id != undefined)) {
                   13556:             if (idstr.test(id)) {
                   13557:                 if (form.elements[i].type == 'radio') {
                   13558:                     form.elements[i].checked = true;
1.1056    raeburn  13559:                     var nostart = i-$startcount;
1.1059    raeburn  13560:                     var offset = nostart%7;
                   13561:                     var count = (nostart-offset)/7;    
1.1056    raeburn  13562:                     dependencyCheck(form,count,offset);
1.1055    raeburn  13563:                 }
                   13564:             }
                   13565:         }
                   13566:     }
                   13567: }
                   13568: 
                   13569: function propagateCheck(form,count) {
                   13570:     if (count > 0) {
1.1059    raeburn  13571:         var startelement = $startcount + ((count-1) * 7);
                   13572:         for (var j=1; j<6; j++) {
                   13573:             if ((j != 2) && (j != 4)) {
1.1056    raeburn  13574:                 var item = startelement + j; 
                   13575:                 if (form.elements[item].type == 'radio') {
                   13576:                     if (form.elements[item].checked) {
                   13577:                         containerCheck(form,count,j);
                   13578:                         break;
                   13579:                     }
1.1055    raeburn  13580:                 }
                   13581:             }
                   13582:         }
                   13583:     }
                   13584: }
                   13585: 
                   13586: numitems = $numitems
1.1056    raeburn  13587: var titles = new Array(numitems);
                   13588: var parents = new Array(numitems);
1.1055    raeburn  13589: for (var i=0; i<numitems; i++) {
1.1056    raeburn  13590:     parents[i] = new Array;
1.1055    raeburn  13591: }
1.1059    raeburn  13592: var maintitle = '$maintitle';
1.1055    raeburn  13593: 
                   13594: START
                   13595: 
1.1056    raeburn  13596:     foreach my $container (sort { $a <=> $b } (keys(%{$children}))) {
                   13597:         my @contents = split(/:/,$children->{$container});
1.1055    raeburn  13598:         for (my $i=0; $i<@contents; $i ++) {
                   13599:             $scripttag .= 'parents['.$container.']['.$i.'] = '.$contents[$i]."\n";
                   13600:         }
                   13601:     }
                   13602: 
1.1056    raeburn  13603:     foreach my $key (sort { $a <=> $b } (keys(%{$titles}))) {
                   13604:         $scripttag .= "titles[$key] = '".$titles->{$key}."';\n";
                   13605:     }
                   13606: 
1.1055    raeburn  13607:     $scripttag .= <<END;
                   13608: 
                   13609: function containerCheck(form,count,offset) {
                   13610:     if (count > 0) {
1.1056    raeburn  13611:         dependencyCheck(form,count,offset);
1.1059    raeburn  13612:         var item = (offset+$startcount)+7*(count-1);
1.1055    raeburn  13613:         form.elements[item].checked = true;
                   13614:         if(Object.prototype.toString.call(parents[count]) === '[object Array]') {
                   13615:             if (parents[count].length > 0) {
                   13616:                 for (var j=0; j<parents[count].length; j++) {
1.1056    raeburn  13617:                     containerCheck(form,parents[count][j],offset);
                   13618:                 }
                   13619:             }
                   13620:         }
                   13621:     }
                   13622: }
                   13623: 
                   13624: function dependencyCheck(form,count,offset) {
                   13625:     if (count > 0) {
1.1059    raeburn  13626:         var chosen = (offset+$startcount)+7*(count-1);
                   13627:         var depitem = $startcount + ((count-1) * 7) + 4;
1.1056    raeburn  13628:         var currtype = form.elements[depitem].type;
                   13629:         if (form.elements[chosen].value == 'dependency') {
                   13630:             document.getElementById('arc_depon_'+count).style.display='block'; 
                   13631:             form.elements[depitem].options.length = 0;
                   13632:             form.elements[depitem].options[0] = new Option('Select','',true,true);
1.1075.2.11  raeburn  13633:             for (var i=1; i<=numitems; i++) {
                   13634:                 if (i == count) {
                   13635:                     continue;
                   13636:                 }
1.1059    raeburn  13637:                 var startelement = $startcount + (i-1) * 7;
                   13638:                 for (var j=1; j<6; j++) {
                   13639:                     if ((j != 2) && (j!= 4)) {
1.1056    raeburn  13640:                         var item = startelement + j;
                   13641:                         if (form.elements[item].type == 'radio') {
                   13642:                             if (form.elements[item].checked) {
                   13643:                                 if (form.elements[item].value == 'display') {
                   13644:                                     var n = form.elements[depitem].options.length;
                   13645:                                     form.elements[depitem].options[n] = new Option(titles[i],i,false,false);
                   13646:                                 }
                   13647:                             }
                   13648:                         }
                   13649:                     }
                   13650:                 }
                   13651:             }
                   13652:         } else {
                   13653:             document.getElementById('arc_depon_'+count).style.display='none';
                   13654:             form.elements[depitem].options.length = 0;
                   13655:             form.elements[depitem].options[0] = new Option('Select','',true,true);
                   13656:         }
1.1059    raeburn  13657:         titleCheck(form,count,offset);
1.1056    raeburn  13658:     }
                   13659: }
                   13660: 
                   13661: function propagateSelect(form,count,offset) {
                   13662:     if (count > 0) {
1.1065    raeburn  13663:         var item = (1+offset+$startcount)+7*(count-1);
1.1056    raeburn  13664:         var picked = form.elements[item].options[form.elements[item].selectedIndex].value; 
                   13665:         if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
                   13666:             if (parents[count].length > 0) {
                   13667:                 for (var j=0; j<parents[count].length; j++) {
                   13668:                     containerSelect(form,parents[count][j],offset,picked);
1.1055    raeburn  13669:                 }
                   13670:             }
                   13671:         }
                   13672:     }
                   13673: }
1.1056    raeburn  13674: 
                   13675: function containerSelect(form,count,offset,picked) {
                   13676:     if (count > 0) {
1.1065    raeburn  13677:         var item = (offset+$startcount)+7*(count-1);
1.1056    raeburn  13678:         if (form.elements[item].type == 'radio') {
                   13679:             if (form.elements[item].value == 'dependency') {
                   13680:                 if (form.elements[item+1].type == 'select-one') {
                   13681:                     for (var i=0; i<form.elements[item+1].options.length; i++) {
                   13682:                         if (form.elements[item+1].options[i].value == picked) {
                   13683:                             form.elements[item+1].selectedIndex = i;
                   13684:                             break;
                   13685:                         }
                   13686:                     }
                   13687:                 }
                   13688:                 if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
                   13689:                     if (parents[count].length > 0) {
                   13690:                         for (var j=0; j<parents[count].length; j++) {
                   13691:                             containerSelect(form,parents[count][j],offset,picked);
                   13692:                         }
                   13693:                     }
                   13694:                 }
                   13695:             }
                   13696:         }
                   13697:     }
                   13698: }
                   13699: 
1.1059    raeburn  13700: function titleCheck(form,count,offset) {
                   13701:     if (count > 0) {
                   13702:         var chosen = (offset+$startcount)+7*(count-1);
                   13703:         var depitem = $startcount + ((count-1) * 7) + 2;
                   13704:         var currtype = form.elements[depitem].type;
                   13705:         if (form.elements[chosen].value == 'display') {
                   13706:             document.getElementById('arc_title_'+count).style.display='block';
                   13707:             if ((count==1) && ((parents[count].length > 0) || (numitems == 1))) {
                   13708:                 document.getElementById('archive_title_'+count).value=maintitle;
                   13709:             }
                   13710:         } else {
                   13711:             document.getElementById('arc_title_'+count).style.display='none';
                   13712:             if (currtype == 'text') { 
                   13713:                 document.getElementById('archive_title_'+count).value='';
                   13714:             }
                   13715:         }
                   13716:     }
                   13717:     return;
                   13718: }
                   13719: 
1.1055    raeburn  13720: // ]]>
                   13721: </script>
                   13722: END
                   13723:     return $scripttag;
                   13724: }
                   13725: 
                   13726: sub process_extracted_files {
1.1067    raeburn  13727:     my ($context,$docudom,$docuname,$destination,$dir_root,$hiddenelem) = @_;
1.1055    raeburn  13728:     my $numitems = $env{'form.archive_count'};
1.1075.2.128  raeburn  13729:     return if ((!$numitems) || ($numitems =~ /\D/));
1.1055    raeburn  13730:     my @ids=&Apache::lonnet::current_machine_ids();
                   13731:     my ($prefix,$pathtocheck,$dir,$ishome,$error,$warning,%toplevelitems,%is_dir,
1.1067    raeburn  13732:         %folders,%containers,%mapinner,%prompttofetch);
1.1055    raeburn  13733:     my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
                   13734:     if (grep(/^\Q$docuhome\E$/,@ids)) {
                   13735:         $prefix = &LONCAPA::propath($docudom,$docuname);
                   13736:         $pathtocheck = "$dir_root/$destination";
                   13737:         $dir = $dir_root;
                   13738:         $ishome = 1;
                   13739:     } else {
                   13740:         $prefix = $Apache::lonnet::perlvar{'lonDocRoot'};
                   13741:         $pathtocheck = "$dir_root/$docudom/$docuname/$destination";
1.1075.2.128  raeburn  13742:         $dir = "$dir_root/$docudom/$docuname";
1.1055    raeburn  13743:     }
                   13744:     my $currdir = "$dir_root/$destination";
                   13745:     (my $docstype,$mapinner{'0'}) = ($destination =~ m{^(docs|supplemental)/(\w+)/});
                   13746:     if ($env{'form.folderpath'}) {
                   13747:         my @items = split('&',$env{'form.folderpath'});
                   13748:         $folders{'0'} = $items[-2];
1.1075.2.17  raeburn  13749:         if ($env{'form.folderpath'} =~ /\:1$/) {
                   13750:             $containers{'0'}='page';
                   13751:         } else {
                   13752:             $containers{'0'}='sequence';
                   13753:         }
1.1055    raeburn  13754:     }
                   13755:     my @archdirs = &get_env_multiple('form.archive_directory');
                   13756:     if ($numitems) {
                   13757:         for (my $i=1; $i<=$numitems; $i++) {
                   13758:             my $path = $env{'form.archive_content_'.$i};
                   13759:             if ($path =~ m{^\Q$pathtocheck\E/([^/]+)$}) {
                   13760:                 my $item = $1;
                   13761:                 $toplevelitems{$item} = $i;
                   13762:                 if (grep(/^\Q$i\E$/,@archdirs)) {
                   13763:                     $is_dir{$item} = 1;
                   13764:                 }
                   13765:             }
                   13766:         }
                   13767:     }
1.1067    raeburn  13768:     my ($output,%children,%parent,%titles,%dirorder,$result);
1.1055    raeburn  13769:     if (keys(%toplevelitems) > 0) {
                   13770:         my @contents = sort(keys(%toplevelitems));
1.1056    raeburn  13771:         (my $count,undef) = &get_extracted($docudom,$docuname,$currdir,\%is_dir,\%children,
                   13772:                                            \%parent,\@contents,\%dirorder,\%titles);
1.1055    raeburn  13773:     }
1.1066    raeburn  13774:     my (%referrer,%orphaned,%todelete,%todeletedir,%newdest,%newseqid);
1.1055    raeburn  13775:     if ($numitems) {
                   13776:         for (my $i=1; $i<=$numitems; $i++) {
1.1075.2.11  raeburn  13777:             next if ($env{'form.archive_'.$i} eq 'dependency');
1.1055    raeburn  13778:             my $path = $env{'form.archive_content_'.$i};
                   13779:             if ($path =~ /^\Q$pathtocheck\E/) {
                   13780:                 if ($env{'form.archive_'.$i} eq 'discard') {
                   13781:                     if ($prefix ne '' && $path ne '') {
                   13782:                         if (-e $prefix.$path) {
1.1066    raeburn  13783:                             if ((@archdirs > 0) && 
                   13784:                                 (grep(/^\Q$i\E$/,@archdirs))) {
                   13785:                                 $todeletedir{$prefix.$path} = 1;
                   13786:                             } else {
                   13787:                                 $todelete{$prefix.$path} = 1;
                   13788:                             }
1.1055    raeburn  13789:                         }
                   13790:                     }
                   13791:                 } elsif ($env{'form.archive_'.$i} eq 'display') {
1.1059    raeburn  13792:                     my ($docstitle,$title,$url,$outer);
1.1055    raeburn  13793:                     ($title) = ($path =~ m{/([^/]+)$});
1.1059    raeburn  13794:                     $docstitle = $env{'form.archive_title_'.$i};
                   13795:                     if ($docstitle eq '') {
                   13796:                         $docstitle = $title;
                   13797:                     }
1.1055    raeburn  13798:                     $outer = 0;
1.1056    raeburn  13799:                     if (ref($dirorder{$i}) eq 'ARRAY') {
                   13800:                         if (@{$dirorder{$i}} > 0) {
                   13801:                             foreach my $item (reverse(@{$dirorder{$i}})) {
1.1055    raeburn  13802:                                 if ($env{'form.archive_'.$item} eq 'display') {
                   13803:                                     $outer = $item;
                   13804:                                     last;
                   13805:                                 }
                   13806:                             }
                   13807:                         }
                   13808:                     }
                   13809:                     my ($errtext,$fatal) = 
                   13810:                         &LONCAPA::map::mapread('/uploaded/'.$docudom.'/'.$docuname.
                   13811:                                                '/'.$folders{$outer}.'.'.
                   13812:                                                $containers{$outer});
                   13813:                     next if ($fatal);
                   13814:                     if ((@archdirs > 0) && (grep(/^\Q$i\E$/,@archdirs))) {
                   13815:                         if ($context eq 'coursedocs') {
1.1056    raeburn  13816:                             $mapinner{$i} = time;
1.1055    raeburn  13817:                             $folders{$i} = 'default_'.$mapinner{$i};
                   13818:                             $containers{$i} = 'sequence';
                   13819:                             my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
                   13820:                                       $folders{$i}.'.'.$containers{$i};
                   13821:                             my $newidx = &LONCAPA::map::getresidx();
                   13822:                             $LONCAPA::map::resources[$newidx]=
1.1059    raeburn  13823:                                 $docstitle.':'.$url.':false:normal:res';
1.1055    raeburn  13824:                             push(@LONCAPA::map::order,$newidx);
                   13825:                             my ($outtext,$errtext) =
                   13826:                                 &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
                   13827:                                                         $docuname.'/'.$folders{$outer}.
1.1075.2.11  raeburn  13828:                                                         '.'.$containers{$outer},1,1);
1.1056    raeburn  13829:                             $newseqid{$i} = $newidx;
1.1067    raeburn  13830:                             unless ($errtext) {
1.1075.2.128  raeburn  13831:                                 $result .=  '<li>'.&mt('Folder: [_1] added to course',
                   13832:                                                        &HTML::Entities::encode($docstitle,'<>&"'))..
                   13833:                                             '</li>'."\n";
1.1067    raeburn  13834:                             }
1.1055    raeburn  13835:                         }
                   13836:                     } else {
                   13837:                         if ($context eq 'coursedocs') {
                   13838:                             my $newidx=&LONCAPA::map::getresidx();
                   13839:                             my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
                   13840:                                       $docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'.
                   13841:                                       $title;
1.1075.2.161.  .13(raeb 13842:-23):                             if (($outer !~ /\D/) &&
                   13843:-23):                                 (($mapinner{$outer} eq 'default') || ($mapinner{$outer} !~ /\D/)) &&
                   13844:-23):                                 ($newidx !~ /\D/)) {
1.1075.2.128  raeburn  13845:                                 if (!-e "$prefix$dir/$docstype/$mapinner{$outer}") {
                   13846:                                     mkdir("$prefix$dir/$docstype/$mapinner{$outer}",0755);
1.1067    raeburn  13847:                                 }
1.1075.2.128  raeburn  13848:                                 if (!-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
                   13849:                                     mkdir("$prefix$dir/$docstype/$mapinner{$outer}/$newidx");
                   13850:                                 }
                   13851:                                 if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
                   13852:                                     if (rename("$prefix$path","$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title")) {
                   13853:                                         $newdest{$i} = "$prefix$dir/$docstype/$mapinner{$outer}/$newidx";
                   13854:                                         unless ($ishome) {
                   13855:                                             my $fetch = "$newdest{$i}/$title";
                   13856:                                             $fetch =~ s/^\Q$prefix$dir\E//;
                   13857:                                             $prompttofetch{$fetch} = 1;
                   13858:                                         }
                   13859:                                    }
                   13860:                                 }
                   13861:                                 $LONCAPA::map::resources[$newidx]=
                   13862:                                     $docstitle.':'.$url.':false:normal:res';
                   13863:                                 push(@LONCAPA::map::order, $newidx);
                   13864:                                 my ($outtext,$errtext)=
                   13865:                                     &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
                   13866:                                                             $docuname.'/'.$folders{$outer}.
                   13867:                                                             '.'.$containers{$outer},1,1);
                   13868:                                 unless ($errtext) {
                   13869:                                     if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title") {
                   13870:                                         $result .= '<li>'.&mt('File: [_1] added to course',
                   13871:                                                               &HTML::Entities::encode($docstitle,'<>&"')).
                   13872:                                                    '</li>'."\n";
                   13873:                                     }
1.1067    raeburn  13874:                                 }
1.1075.2.128  raeburn  13875:                             } else {
                   13876:                                 $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',
                   13877:                                                 &HTML::Entities::encode($path,'<>&"')).'<br />';
1.1067    raeburn  13878:                             }
1.1055    raeburn  13879:                         }
                   13880:                     }
1.1075.2.11  raeburn  13881:                 }
                   13882:             } else {
1.1075.2.128  raeburn  13883:                 $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',
                   13884:                                 &HTML::Entities::encode($path,'<>&"')).'<br />';
1.1075.2.11  raeburn  13885:             }
                   13886:         }
                   13887:         for (my $i=1; $i<=$numitems; $i++) {
                   13888:             next unless ($env{'form.archive_'.$i} eq 'dependency');
                   13889:             my $path = $env{'form.archive_content_'.$i};
                   13890:             if ($path =~ /^\Q$pathtocheck\E/) {
                   13891:                 my ($title) = ($path =~ m{/([^/]+)$});
                   13892:                 $referrer{$i} = $env{'form.archive_dependent_on_'.$i};
                   13893:                 if ($env{'form.archive_'.$referrer{$i}} eq 'display') {
                   13894:                     if (ref($dirorder{$i}) eq 'ARRAY') {
                   13895:                         my ($itemidx,$fullpath,$relpath);
                   13896:                         if (ref($dirorder{$referrer{$i}}) eq 'ARRAY') {
                   13897:                             my $container = $dirorder{$referrer{$i}}->[-1];
1.1056    raeburn  13898:                             for (my $j=0; $j<@{$dirorder{$i}}; $j++) {
1.1075.2.11  raeburn  13899:                                 if ($dirorder{$i}->[$j] eq $container) {
                   13900:                                     $itemidx = $j;
1.1056    raeburn  13901:                                 }
                   13902:                             }
1.1075.2.11  raeburn  13903:                         }
                   13904:                         if ($itemidx eq '') {
                   13905:                             $itemidx =  0;
                   13906:                         }
                   13907:                         if (grep(/^\Q$referrer{$i}\E$/,@archdirs)) {
                   13908:                             if ($mapinner{$referrer{$i}}) {
                   13909:                                 $fullpath = "$prefix$dir/$docstype/$mapinner{$referrer{$i}}";
                   13910:                                 for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
                   13911:                                     if (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
                   13912:                                         unless (defined($newseqid{$dirorder{$i}->[$j]})) {
                   13913:                                             $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
                   13914:                                             $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
                   13915:                                             if (!-e $fullpath) {
                   13916:                                                 mkdir($fullpath,0755);
1.1056    raeburn  13917:                                             }
                   13918:                                         }
1.1075.2.11  raeburn  13919:                                     } else {
                   13920:                                         last;
1.1056    raeburn  13921:                                     }
1.1075.2.11  raeburn  13922:                                 }
                   13923:                             }
                   13924:                         } elsif ($newdest{$referrer{$i}}) {
                   13925:                             $fullpath = $newdest{$referrer{$i}};
                   13926:                             for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
                   13927:                                 if ($env{'form.archive_'.$dirorder{$i}->[$j]} eq 'discard') {
                   13928:                                     $orphaned{$i} = $env{'form.archive_'.$dirorder{$i}->[$j]};
                   13929:                                     last;
                   13930:                                 } elsif (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
                   13931:                                     unless (defined($newseqid{$dirorder{$i}->[$j]})) {
                   13932:                                         $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
                   13933:                                         $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
                   13934:                                         if (!-e $fullpath) {
                   13935:                                             mkdir($fullpath,0755);
1.1056    raeburn  13936:                                         }
                   13937:                                     }
1.1075.2.11  raeburn  13938:                                 } else {
                   13939:                                     last;
1.1056    raeburn  13940:                                 }
1.1075.2.11  raeburn  13941:                             }
                   13942:                         }
                   13943:                         if ($fullpath ne '') {
                   13944:                             if (-e "$prefix$path") {
1.1075.2.128  raeburn  13945:                                 unless (rename("$prefix$path","$fullpath/$title")) {
                   13946:                                      $warning .= &mt('Failed to rename dependency').'<br />';
                   13947:                                 }
1.1075.2.11  raeburn  13948:                             }
                   13949:                             if (-e "$fullpath/$title") {
                   13950:                                 my $showpath;
                   13951:                                 if ($relpath ne '') {
                   13952:                                     $showpath = "$relpath/$title";
                   13953:                                 } else {
                   13954:                                     $showpath = "/$title";
1.1056    raeburn  13955:                                 }
1.1075.2.128  raeburn  13956:                                 $result .= '<li>'.&mt('[_1] included as a dependency',
                   13957:                                                       &HTML::Entities::encode($showpath,'<>&"')).
                   13958:                                            '</li>'."\n";
                   13959:                                 unless ($ishome) {
                   13960:                                     my $fetch = "$fullpath/$title";
                   13961:                                     $fetch =~ s/^\Q$prefix$dir\E//;
                   13962:                                     $prompttofetch{$fetch} = 1;
                   13963:                                 }
1.1055    raeburn  13964:                             }
                   13965:                         }
                   13966:                     }
1.1075.2.11  raeburn  13967:                 } elsif ($env{'form.archive_'.$referrer{$i}} eq 'discard') {
                   13968:                     $warning .= &mt('[_1] is a dependency of [_2], which was discarded.',
1.1075.2.128  raeburn  13969:                                     &HTML::Entities::encode($path,'<>&"'),
                   13970:                                     &HTML::Entities::encode($env{'form.archive_content_'.$referrer{$i}},'<>&"')).
                   13971:                                 '<br />';
1.1055    raeburn  13972:                 }
                   13973:             } else {
1.1075.2.128  raeburn  13974:                 $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',
                   13975:                                 &HTML::Entities::encode($path)).'<br />';
1.1055    raeburn  13976:             }
                   13977:         }
                   13978:         if (keys(%todelete)) {
                   13979:             foreach my $key (keys(%todelete)) {
                   13980:                 unlink($key);
1.1066    raeburn  13981:             }
                   13982:         }
                   13983:         if (keys(%todeletedir)) {
                   13984:             foreach my $key (keys(%todeletedir)) {
                   13985:                 rmdir($key);
                   13986:             }
                   13987:         }
                   13988:         foreach my $dir (sort(keys(%is_dir))) {
                   13989:             if (($pathtocheck ne '') && ($dir ne ''))  {
                   13990:                 &cleanup_empty_dirs($prefix."$pathtocheck/$dir");
1.1055    raeburn  13991:             }
                   13992:         }
1.1067    raeburn  13993:         if ($result ne '') {
                   13994:             $output .= '<ul>'."\n".
                   13995:                        $result."\n".
                   13996:                        '</ul>';
                   13997:         }
                   13998:         unless ($ishome) {
                   13999:             my $replicationfail;
                   14000:             foreach my $item (keys(%prompttofetch)) {
                   14001:                 my $fetchresult= &Apache::lonnet::reply('fetchuserfile:'.$item,$docuhome);
                   14002:                 unless ($fetchresult eq 'ok') {
                   14003:                     $replicationfail .= '<li>'.$item.'</li>'."\n";
                   14004:                 }
                   14005:             }
                   14006:             if ($replicationfail) {
                   14007:                 $output .= '<p class="LC_error">'.
                   14008:                            &mt('Course home server failed to retrieve:').'<ul>'.
                   14009:                            $replicationfail.
                   14010:                            '</ul></p>';
                   14011:             }
                   14012:         }
1.1055    raeburn  14013:     } else {
                   14014:         $warning = &mt('No items found in archive.');
                   14015:     }
                   14016:     if ($error) {
                   14017:         $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
                   14018:                    $error.'</p>'."\n";
                   14019:     }
                   14020:     if ($warning) {
                   14021:         $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
                   14022:     }
                   14023:     return $output;
                   14024: }
                   14025: 
1.1066    raeburn  14026: sub cleanup_empty_dirs {
                   14027:     my ($path) = @_;
                   14028:     if (($path ne '') && (-d $path)) {
                   14029:         if (opendir(my $dirh,$path)) {
                   14030:             my @dircontents = grep(!/^\./,readdir($dirh));
                   14031:             my $numitems = 0;
                   14032:             foreach my $item (@dircontents) {
                   14033:                 if (-d "$path/$item") {
1.1075.2.28  raeburn  14034:                     &cleanup_empty_dirs("$path/$item");
1.1066    raeburn  14035:                     if (-e "$path/$item") {
                   14036:                         $numitems ++;
                   14037:                     }
                   14038:                 } else {
                   14039:                     $numitems ++;
                   14040:                 }
                   14041:             }
                   14042:             if ($numitems == 0) {
                   14043:                 rmdir($path);
                   14044:             }
                   14045:             closedir($dirh);
                   14046:         }
                   14047:     }
                   14048:     return;
                   14049: }
                   14050: 
1.41      ng       14051: =pod
1.45      matthew  14052: 
1.1075.2.56  raeburn  14053: =item * &get_folder_hierarchy()
1.1068    raeburn  14054: 
                   14055: Provides hierarchy of names of folders/sub-folders containing the current
                   14056: item,
                   14057: 
                   14058: Inputs: 3
                   14059:      - $navmap - navmaps object
                   14060: 
                   14061:      - $map - url for map (either the trigger itself, or map containing
                   14062:                            the resource, which is the trigger).
                   14063: 
                   14064:      - $showitem - 1 => show title for map itself; 0 => do not show.
                   14065: 
                   14066: Outputs: 1 @pathitems - array of folder/subfolder names.
                   14067: 
                   14068: =cut
                   14069: 
                   14070: sub get_folder_hierarchy {
                   14071:     my ($navmap,$map,$showitem) = @_;
                   14072:     my @pathitems;
                   14073:     if (ref($navmap)) {
                   14074:         my $mapres = $navmap->getResourceByUrl($map);
                   14075:         if (ref($mapres)) {
                   14076:             my $pcslist = $mapres->map_hierarchy();
                   14077:             if ($pcslist ne '') {
                   14078:                 my @pcs = split(/,/,$pcslist);
                   14079:                 foreach my $pc (@pcs) {
                   14080:                     if ($pc == 1) {
1.1075.2.38  raeburn  14081:                         push(@pathitems,&mt('Main Content'));
1.1068    raeburn  14082:                     } else {
                   14083:                         my $res = $navmap->getByMapPc($pc);
                   14084:                         if (ref($res)) {
                   14085:                             my $title = $res->compTitle();
                   14086:                             $title =~ s/\W+/_/g;
                   14087:                             if ($title ne '') {
                   14088:                                 push(@pathitems,$title);
                   14089:                             }
                   14090:                         }
                   14091:                     }
                   14092:                 }
                   14093:             }
1.1071    raeburn  14094:             if ($showitem) {
                   14095:                 if ($mapres->{ID} eq '0.0') {
1.1075.2.38  raeburn  14096:                     push(@pathitems,&mt('Main Content'));
1.1071    raeburn  14097:                 } else {
                   14098:                     my $maptitle = $mapres->compTitle();
                   14099:                     $maptitle =~ s/\W+/_/g;
                   14100:                     if ($maptitle ne '') {
                   14101:                         push(@pathitems,$maptitle);
                   14102:                     }
1.1068    raeburn  14103:                 }
                   14104:             }
                   14105:         }
                   14106:     }
                   14107:     return @pathitems;
                   14108: }
                   14109: 
                   14110: =pod
                   14111: 
1.1015    raeburn  14112: =item * &get_turnedin_filepath()
                   14113: 
                   14114: Determines path in a user's portfolio file for storage of files uploaded
                   14115: to a specific essayresponse or dropbox item.
                   14116: 
                   14117: Inputs: 3 required + 1 optional.
                   14118: $symb is symb for resource, $uname and $udom are for current user (required).
                   14119: $caller is optional (can be "submission", if routine is called when storing
                   14120: an upoaded file when "Submit Answer" button was pressed).
                   14121: 
                   14122: Returns array containing $path and $multiresp. 
                   14123: $path is path in portfolio.  $multiresp is 1 if this resource contains more
                   14124: than one file upload item.  Callers of routine should append partid as a 
                   14125: subdirectory to $path in cases where $multiresp is 1.
                   14126: 
                   14127: Called by: homework/essayresponse.pm and homework/structuretags.pm
                   14128: 
                   14129: =cut
                   14130: 
                   14131: sub get_turnedin_filepath {
                   14132:     my ($symb,$uname,$udom,$caller) = @_;
                   14133:     my ($map,$resid,$resurl)=&Apache::lonnet::decode_symb($symb);
                   14134:     my $turnindir;
                   14135:     my %userhash = &Apache::lonnet::userenvironment($udom,$uname,'turnindir');
                   14136:     $turnindir = $userhash{'turnindir'};
                   14137:     my ($path,$multiresp);
                   14138:     if ($turnindir eq '') {
                   14139:         if ($caller eq 'submission') {
                   14140:             $turnindir = &mt('turned in');
                   14141:             $turnindir =~ s/\W+/_/g;
                   14142:             my %newhash = (
                   14143:                             'turnindir' => $turnindir,
                   14144:                           );
                   14145:             &Apache::lonnet::put('environment',\%newhash,$udom,$uname);
                   14146:         }
                   14147:     }
                   14148:     if ($turnindir ne '') {
                   14149:         $path = '/'.$turnindir.'/';
                   14150:         my ($multipart,$turnin,@pathitems);
                   14151:         my $navmap = Apache::lonnavmaps::navmap->new();
                   14152:         if (defined($navmap)) {
                   14153:             my $mapres = $navmap->getResourceByUrl($map);
                   14154:             if (ref($mapres)) {
                   14155:                 my $pcslist = $mapres->map_hierarchy();
                   14156:                 if ($pcslist ne '') {
                   14157:                     foreach my $pc (split(/,/,$pcslist)) {
                   14158:                         my $res = $navmap->getByMapPc($pc);
                   14159:                         if (ref($res)) {
                   14160:                             my $title = $res->compTitle();
                   14161:                             $title =~ s/\W+/_/g;
                   14162:                             if ($title ne '') {
1.1075.2.48  raeburn  14163:                                 if (($pc > 1) && (length($title) > 12)) {
                   14164:                                     $title = substr($title,0,12);
                   14165:                                 }
1.1015    raeburn  14166:                                 push(@pathitems,$title);
                   14167:                             }
                   14168:                         }
                   14169:                     }
                   14170:                 }
                   14171:                 my $maptitle = $mapres->compTitle();
                   14172:                 $maptitle =~ s/\W+/_/g;
                   14173:                 if ($maptitle ne '') {
1.1075.2.48  raeburn  14174:                     if (length($maptitle) > 12) {
                   14175:                         $maptitle = substr($maptitle,0,12);
                   14176:                     }
1.1015    raeburn  14177:                     push(@pathitems,$maptitle);
                   14178:                 }
                   14179:                 unless ($env{'request.state'} eq 'construct') {
                   14180:                     my $res = $navmap->getBySymb($symb);
                   14181:                     if (ref($res)) {
                   14182:                         my $partlist = $res->parts();
                   14183:                         my $totaluploads = 0;
                   14184:                         if (ref($partlist) eq 'ARRAY') {
                   14185:                             foreach my $part (@{$partlist}) {
                   14186:                                 my @types = $res->responseType($part);
                   14187:                                 my @ids = $res->responseIds($part);
                   14188:                                 for (my $i=0; $i < scalar(@ids); $i++) {
                   14189:                                     if ($types[$i] eq 'essay') {
                   14190:                                         my $partid = $part.'_'.$ids[$i];
                   14191:                                         if (&Apache::lonnet::EXT("resource.$partid.uploadedfiletypes") ne '') {
                   14192:                                             $totaluploads ++;
                   14193:                                         }
                   14194:                                     }
                   14195:                                 }
                   14196:                             }
                   14197:                             if ($totaluploads > 1) {
                   14198:                                 $multiresp = 1;
                   14199:                             }
                   14200:                         }
                   14201:                     }
                   14202:                 }
                   14203:             } else {
                   14204:                 return;
                   14205:             }
                   14206:         } else {
                   14207:             return;
                   14208:         }
                   14209:         my $restitle=&Apache::lonnet::gettitle($symb);
                   14210:         $restitle =~ s/\W+/_/g;
                   14211:         if ($restitle eq '') {
                   14212:             $restitle = ($resurl =~ m{/[^/]+$});
                   14213:             if ($restitle eq '') {
                   14214:                 $restitle = time;
                   14215:             }
                   14216:         }
1.1075.2.48  raeburn  14217:         if (length($restitle) > 12) {
                   14218:             $restitle = substr($restitle,0,12);
                   14219:         }
1.1015    raeburn  14220:         push(@pathitems,$restitle);
                   14221:         $path .= join('/',@pathitems);
                   14222:     }
                   14223:     return ($path,$multiresp);
                   14224: }
                   14225: 
                   14226: =pod
                   14227: 
1.464     albertel 14228: =back
1.41      ng       14229: 
1.112     bowersj2 14230: =head1 CSV Upload/Handling functions
1.38      albertel 14231: 
1.41      ng       14232: =over 4
                   14233: 
1.648     raeburn  14234: =item * &upfile_store($r)
1.41      ng       14235: 
                   14236: Store uploaded file, $r should be the HTTP Request object,
1.258     albertel 14237: needs $env{'form.upfile'}
1.41      ng       14238: returns $datatoken to be put into hidden field
                   14239: 
                   14240: =cut
1.31      albertel 14241: 
                   14242: sub upfile_store {
                   14243:     my $r=shift;
1.258     albertel 14244:     $env{'form.upfile'}=~s/\r/\n/gs;
                   14245:     $env{'form.upfile'}=~s/\f/\n/gs;
                   14246:     $env{'form.upfile'}=~s/\n+/\n/gs;
                   14247:     $env{'form.upfile'}=~s/\n+$//gs;
1.31      albertel 14248: 
1.1075.2.128  raeburn  14249:     my $datatoken = &valid_datatoken($env{'user.name'}.'_'.$env{'user.domain'}.
                   14250:                                      '_enroll_'.$env{'request.course.id'}.'_'.
                   14251:                                      time.'_'.$$);
                   14252:     return if ($datatoken eq '');
                   14253: 
1.31      albertel 14254:     {
1.158     raeburn  14255:         my $datafile = $r->dir_config('lonDaemons').
                   14256:                            '/tmp/'.$datatoken.'.tmp';
1.1075.2.128  raeburn  14257:         if ( open(my $fh,'>',$datafile) ) {
1.258     albertel 14258:             print $fh $env{'form.upfile'};
1.158     raeburn  14259:             close($fh);
                   14260:         }
1.31      albertel 14261:     }
                   14262:     return $datatoken;
                   14263: }
                   14264: 
1.56      matthew  14265: =pod
                   14266: 
1.1075.2.128  raeburn  14267: =item * &load_tmp_file($r,$datatoken)
1.41      ng       14268: 
                   14269: Load uploaded file from tmp, $r should be the HTTP Request object,
1.1075.2.128  raeburn  14270: $datatoken is the name to assign to the temporary file.
1.258     albertel 14271: sets $env{'form.upfile'} to the contents of the file
1.41      ng       14272: 
                   14273: =cut
1.31      albertel 14274: 
                   14275: sub load_tmp_file {
1.1075.2.128  raeburn  14276:     my ($r,$datatoken) = @_;
                   14277:     return if ($datatoken eq '');
1.31      albertel 14278:     my @studentdata=();
                   14279:     {
1.158     raeburn  14280:         my $studentfile = $r->dir_config('lonDaemons').
1.1075.2.128  raeburn  14281:                               '/tmp/'.$datatoken.'.tmp';
                   14282:         if ( open(my $fh,'<',$studentfile) ) {
1.158     raeburn  14283:             @studentdata=<$fh>;
                   14284:             close($fh);
                   14285:         }
1.31      albertel 14286:     }
1.258     albertel 14287:     $env{'form.upfile'}=join('',@studentdata);
1.31      albertel 14288: }
                   14289: 
1.1075.2.128  raeburn  14290: sub valid_datatoken {
                   14291:     my ($datatoken) = @_;
1.1075.2.131  raeburn  14292:     if ($datatoken =~ /^$match_username\_$match_domain\_enroll_(|$match_domain\_$match_courseid)\_\d+_\d+$/) {
1.1075.2.128  raeburn  14293:         return $datatoken;
                   14294:     }
                   14295:     return;
                   14296: }
                   14297: 
1.56      matthew  14298: =pod
                   14299: 
1.648     raeburn  14300: =item * &upfile_record_sep()
1.41      ng       14301: 
                   14302: Separate uploaded file into records
                   14303: returns array of records,
1.258     albertel 14304: needs $env{'form.upfile'} and $env{'form.upfiletype'}
1.41      ng       14305: 
                   14306: =cut
1.31      albertel 14307: 
                   14308: sub upfile_record_sep {
1.258     albertel 14309:     if ($env{'form.upfiletype'} eq 'xml') {
1.31      albertel 14310:     } else {
1.248     albertel 14311: 	my @records;
1.258     albertel 14312: 	foreach my $line (split(/\n/,$env{'form.upfile'})) {
1.248     albertel 14313: 	    if ($line=~/^\s*$/) { next; }
                   14314: 	    push(@records,$line);
                   14315: 	}
                   14316: 	return @records;
1.31      albertel 14317:     }
                   14318: }
                   14319: 
1.56      matthew  14320: =pod
                   14321: 
1.648     raeburn  14322: =item * &record_sep($record)
1.41      ng       14323: 
1.258     albertel 14324: Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'}
1.41      ng       14325: 
                   14326: =cut
                   14327: 
1.263     www      14328: sub takeleft {
                   14329:     my $index=shift;
                   14330:     return substr('0000'.$index,-4,4);
                   14331: }
                   14332: 
1.31      albertel 14333: sub record_sep {
                   14334:     my $record=shift;
                   14335:     my %components=();
1.258     albertel 14336:     if ($env{'form.upfiletype'} eq 'xml') {
                   14337:     } elsif ($env{'form.upfiletype'} eq 'space') {
1.31      albertel 14338:         my $i=0;
1.356     albertel 14339:         foreach my $field (split(/\s+/,$record)) {
1.31      albertel 14340:             $field=~s/^(\"|\')//;
                   14341:             $field=~s/(\"|\')$//;
1.263     www      14342:             $components{&takeleft($i)}=$field;
1.31      albertel 14343:             $i++;
                   14344:         }
1.258     albertel 14345:     } elsif ($env{'form.upfiletype'} eq 'tab') {
1.31      albertel 14346:         my $i=0;
1.356     albertel 14347:         foreach my $field (split(/\t/,$record)) {
1.31      albertel 14348:             $field=~s/^(\"|\')//;
                   14349:             $field=~s/(\"|\')$//;
1.263     www      14350:             $components{&takeleft($i)}=$field;
1.31      albertel 14351:             $i++;
                   14352:         }
                   14353:     } else {
1.561     www      14354:         my $separator=',';
1.480     banghart 14355:         if ($env{'form.upfiletype'} eq 'semisv') {
1.561     www      14356:             $separator=';';
1.480     banghart 14357:         }
1.31      albertel 14358:         my $i=0;
1.561     www      14359: # the character we are looking for to indicate the end of a quote or a record 
                   14360:         my $looking_for=$separator;
                   14361: # do not add the characters to the fields
                   14362:         my $ignore=0;
                   14363: # we just encountered a separator (or the beginning of the record)
                   14364:         my $just_found_separator=1;
                   14365: # store the field we are working on here
                   14366:         my $field='';
                   14367: # work our way through all characters in record
                   14368:         foreach my $character ($record=~/(.)/g) {
                   14369:             if ($character eq $looking_for) {
                   14370:                if ($character ne $separator) {
                   14371: # Found the end of a quote, again looking for separator
                   14372:                   $looking_for=$separator;
                   14373:                   $ignore=1;
                   14374:                } else {
                   14375: # Found a separator, store away what we got
                   14376:                   $components{&takeleft($i)}=$field;
                   14377: 	          $i++;
                   14378:                   $just_found_separator=1;
                   14379:                   $ignore=0;
                   14380:                   $field='';
                   14381:                }
                   14382:                next;
                   14383:             }
                   14384: # single or double quotation marks after a separator indicate beginning of a quote
                   14385: # we are now looking for the end of the quote and need to ignore separators
                   14386:             if ((($character eq '"') || ($character eq "'")) && ($just_found_separator))  {
                   14387:                $looking_for=$character;
                   14388:                next;
                   14389:             }
                   14390: # ignore would be true after we reached the end of a quote
                   14391:             if ($ignore) { next; }
                   14392:             if (($just_found_separator) && ($character=~/\s/)) { next; }
                   14393:             $field.=$character;
                   14394:             $just_found_separator=0; 
1.31      albertel 14395:         }
1.561     www      14396: # catch the very last entry, since we never encountered the separator
                   14397:         $components{&takeleft($i)}=$field;
1.31      albertel 14398:     }
                   14399:     return %components;
                   14400: }
                   14401: 
1.144     matthew  14402: ######################################################
                   14403: ######################################################
                   14404: 
1.56      matthew  14405: =pod
                   14406: 
1.648     raeburn  14407: =item * &upfile_select_html()
1.41      ng       14408: 
1.144     matthew  14409: Return HTML code to select a file from the users machine and specify 
                   14410: the file type.
1.41      ng       14411: 
                   14412: =cut
                   14413: 
1.144     matthew  14414: ######################################################
                   14415: ######################################################
1.31      albertel 14416: sub upfile_select_html {
1.144     matthew  14417:     my %Types = (
                   14418:                  csv   => &mt('CSV (comma separated values, spreadsheet)'),
1.480     banghart 14419:                  semisv => &mt('Semicolon separated values'),
1.144     matthew  14420:                  space => &mt('Space separated'),
                   14421:                  tab   => &mt('Tabulator separated'),
                   14422: #                 xml   => &mt('HTML/XML'),
                   14423:                  );
                   14424:     my $Str = '<input type="file" name="upfile" size="50" />'.
1.727     riegler  14425:         '<br />'.&mt('Type').': <select name="upfiletype">';
1.144     matthew  14426:     foreach my $type (sort(keys(%Types))) {
                   14427:         $Str .= '<option value="'.$type.'" >'.$Types{$type}."</option>\n";
                   14428:     }
                   14429:     $Str .= "</select>\n";
                   14430:     return $Str;
1.31      albertel 14431: }
                   14432: 
1.301     albertel 14433: sub get_samples {
                   14434:     my ($records,$toget) = @_;
                   14435:     my @samples=({});
                   14436:     my $got=0;
                   14437:     foreach my $rec (@$records) {
                   14438: 	my %temp = &record_sep($rec);
                   14439: 	if (! grep(/\S/, values(%temp))) { next; }
                   14440: 	if (%temp) {
                   14441: 	    $samples[$got]=\%temp;
                   14442: 	    $got++;
                   14443: 	    if ($got == $toget) { last; }
                   14444: 	}
                   14445:     }
                   14446:     return \@samples;
                   14447: }
                   14448: 
1.144     matthew  14449: ######################################################
                   14450: ######################################################
                   14451: 
1.56      matthew  14452: =pod
                   14453: 
1.648     raeburn  14454: =item * &csv_print_samples($r,$records)
1.41      ng       14455: 
                   14456: Prints a table of sample values from each column uploaded $r is an
                   14457: Apache Request ref, $records is an arrayref from
                   14458: &Apache::loncommon::upfile_record_sep
                   14459: 
                   14460: =cut
                   14461: 
1.144     matthew  14462: ######################################################
                   14463: ######################################################
1.31      albertel 14464: sub csv_print_samples {
                   14465:     my ($r,$records) = @_;
1.662     bisitz   14466:     my $samples = &get_samples($records,5);
1.301     albertel 14467: 
1.594     raeburn  14468:     $r->print(&mt('Samples').'<br />'.&start_data_table().
                   14469:               &start_data_table_header_row());
1.356     albertel 14470:     foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) { 
1.845     bisitz   14471:         $r->print('<th>'.&mt('Column [_1]',($sample+1)).'</th>'); }
1.594     raeburn  14472:     $r->print(&end_data_table_header_row());
1.301     albertel 14473:     foreach my $hash (@$samples) {
1.594     raeburn  14474: 	$r->print(&start_data_table_row());
1.356     albertel 14475: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.31      albertel 14476: 	    $r->print('<td>');
1.356     albertel 14477: 	    if (defined($$hash{$sample})) { $r->print($$hash{$sample}); }
1.31      albertel 14478: 	    $r->print('</td>');
                   14479: 	}
1.594     raeburn  14480: 	$r->print(&end_data_table_row());
1.31      albertel 14481:     }
1.594     raeburn  14482:     $r->print(&end_data_table().'<br />'."\n");
1.31      albertel 14483: }
                   14484: 
1.144     matthew  14485: ######################################################
                   14486: ######################################################
                   14487: 
1.56      matthew  14488: =pod
                   14489: 
1.648     raeburn  14490: =item * &csv_print_select_table($r,$records,$d)
1.41      ng       14491: 
                   14492: Prints a table to create associations between values and table columns.
1.144     matthew  14493: 
1.41      ng       14494: $r is an Apache Request ref,
                   14495: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
1.174     matthew  14496: $d is an array of 2 element arrays (internal name, displayed name,defaultcol)
1.41      ng       14497: 
                   14498: =cut
                   14499: 
1.144     matthew  14500: ######################################################
                   14501: ######################################################
1.31      albertel 14502: sub csv_print_select_table {
                   14503:     my ($r,$records,$d) = @_;
1.301     albertel 14504:     my $i=0;
                   14505:     my $samples = &get_samples($records,1);
1.144     matthew  14506:     $r->print(&mt('Associate columns with student attributes.')."\n".
1.594     raeburn  14507: 	      &start_data_table().&start_data_table_header_row().
1.144     matthew  14508:               '<th>'.&mt('Attribute').'</th>'.
1.594     raeburn  14509:               '<th>'.&mt('Column').'</th>'.
                   14510:               &end_data_table_header_row()."\n");
1.356     albertel 14511:     foreach my $array_ref (@$d) {
                   14512: 	my ($value,$display,$defaultcol)=@{ $array_ref };
1.729     raeburn  14513: 	$r->print(&start_data_table_row().'<td>'.$display.'</td>');
1.31      albertel 14514: 
1.875     bisitz   14515: 	$r->print('<td><select name="f'.$i.'"'.
1.32      matthew  14516: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.31      albertel 14517: 	$r->print('<option value="none"></option>');
1.356     albertel 14518: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
                   14519: 	    $r->print('<option value="'.$sample.'"'.
                   14520:                       ($sample eq $defaultcol ? ' selected="selected" ' : '').
1.662     bisitz   14521:                       '>'.&mt('Column [_1]',($sample+1)).'</option>');
1.31      albertel 14522: 	}
1.594     raeburn  14523: 	$r->print('</select></td>'.&end_data_table_row()."\n");
1.31      albertel 14524: 	$i++;
                   14525:     }
1.594     raeburn  14526:     $r->print(&end_data_table());
1.31      albertel 14527:     $i--;
                   14528:     return $i;
                   14529: }
1.56      matthew  14530: 
1.144     matthew  14531: ######################################################
                   14532: ######################################################
                   14533: 
1.56      matthew  14534: =pod
1.31      albertel 14535: 
1.648     raeburn  14536: =item * &csv_samples_select_table($r,$records,$d)
1.41      ng       14537: 
                   14538: Prints a table of sample values from the upload and can make associate samples to internal names.
                   14539: 
                   14540: $r is an Apache Request ref,
                   14541: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
                   14542: $d is an array of 2 element arrays (internal name, displayed name)
                   14543: 
                   14544: =cut
                   14545: 
1.144     matthew  14546: ######################################################
                   14547: ######################################################
1.31      albertel 14548: sub csv_samples_select_table {
                   14549:     my ($r,$records,$d) = @_;
                   14550:     my $i=0;
1.144     matthew  14551:     #
1.662     bisitz   14552:     my $max_samples = 5;
                   14553:     my $samples = &get_samples($records,$max_samples);
1.594     raeburn  14554:     $r->print(&start_data_table().
                   14555:               &start_data_table_header_row().'<th>'.
                   14556:               &mt('Field').'</th><th>'.&mt('Samples').'</th>'.
                   14557:               &end_data_table_header_row());
1.301     albertel 14558: 
                   14559:     foreach my $key (sort(keys(%{ $samples->[0] }))) {
1.594     raeburn  14560: 	$r->print(&start_data_table_row().'<td><select name="f'.$i.'"'.
1.32      matthew  14561: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.301     albertel 14562: 	foreach my $option (@$d) {
                   14563: 	    my ($value,$display,$defaultcol)=@{ $option };
1.174     matthew  14564: 	    $r->print('<option value="'.$value.'"'.
1.253     albertel 14565:                       ($i eq $defaultcol ? ' selected="selected" ':'').'>'.
1.174     matthew  14566:                       $display.'</option>');
1.31      albertel 14567: 	}
                   14568: 	$r->print('</select></td><td>');
1.662     bisitz   14569: 	foreach my $line (0..($max_samples-1)) {
1.301     albertel 14570: 	    if (defined($samples->[$line]{$key})) { 
                   14571: 		$r->print($samples->[$line]{$key}."<br />\n"); 
                   14572: 	    }
                   14573: 	}
1.594     raeburn  14574: 	$r->print('</td>'.&end_data_table_row());
1.31      albertel 14575: 	$i++;
                   14576:     }
1.594     raeburn  14577:     $r->print(&end_data_table());
1.31      albertel 14578:     $i--;
                   14579:     return($i);
1.115     matthew  14580: }
                   14581: 
1.144     matthew  14582: ######################################################
                   14583: ######################################################
                   14584: 
1.115     matthew  14585: =pod
                   14586: 
1.648     raeburn  14587: =item * &clean_excel_name($name)
1.115     matthew  14588: 
                   14589: Returns a replacement for $name which does not contain any illegal characters.
                   14590: 
                   14591: =cut
                   14592: 
1.144     matthew  14593: ######################################################
                   14594: ######################################################
1.115     matthew  14595: sub clean_excel_name {
                   14596:     my ($name) = @_;
                   14597:     $name =~ s/[:\*\?\/\\]//g;
                   14598:     if (length($name) > 31) {
                   14599:         $name = substr($name,0,31);
                   14600:     }
                   14601:     return $name;
1.25      albertel 14602: }
1.84      albertel 14603: 
1.85      albertel 14604: =pod
                   14605: 
1.648     raeburn  14606: =item * &check_if_partid_hidden($id,$symb,$udom,$uname)
1.85      albertel 14607: 
                   14608: Returns either 1 or undef
                   14609: 
                   14610: 1 if the part is to be hidden, undef if it is to be shown
                   14611: 
                   14612: Arguments are:
                   14613: 
                   14614: $id the id of the part to be checked
                   14615: $symb, optional the symb of the resource to check
                   14616: $udom, optional the domain of the user to check for
                   14617: $uname, optional the username of the user to check for
                   14618: 
                   14619: =cut
1.84      albertel 14620: 
                   14621: sub check_if_partid_hidden {
                   14622:     my ($id,$symb,$udom,$uname) = @_;
1.133     albertel 14623:     my $hiddenparts=&Apache::lonnet::EXT('resource.0.hiddenparts',
1.84      albertel 14624: 					 $symb,$udom,$uname);
1.141     albertel 14625:     my $truth=1;
                   14626:     #if the string starts with !, then the list is the list to show not hide
                   14627:     if ($hiddenparts=~s/^\s*!//) { $truth=undef; }
1.84      albertel 14628:     my @hiddenlist=split(/,/,$hiddenparts);
                   14629:     foreach my $checkid (@hiddenlist) {
1.141     albertel 14630: 	if ($checkid =~ /^\s*\Q$id\E\s*$/) { return $truth; }
1.84      albertel 14631:     }
1.141     albertel 14632:     return !$truth;
1.84      albertel 14633: }
1.127     matthew  14634: 
1.138     matthew  14635: 
                   14636: ############################################################
                   14637: ############################################################
                   14638: 
                   14639: =pod
                   14640: 
1.157     matthew  14641: =back 
                   14642: 
1.138     matthew  14643: =head1 cgi-bin script and graphing routines
                   14644: 
1.157     matthew  14645: =over 4
                   14646: 
1.648     raeburn  14647: =item * &get_cgi_id()
1.138     matthew  14648: 
                   14649: Inputs: none
                   14650: 
                   14651: Returns an id which can be used to pass environment variables
                   14652: to various cgi-bin scripts.  These environment variables will
                   14653: be removed from the users environment after a given time by
                   14654: the routine &Apache::lonnet::transfer_profile_to_env.
                   14655: 
                   14656: =cut
                   14657: 
                   14658: ############################################################
                   14659: ############################################################
1.152     albertel 14660: my $uniq=0;
1.136     matthew  14661: sub get_cgi_id {
1.154     albertel 14662:     $uniq=($uniq+1)%100000;
1.280     albertel 14663:     return (time.'_'.$$.'_'.$uniq);
1.136     matthew  14664: }
                   14665: 
1.127     matthew  14666: ############################################################
                   14667: ############################################################
                   14668: 
                   14669: =pod
                   14670: 
1.648     raeburn  14671: =item * &DrawBarGraph()
1.127     matthew  14672: 
1.138     matthew  14673: Facilitates the plotting of data in a (stacked) bar graph.
                   14674: Puts plot definition data into the users environment in order for 
                   14675: graph.png to plot it.  Returns an <img> tag for the plot.
                   14676: The bars on the plot are labeled '1','2',...,'n'.
                   14677: 
                   14678: Inputs:
                   14679: 
                   14680: =over 4
                   14681: 
                   14682: =item $Title: string, the title of the plot
                   14683: 
                   14684: =item $xlabel: string, text describing the X-axis of the plot
                   14685: 
                   14686: =item $ylabel: string, text describing the Y-axis of the plot
                   14687: 
                   14688: =item $Max: scalar, the maximum Y value to use in the plot
                   14689: If $Max is < any data point, the graph will not be rendered.
                   14690: 
1.140     matthew  14691: =item $colors: array ref holding the colors to be used for the data sets when
1.138     matthew  14692: they are plotted.  If undefined, default values will be used.
                   14693: 
1.178     matthew  14694: =item $labels: array ref holding the labels to use on the x-axis for the bars.
                   14695: 
1.138     matthew  14696: =item @Values: An array of array references.  Each array reference holds data
                   14697: to be plotted in a stacked bar chart.
                   14698: 
1.239     matthew  14699: =item If the final element of @Values is a hash reference the key/value
                   14700: pairs will be added to the graph definition.
                   14701: 
1.138     matthew  14702: =back
                   14703: 
                   14704: Returns:
                   14705: 
                   14706: An <img> tag which references graph.png and the appropriate identifying
                   14707: information for the plot.
                   14708: 
1.127     matthew  14709: =cut
                   14710: 
                   14711: ############################################################
                   14712: ############################################################
1.134     matthew  14713: sub DrawBarGraph {
1.178     matthew  14714:     my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_;
1.134     matthew  14715:     #
                   14716:     if (! defined($colors)) {
                   14717:         $colors = ['#33ff00', 
                   14718:                   '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
                   14719:                   '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
                   14720:                   ]; 
                   14721:     }
1.228     matthew  14722:     my $extra_settings = {};
                   14723:     if (ref($Values[-1]) eq 'HASH') {
                   14724:         $extra_settings = pop(@Values);
                   14725:     }
1.127     matthew  14726:     #
1.136     matthew  14727:     my $identifier = &get_cgi_id();
                   14728:     my $id = 'cgi.'.$identifier;        
1.129     matthew  14729:     if (! @Values || ref($Values[0]) ne 'ARRAY') {
1.127     matthew  14730:         return '';
                   14731:     }
1.225     matthew  14732:     #
                   14733:     my @Labels;
                   14734:     if (defined($labels)) {
                   14735:         @Labels = @$labels;
                   14736:     } else {
                   14737:         for (my $i=0;$i<@{$Values[0]};$i++) {
1.1075.2.119  raeburn  14738:             push(@Labels,$i+1);
1.225     matthew  14739:         }
                   14740:     }
                   14741:     #
1.129     matthew  14742:     my $NumBars = scalar(@{$Values[0]});
1.225     matthew  14743:     if ($NumBars < scalar(@Labels)) { $NumBars = scalar(@Labels); }
1.129     matthew  14744:     my %ValuesHash;
                   14745:     my $NumSets=1;
                   14746:     foreach my $array (@Values) {
                   14747:         next if (! ref($array));
1.136     matthew  14748:         $ValuesHash{$id.'.data.'.$NumSets++} = 
1.132     matthew  14749:             join(',',@$array);
1.129     matthew  14750:     }
1.127     matthew  14751:     #
1.136     matthew  14752:     my ($height,$width,$xskip,$bar_width) = (200,120,1,15);
1.225     matthew  14753:     if ($NumBars < 3) {
                   14754:         $width = 120+$NumBars*32;
1.220     matthew  14755:         $xskip = 1;
1.225     matthew  14756:         $bar_width = 30;
                   14757:     } elsif ($NumBars < 5) {
                   14758:         $width = 120+$NumBars*20;
                   14759:         $xskip = 1;
                   14760:         $bar_width = 20;
1.220     matthew  14761:     } elsif ($NumBars < 10) {
1.136     matthew  14762:         $width = 120+$NumBars*15;
                   14763:         $xskip = 1;
                   14764:         $bar_width = 15;
                   14765:     } elsif ($NumBars <= 25) {
                   14766:         $width = 120+$NumBars*11;
                   14767:         $xskip = 5;
                   14768:         $bar_width = 8;
                   14769:     } elsif ($NumBars <= 50) {
                   14770:         $width = 120+$NumBars*8;
                   14771:         $xskip = 5;
                   14772:         $bar_width = 4;
                   14773:     } else {
                   14774:         $width = 120+$NumBars*8;
                   14775:         $xskip = 5;
                   14776:         $bar_width = 4;
                   14777:     }
                   14778:     #
1.137     matthew  14779:     $Max = 1 if ($Max < 1);
                   14780:     if ( int($Max) < $Max ) {
                   14781:         $Max++;
                   14782:         $Max = int($Max);
                   14783:     }
1.127     matthew  14784:     $Title  = '' if (! defined($Title));
                   14785:     $xlabel = '' if (! defined($xlabel));
                   14786:     $ylabel = '' if (! defined($ylabel));
1.369     www      14787:     $ValuesHash{$id.'.title'}    = &escape($Title);
                   14788:     $ValuesHash{$id.'.xlabel'}   = &escape($xlabel);
                   14789:     $ValuesHash{$id.'.ylabel'}   = &escape($ylabel);
1.137     matthew  14790:     $ValuesHash{$id.'.y_max_value'} = $Max;
1.136     matthew  14791:     $ValuesHash{$id.'.NumBars'}  = $NumBars;
                   14792:     $ValuesHash{$id.'.NumSets'}  = $NumSets;
                   14793:     $ValuesHash{$id.'.PlotType'} = 'bar';
                   14794:     $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   14795:     $ValuesHash{$id.'.height'}   = $height;
                   14796:     $ValuesHash{$id.'.width'}    = $width;
                   14797:     $ValuesHash{$id.'.xskip'}    = $xskip;
                   14798:     $ValuesHash{$id.'.bar_width'} = $bar_width;
                   14799:     $ValuesHash{$id.'.labels'} = join(',',@Labels);
1.127     matthew  14800:     #
1.228     matthew  14801:     # Deal with other parameters
                   14802:     while (my ($key,$value) = each(%$extra_settings)) {
                   14803:         $ValuesHash{$id.'.'.$key} = $value;
                   14804:     }
                   14805:     #
1.646     raeburn  14806:     &Apache::lonnet::appenv(\%ValuesHash);
1.137     matthew  14807:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   14808: }
                   14809: 
                   14810: ############################################################
                   14811: ############################################################
                   14812: 
                   14813: =pod
                   14814: 
1.648     raeburn  14815: =item * &DrawXYGraph()
1.137     matthew  14816: 
1.138     matthew  14817: Facilitates the plotting of data in an XY graph.
                   14818: Puts plot definition data into the users environment in order for 
                   14819: graph.png to plot it.  Returns an <img> tag for the plot.
                   14820: 
                   14821: Inputs:
                   14822: 
                   14823: =over 4
                   14824: 
                   14825: =item $Title: string, the title of the plot
                   14826: 
                   14827: =item $xlabel: string, text describing the X-axis of the plot
                   14828: 
                   14829: =item $ylabel: string, text describing the Y-axis of the plot
                   14830: 
                   14831: =item $Max: scalar, the maximum Y value to use in the plot
                   14832: If $Max is < any data point, the graph will not be rendered.
                   14833: 
                   14834: =item $colors: Array ref containing the hex color codes for the data to be 
                   14835: plotted in.  If undefined, default values will be used.
                   14836: 
                   14837: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   14838: 
                   14839: =item $Ydata: Array ref containing Array refs.  
1.185     www      14840: Each of the contained arrays will be plotted as a separate curve.
1.138     matthew  14841: 
                   14842: =item %Values: hash indicating or overriding any default values which are 
                   14843: passed to graph.png.  
                   14844: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   14845: 
                   14846: =back
                   14847: 
                   14848: Returns:
                   14849: 
                   14850: An <img> tag which references graph.png and the appropriate identifying
                   14851: information for the plot.
                   14852: 
1.137     matthew  14853: =cut
                   14854: 
                   14855: ############################################################
                   14856: ############################################################
                   14857: sub DrawXYGraph {
                   14858:     my ($Title,$xlabel,$ylabel,$Max,$colors,$Xlabels,$Ydata,%Values)=@_;
                   14859:     #
                   14860:     # Create the identifier for the graph
                   14861:     my $identifier = &get_cgi_id();
                   14862:     my $id = 'cgi.'.$identifier;
                   14863:     #
                   14864:     $Title  = '' if (! defined($Title));
                   14865:     $xlabel = '' if (! defined($xlabel));
                   14866:     $ylabel = '' if (! defined($ylabel));
                   14867:     my %ValuesHash = 
                   14868:         (
1.369     www      14869:          $id.'.title'  => &escape($Title),
                   14870:          $id.'.xlabel' => &escape($xlabel),
                   14871:          $id.'.ylabel' => &escape($ylabel),
1.137     matthew  14872:          $id.'.y_max_value'=> $Max,
                   14873:          $id.'.labels'     => join(',',@$Xlabels),
                   14874:          $id.'.PlotType'   => 'XY',
                   14875:          );
                   14876:     #
                   14877:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   14878:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   14879:     }
                   14880:     #
                   14881:     if (! ref($Ydata) || ref($Ydata) ne 'ARRAY') {
                   14882:         return '';
                   14883:     }
                   14884:     my $NumSets=1;
1.138     matthew  14885:     foreach my $array (@{$Ydata}){
1.137     matthew  14886:         next if (! ref($array));
                   14887:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
                   14888:     }
1.138     matthew  14889:     $ValuesHash{$id.'.NumSets'} = $NumSets-1;
1.137     matthew  14890:     #
                   14891:     # Deal with other parameters
                   14892:     while (my ($key,$value) = each(%Values)) {
                   14893:         $ValuesHash{$id.'.'.$key} = $value;
1.127     matthew  14894:     }
                   14895:     #
1.646     raeburn  14896:     &Apache::lonnet::appenv(\%ValuesHash);
1.136     matthew  14897:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   14898: }
                   14899: 
                   14900: ############################################################
                   14901: ############################################################
                   14902: 
                   14903: =pod
                   14904: 
1.648     raeburn  14905: =item * &DrawXYYGraph()
1.138     matthew  14906: 
                   14907: Facilitates the plotting of data in an XY graph with two Y axes.
                   14908: Puts plot definition data into the users environment in order for 
                   14909: graph.png to plot it.  Returns an <img> tag for the plot.
                   14910: 
                   14911: Inputs:
                   14912: 
                   14913: =over 4
                   14914: 
                   14915: =item $Title: string, the title of the plot
                   14916: 
                   14917: =item $xlabel: string, text describing the X-axis of the plot
                   14918: 
                   14919: =item $ylabel: string, text describing the Y-axis of the plot
                   14920: 
                   14921: =item $colors: Array ref containing the hex color codes for the data to be 
                   14922: plotted in.  If undefined, default values will be used.
                   14923: 
                   14924: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   14925: 
                   14926: =item $Ydata1: The first data set
                   14927: 
                   14928: =item $Min1: The minimum value of the left Y-axis
                   14929: 
                   14930: =item $Max1: The maximum value of the left Y-axis
                   14931: 
                   14932: =item $Ydata2: The second data set
                   14933: 
                   14934: =item $Min2: The minimum value of the right Y-axis
                   14935: 
                   14936: =item $Max2: The maximum value of the left Y-axis
                   14937: 
                   14938: =item %Values: hash indicating or overriding any default values which are 
                   14939: passed to graph.png.  
                   14940: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   14941: 
                   14942: =back
                   14943: 
                   14944: Returns:
                   14945: 
                   14946: An <img> tag which references graph.png and the appropriate identifying
                   14947: information for the plot.
1.136     matthew  14948: 
                   14949: =cut
                   14950: 
                   14951: ############################################################
                   14952: ############################################################
1.137     matthew  14953: sub DrawXYYGraph {
                   14954:     my ($Title,$xlabel,$ylabel,$colors,$Xlabels,$Ydata1,$Min1,$Max1,
                   14955:                                         $Ydata2,$Min2,$Max2,%Values)=@_;
1.136     matthew  14956:     #
                   14957:     # Create the identifier for the graph
                   14958:     my $identifier = &get_cgi_id();
                   14959:     my $id = 'cgi.'.$identifier;
                   14960:     #
                   14961:     $Title  = '' if (! defined($Title));
                   14962:     $xlabel = '' if (! defined($xlabel));
                   14963:     $ylabel = '' if (! defined($ylabel));
                   14964:     my %ValuesHash = 
                   14965:         (
1.369     www      14966:          $id.'.title'  => &escape($Title),
                   14967:          $id.'.xlabel' => &escape($xlabel),
                   14968:          $id.'.ylabel' => &escape($ylabel),
1.136     matthew  14969:          $id.'.labels' => join(',',@$Xlabels),
                   14970:          $id.'.PlotType' => 'XY',
                   14971:          $id.'.NumSets' => 2,
1.137     matthew  14972:          $id.'.two_axes' => 1,
                   14973:          $id.'.y1_max_value' => $Max1,
                   14974:          $id.'.y1_min_value' => $Min1,
                   14975:          $id.'.y2_max_value' => $Max2,
                   14976:          $id.'.y2_min_value' => $Min2,
1.136     matthew  14977:          );
                   14978:     #
1.137     matthew  14979:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   14980:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   14981:     }
                   14982:     #
                   14983:     if (! ref($Ydata1) || ref($Ydata1) ne 'ARRAY' ||
                   14984:         ! ref($Ydata2) || ref($Ydata2) ne 'ARRAY'){
1.136     matthew  14985:         return '';
                   14986:     }
                   14987:     my $NumSets=1;
1.137     matthew  14988:     foreach my $array ($Ydata1,$Ydata2){
1.136     matthew  14989:         next if (! ref($array));
                   14990:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
1.137     matthew  14991:     }
                   14992:     #
                   14993:     # Deal with other parameters
                   14994:     while (my ($key,$value) = each(%Values)) {
                   14995:         $ValuesHash{$id.'.'.$key} = $value;
1.136     matthew  14996:     }
                   14997:     #
1.646     raeburn  14998:     &Apache::lonnet::appenv(\%ValuesHash);
1.130     albertel 14999:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
1.139     matthew  15000: }
                   15001: 
                   15002: ############################################################
                   15003: ############################################################
                   15004: 
                   15005: =pod
                   15006: 
1.157     matthew  15007: =back 
                   15008: 
1.139     matthew  15009: =head1 Statistics helper routines?  
                   15010: 
                   15011: Bad place for them but what the hell.
                   15012: 
1.157     matthew  15013: =over 4
                   15014: 
1.648     raeburn  15015: =item * &chartlink()
1.139     matthew  15016: 
                   15017: Returns a link to the chart for a specific student.  
                   15018: 
                   15019: Inputs:
                   15020: 
                   15021: =over 4
                   15022: 
                   15023: =item $linktext: The text of the link
                   15024: 
                   15025: =item $sname: The students username
                   15026: 
                   15027: =item $sdomain: The students domain
                   15028: 
                   15029: =back
                   15030: 
1.157     matthew  15031: =back
                   15032: 
1.139     matthew  15033: =cut
                   15034: 
                   15035: ############################################################
                   15036: ############################################################
                   15037: sub chartlink {
                   15038:     my ($linktext, $sname, $sdomain) = @_;
                   15039:     my $link = '<a href="/adm/statistics?reportSelected=student_assessment'.
1.369     www      15040:         '&amp;SelectedStudent='.&escape($sname.':'.$sdomain).
1.219     albertel 15041:         '&amp;chartoutputmode='.HTML::Entities::encode('html, with all links').
1.139     matthew  15042:        '">'.$linktext.'</a>';
1.153     matthew  15043: }
                   15044: 
                   15045: #######################################################
                   15046: #######################################################
                   15047: 
                   15048: =pod
                   15049: 
                   15050: =head1 Course Environment Routines
1.157     matthew  15051: 
                   15052: =over 4
1.153     matthew  15053: 
1.648     raeburn  15054: =item * &restore_course_settings()
1.153     matthew  15055: 
1.648     raeburn  15056: =item * &store_course_settings()
1.153     matthew  15057: 
                   15058: Restores/Store indicated form parameters from the course environment.
                   15059: Will not overwrite existing values of the form parameters.
                   15060: 
                   15061: Inputs: 
                   15062: a scalar describing the data (e.g. 'chart', 'problem_analysis')
                   15063: 
                   15064: a hash ref describing the data to be stored.  For example:
                   15065:    
                   15066: %Save_Parameters = ('Status' => 'scalar',
                   15067:     'chartoutputmode' => 'scalar',
                   15068:     'chartoutputdata' => 'scalar',
                   15069:     'Section' => 'array',
1.373     raeburn  15070:     'Group' => 'array',
1.153     matthew  15071:     'StudentData' => 'array',
                   15072:     'Maps' => 'array');
                   15073: 
                   15074: Returns: both routines return nothing
                   15075: 
1.631     raeburn  15076: =back
                   15077: 
1.153     matthew  15078: =cut
                   15079: 
                   15080: #######################################################
                   15081: #######################################################
                   15082: sub store_course_settings {
1.496     albertel 15083:     return &store_settings($env{'request.course.id'},@_);
                   15084: }
                   15085: 
                   15086: sub store_settings {
1.153     matthew  15087:     # save to the environment
                   15088:     # appenv the same items, just to be safe
1.300     albertel 15089:     my $udom  = $env{'user.domain'};
                   15090:     my $uname = $env{'user.name'};
1.496     albertel 15091:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  15092:     my %SaveHash;
                   15093:     my %AppHash;
                   15094:     while (my ($setting,$type) = each(%$Settings)) {
1.496     albertel 15095:         my $basename = join('.','internal',$context,$prefix,$setting);
1.300     albertel 15096:         my $envname = 'environment.'.$basename;
1.258     albertel 15097:         if (exists($env{'form.'.$setting})) {
1.153     matthew  15098:             # Save this value away
                   15099:             if ($type eq 'scalar' &&
1.258     albertel 15100:                 (! exists($env{$envname}) || 
                   15101:                  $env{$envname} ne $env{'form.'.$setting})) {
                   15102:                 $SaveHash{$basename} = $env{'form.'.$setting};
                   15103:                 $AppHash{$envname}   = $env{'form.'.$setting};
1.153     matthew  15104:             } elsif ($type eq 'array') {
                   15105:                 my $stored_form;
1.258     albertel 15106:                 if (ref($env{'form.'.$setting})) {
1.153     matthew  15107:                     $stored_form = join(',',
                   15108:                                         map {
1.369     www      15109:                                             &escape($_);
1.258     albertel 15110:                                         } sort(@{$env{'form.'.$setting}}));
1.153     matthew  15111:                 } else {
                   15112:                     $stored_form = 
1.369     www      15113:                         &escape($env{'form.'.$setting});
1.153     matthew  15114:                 }
                   15115:                 # Determine if the array contents are the same.
1.258     albertel 15116:                 if ($stored_form ne $env{$envname}) {
1.153     matthew  15117:                     $SaveHash{$basename} = $stored_form;
                   15118:                     $AppHash{$envname}   = $stored_form;
                   15119:                 }
                   15120:             }
                   15121:         }
                   15122:     }
                   15123:     my $put_result = &Apache::lonnet::put('environment',\%SaveHash,
1.300     albertel 15124:                                           $udom,$uname);
1.153     matthew  15125:     if ($put_result !~ /^(ok|delayed)/) {
                   15126:         &Apache::lonnet::logthis('unable to save form parameters, '.
                   15127:                                  'got error:'.$put_result);
                   15128:     }
                   15129:     # Make sure these settings stick around in this session, too
1.646     raeburn  15130:     &Apache::lonnet::appenv(\%AppHash);
1.153     matthew  15131:     return;
                   15132: }
                   15133: 
                   15134: sub restore_course_settings {
1.499     albertel 15135:     return &restore_settings($env{'request.course.id'},@_);
1.496     albertel 15136: }
                   15137: 
                   15138: sub restore_settings {
                   15139:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  15140:     while (my ($setting,$type) = each(%$Settings)) {
1.258     albertel 15141:         next if (exists($env{'form.'.$setting}));
1.496     albertel 15142:         my $envname = 'environment.internal.'.$context.'.'.$prefix.
1.153     matthew  15143:             '.'.$setting;
1.258     albertel 15144:         if (exists($env{$envname})) {
1.153     matthew  15145:             if ($type eq 'scalar') {
1.258     albertel 15146:                 $env{'form.'.$setting} = $env{$envname};
1.153     matthew  15147:             } elsif ($type eq 'array') {
1.258     albertel 15148:                 $env{'form.'.$setting} = [ 
1.153     matthew  15149:                                            map { 
1.369     www      15150:                                                &unescape($_); 
1.258     albertel 15151:                                            } split(',',$env{$envname})
1.153     matthew  15152:                                            ];
                   15153:             }
                   15154:         }
                   15155:     }
1.127     matthew  15156: }
                   15157: 
1.618     raeburn  15158: #######################################################
                   15159: #######################################################
                   15160: 
                   15161: =pod
                   15162: 
                   15163: =head1 Domain E-mail Routines  
                   15164: 
                   15165: =over 4
                   15166: 
1.648     raeburn  15167: =item * &build_recipient_list()
1.618     raeburn  15168: 
1.1075.2.44  raeburn  15169: Build recipient lists for following types of e-mail:
1.766     raeburn  15170: (a) Error Reports, (b) Package Updates, (c) lonstatus warnings/errors
1.1075.2.44  raeburn  15171: (d) Help requests, (e) Course requests needing approval, (f) loncapa
                   15172: module change checking, student/employee ID conflict checks, as
                   15173: generated by lonerrorhandler.pm, CHECKRPMS, loncron,
                   15174: lonsupportreq.pm, loncoursequeueadmin.pm, searchcat.pl respectively.
1.618     raeburn  15175: 
                   15176: Inputs:
1.1075.2.44  raeburn  15177: defmail (scalar - email address of default recipient),
                   15178: mailing type (scalar: errormail, packagesmail, helpdeskmail,
                   15179: requestsmail, updatesmail, or idconflictsmail).
                   15180: 
1.619     raeburn  15181: defdom (domain for which to retrieve configuration settings),
1.1075.2.44  raeburn  15182: 
                   15183: origmail (scalar - email address of recipient from loncapa.conf,
                   15184: i.e., predates configuration by DC via domainprefs.pm
1.618     raeburn  15185: 
1.1075.2.139  raeburn  15186: $requname username of requester (if mailing type is helpdeskmail)
                   15187: 
                   15188: $requdom domain of requester (if mailing type is helpdeskmail)
                   15189: 
                   15190: $reqemail e-mail address of requester (if mailing type is helpdeskmail)
                   15191: 
1.655     raeburn  15192: Returns: comma separated list of addresses to which to send e-mail.
                   15193: 
                   15194: =back
1.618     raeburn  15195: 
                   15196: =cut
                   15197: 
                   15198: ############################################################
                   15199: ############################################################
                   15200: sub build_recipient_list {
1.1075.2.139  raeburn  15201:     my ($defmail,$mailing,$defdom,$origmail,$requname,$requdom,$reqemail) = @_;
1.618     raeburn  15202:     my @recipients;
1.1075.2.122  raeburn  15203:     my ($otheremails,$lastresort,$allbcc,$addtext);
1.618     raeburn  15204:     my %domconfig =
1.1075.2.122  raeburn  15205:         &Apache::lonnet::get_dom('configuration',['contacts'],$defdom);
1.618     raeburn  15206:     if (ref($domconfig{'contacts'}) eq 'HASH') {
1.766     raeburn  15207:         if (exists($domconfig{'contacts'}{$mailing})) {
                   15208:             if (ref($domconfig{'contacts'}{$mailing}) eq 'HASH') {
                   15209:                 my @contacts = ('adminemail','supportemail');
                   15210:                 foreach my $item (@contacts) {
                   15211:                     if ($domconfig{'contacts'}{$mailing}{$item}) {
                   15212:                         my $addr = $domconfig{'contacts'}{$item}; 
                   15213:                         if (!grep(/^\Q$addr\E$/,@recipients)) {
                   15214:                             push(@recipients,$addr);
                   15215:                         }
1.619     raeburn  15216:                     }
1.1075.2.122  raeburn  15217:                 }
                   15218:                 $otheremails = $domconfig{'contacts'}{$mailing}{'others'};
                   15219:                 if ($mailing eq 'helpdeskmail') {
                   15220:                     if ($domconfig{'contacts'}{$mailing}{'bcc'}) {
                   15221:                         my @bccs = split(/,/,$domconfig{'contacts'}{$mailing}{'bcc'});
                   15222:                         my @ok_bccs;
                   15223:                         foreach my $bcc (@bccs) {
                   15224:                             $bcc =~ s/^\s+//g;
                   15225:                             $bcc =~ s/\s+$//g;
                   15226:                             if ($bcc =~ m/^[^\@]+\@[^\@]+$/) {
                   15227:                                 if (!(grep(/^\Q$bcc\E$/,@ok_bccs))) {
                   15228:                                     push(@ok_bccs,$bcc);
                   15229:                                 }
                   15230:                             }
                   15231:                         }
                   15232:                         if (@ok_bccs > 0) {
                   15233:                             $allbcc = join(', ',@ok_bccs);
                   15234:                         }
                   15235:                     }
                   15236:                     $addtext = $domconfig{'contacts'}{$mailing}{'include'};
1.618     raeburn  15237:                 }
                   15238:             }
1.766     raeburn  15239:         } elsif ($origmail ne '') {
1.1075.2.122  raeburn  15240:             $lastresort = $origmail;
1.618     raeburn  15241:         }
1.1075.2.139  raeburn  15242:         if ($mailing eq 'helpdeskmail') {
                   15243:             if ((ref($domconfig{'contacts'}{'overrides'}) eq 'HASH') &&
                   15244:                 (keys(%{$domconfig{'contacts'}{'overrides'}}))) {
                   15245:                 my ($inststatus,$inststatus_checked);
                   15246:                 if (($env{'user.name'} ne '') && ($env{'user.domain'} ne '') &&
                   15247:                     ($env{'user.domain'} ne 'public')) {
                   15248:                     $inststatus_checked = 1;
                   15249:                     $inststatus = $env{'environment.inststatus'};
                   15250:                 }
                   15251:                 unless ($inststatus_checked) {
                   15252:                     if (($requname ne '') && ($requdom ne '')) {
                   15253:                         if (($requname =~ /^$match_username$/) &&
                   15254:                             ($requdom =~ /^$match_domain$/) &&
                   15255:                             (&Apache::lonnet::domain($requdom))) {
                   15256:                             my $requhome = &Apache::lonnet::homeserver($requname,
                   15257:                                                                       $requdom);
                   15258:                             unless ($requhome eq 'no_host') {
                   15259:                                 my %userenv = &Apache::lonnet::userenvironment($requdom,$requname,'inststatus');
                   15260:                                 $inststatus = $userenv{'inststatus'};
                   15261:                                 $inststatus_checked = 1;
                   15262:                             }
                   15263:                         }
                   15264:                     }
                   15265:                 }
                   15266:                 unless ($inststatus_checked) {
                   15267:                     if ($reqemail =~ /^[^\@]+\@[^\@]+$/) {
                   15268:                         my %srch = (srchby     => 'email',
                   15269:                                     srchdomain => $defdom,
                   15270:                                     srchterm   => $reqemail,
                   15271:                                     srchtype   => 'exact');
                   15272:                         my %srch_results = &Apache::lonnet::usersearch(\%srch);
                   15273:                         foreach my $uname (keys(%srch_results)) {
                   15274:                             if (ref($srch_results{$uname}{'inststatus'}) eq 'ARRAY') {
                   15275:                                 $inststatus = join(',',@{$srch_results{$uname}{'inststatus'}});
                   15276:                                 $inststatus_checked = 1;
                   15277:                                 last;
                   15278:                             }
                   15279:                         }
                   15280:                         unless ($inststatus_checked) {
                   15281:                             my ($dirsrchres,%srch_results) = &Apache::lonnet::inst_directory_query(\%srch);
                   15282:                             if ($dirsrchres eq 'ok') {
                   15283:                                 foreach my $uname (keys(%srch_results)) {
                   15284:                                     if (ref($srch_results{$uname}{'inststatus'}) eq 'ARRAY') {
                   15285:                                         $inststatus = join(',',@{$srch_results{$uname}{'inststatus'}});
                   15286:                                         $inststatus_checked = 1;
                   15287:                                         last;
                   15288:                                     }
                   15289:                                 }
                   15290:                             }
                   15291:                         }
                   15292:                     }
                   15293:                 }
                   15294:                 if ($inststatus ne '') {
                   15295:                     foreach my $status (split(/\:/,$inststatus)) {
                   15296:                         if (ref($domconfig{'contacts'}{'overrides'}{$status}) eq 'HASH') {
                   15297:                             my @contacts = ('adminemail','supportemail');
                   15298:                             foreach my $item (@contacts) {
                   15299:                                 if ($domconfig{'contacts'}{'overrides'}{$status}{$item}) {
                   15300:                                     my $addr = $domconfig{'contacts'}{'overrides'}{$status};
                   15301:                                     if (!grep(/^\Q$addr\E$/,@recipients)) {
                   15302:                                         push(@recipients,$addr);
                   15303:                                     }
                   15304:                                 }
                   15305:                             }
                   15306:                             $otheremails = $domconfig{'contacts'}{'overrides'}{$status}{'others'};
                   15307:                             if ($domconfig{'contacts'}{'overrides'}{$status}{'bcc'}) {
                   15308:                                 my @bccs = split(/,/,$domconfig{'contacts'}{'overrides'}{$status}{'bcc'});
                   15309:                                 my @ok_bccs;
                   15310:                                 foreach my $bcc (@bccs) {
                   15311:                                     $bcc =~ s/^\s+//g;
                   15312:                                     $bcc =~ s/\s+$//g;
                   15313:                                     if ($bcc =~ m/^[^\@]+\@[^\@]+$/) {
                   15314:                                         if (!(grep(/^\Q$bcc\E$/,@ok_bccs))) {
                   15315:                                             push(@ok_bccs,$bcc);
                   15316:                                         }
                   15317:                                     }
                   15318:                                 }
                   15319:                                 if (@ok_bccs > 0) {
                   15320:                                     $allbcc = join(', ',@ok_bccs);
                   15321:                                 }
                   15322:                             }
                   15323:                             $addtext = $domconfig{'contacts'}{'overrides'}{$status}{'include'};
                   15324:                             last;
                   15325:                         }
                   15326:                     }
                   15327:                 }
                   15328:             }
                   15329:         }
1.619     raeburn  15330:     } elsif ($origmail ne '') {
1.1075.2.122  raeburn  15331:         $lastresort = $origmail;
                   15332:     }
1.1075.2.128  raeburn  15333:     if (($mailing eq 'helpdeskmail') && ($lastresort ne '')) {
1.1075.2.122  raeburn  15334:         unless (grep(/^\Q$defdom\E$/,&Apache::lonnet::current_machine_domains())) {
                   15335:             my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
                   15336:             my $machinedom = $Apache::lonnet::perlvar{'lonDefDomain'};
                   15337:             my %what = (
                   15338:                           perlvar => 1,
                   15339:                        );
                   15340:             my $primary = &Apache::lonnet::domain($defdom,'primary');
                   15341:             if ($primary) {
                   15342:                 my $gotaddr;
                   15343:                 my ($result,$returnhash) =
                   15344:                     &Apache::lonnet::get_remote_globals($primary,{ perlvar => 1 });
                   15345:                 if (($result eq 'ok') && (ref($returnhash) eq 'HASH')) {
                   15346:                     if ($returnhash->{'lonSupportEMail'} =~ /^[^\@]+\@[^\@]+$/) {
                   15347:                         $lastresort = $returnhash->{'lonSupportEMail'};
                   15348:                         $gotaddr = 1;
                   15349:                     }
                   15350:                 }
                   15351:                 unless ($gotaddr) {
                   15352:                     my $uintdom = &Apache::lonnet::internet_dom($primary);
                   15353:                     my $intdom = &Apache::lonnet::internet_dom($lonhost);
                   15354:                     unless ($uintdom eq $intdom) {
                   15355:                         my %domconfig =
                   15356:                             &Apache::lonnet::get_dom('configuration',['contacts'],$machinedom);
                   15357:                         if (ref($domconfig{'contacts'}) eq 'HASH') {
                   15358:                             if (ref($domconfig{'contacts'}{'otherdomsmail'}) eq 'HASH') {
                   15359:                                 my @contacts = ('adminemail','supportemail');
                   15360:                                 foreach my $item (@contacts) {
                   15361:                                     if ($domconfig{'contacts'}{'otherdomsmail'}{$item}) {
                   15362:                                         my $addr = $domconfig{'contacts'}{$item};
                   15363:                                         if (!grep(/^\Q$addr\E$/,@recipients)) {
                   15364:                                             push(@recipients,$addr);
                   15365:                                         }
                   15366:                                     }
                   15367:                                 }
                   15368:                                 if ($domconfig{'contacts'}{'otherdomsmail'}{'others'}) {
                   15369:                                     $otheremails = $domconfig{'contacts'}{'otherdomsmail'}{'others'};
                   15370:                                 }
                   15371:                                 if ($domconfig{'contacts'}{'otherdomsmail'}{'bcc'}) {
                   15372:                                     my @bccs = split(/,/,$domconfig{'contacts'}{'otherdomsmail'}{'bcc'});
                   15373:                                     my @ok_bccs;
                   15374:                                     foreach my $bcc (@bccs) {
                   15375:                                         $bcc =~ s/^\s+//g;
                   15376:                                         $bcc =~ s/\s+$//g;
                   15377:                                         if ($bcc =~ m/^[^\@]+\@[^\@]+$/) {
                   15378:                                             if (!(grep(/^\Q$bcc\E$/,@ok_bccs))) {
                   15379:                                                 push(@ok_bccs,$bcc);
                   15380:                                             }
                   15381:                                         }
                   15382:                                     }
                   15383:                                     if (@ok_bccs > 0) {
                   15384:                                         $allbcc = join(', ',@ok_bccs);
                   15385:                                     }
                   15386:                                 }
                   15387:                                 $addtext = $domconfig{'contacts'}{'otherdomsmail'}{'include'};
                   15388:                             }
                   15389:                         }
                   15390:                     }
                   15391:                 }
                   15392:             }
                   15393:         }
1.618     raeburn  15394:     }
1.688     raeburn  15395:     if (defined($defmail)) {
                   15396:         if ($defmail ne '') {
                   15397:             push(@recipients,$defmail);
                   15398:         }
1.618     raeburn  15399:     }
                   15400:     if ($otheremails) {
1.619     raeburn  15401:         my @others;
                   15402:         if ($otheremails =~ /,/) {
                   15403:             @others = split(/,/,$otheremails);
1.618     raeburn  15404:         } else {
1.619     raeburn  15405:             push(@others,$otheremails);
                   15406:         }
                   15407:         foreach my $addr (@others) {
                   15408:             if (!grep(/^\Q$addr\E$/,@recipients)) {
                   15409:                 push(@recipients,$addr);
                   15410:             }
1.618     raeburn  15411:         }
                   15412:     }
1.1075.2.128  raeburn  15413:     if ($mailing eq 'helpdeskmail') {
1.1075.2.122  raeburn  15414:         if ((!@recipients) && ($lastresort ne '')) {
                   15415:             push(@recipients,$lastresort);
                   15416:         }
                   15417:     } elsif ($lastresort ne '') {
                   15418:         if (!grep(/^\Q$lastresort\E$/,@recipients)) {
                   15419:             push(@recipients,$lastresort);
                   15420:         }
                   15421:     }
                   15422:     my $recipientlist = join(',',@recipients);
                   15423:     if (wantarray) {
                   15424:         return ($recipientlist,$allbcc,$addtext);
                   15425:     } else {
                   15426:         return $recipientlist;
                   15427:     }
1.618     raeburn  15428: }
                   15429: 
1.127     matthew  15430: ############################################################
                   15431: ############################################################
1.154     albertel 15432: 
1.655     raeburn  15433: =pod
                   15434: 
                   15435: =head1 Course Catalog Routines
                   15436: 
                   15437: =over 4
                   15438: 
                   15439: =item * &gather_categories()
                   15440: 
                   15441: Converts category definitions - keys of categories hash stored in  
                   15442: coursecategories in configuration.db on the primary library server in a 
                   15443: domain - to an array.  Also generates javascript and idx hash used to 
                   15444: generate Domain Coordinator interface for editing Course Categories.
                   15445: 
                   15446: Inputs:
1.663     raeburn  15447: 
1.655     raeburn  15448: categories (reference to hash of category definitions).
1.663     raeburn  15449: 
1.655     raeburn  15450: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   15451:       categories and subcategories).
1.663     raeburn  15452: 
1.655     raeburn  15453: idx (reference to hash of counters used in Domain Coordinator interface for 
                   15454:       editing Course Categories).
1.663     raeburn  15455: 
1.655     raeburn  15456: jsarray (reference to array of categories used to create Javascript arrays for
                   15457:          Domain Coordinator interface for editing Course Categories).
                   15458: 
                   15459: Returns: nothing
                   15460: 
                   15461: Side effects: populates cats, idx and jsarray. 
                   15462: 
                   15463: =cut
                   15464: 
                   15465: sub gather_categories {
                   15466:     my ($categories,$cats,$idx,$jsarray) = @_;
                   15467:     my %counters;
                   15468:     my $num = 0;
                   15469:     foreach my $item (keys(%{$categories})) {
                   15470:         my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item);
                   15471:         if ($container eq '' && $depth == 0) {
                   15472:             $cats->[$depth][$categories->{$item}] = $cat;
                   15473:         } else {
                   15474:             $cats->[$depth]{$container}[$categories->{$item}] = $cat;
                   15475:         }
                   15476:         my ($escitem,$tail) = split(/:/,$item,2);
                   15477:         if ($counters{$tail} eq '') {
                   15478:             $counters{$tail} = $num;
                   15479:             $num ++;
                   15480:         }
                   15481:         if (ref($idx) eq 'HASH') {
                   15482:             $idx->{$item} = $counters{$tail};
                   15483:         }
                   15484:         if (ref($jsarray) eq 'ARRAY') {
                   15485:             push(@{$jsarray->[$counters{$tail}]},$item);
                   15486:         }
                   15487:     }
                   15488:     return;
                   15489: }
                   15490: 
                   15491: =pod
                   15492: 
                   15493: =item * &extract_categories()
                   15494: 
                   15495: Used to generate breadcrumb trails for course categories.
                   15496: 
                   15497: Inputs:
1.663     raeburn  15498: 
1.655     raeburn  15499: categories (reference to hash of category definitions).
1.663     raeburn  15500: 
1.655     raeburn  15501: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   15502:       categories and subcategories).
1.663     raeburn  15503: 
1.655     raeburn  15504: trails (reference to array of breacrumb trails for each category).
1.663     raeburn  15505: 
1.655     raeburn  15506: allitems (reference to hash - key is category key 
                   15507:          (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663     raeburn  15508: 
1.655     raeburn  15509: idx (reference to hash of counters used in Domain Coordinator interface for
                   15510:       editing Course Categories).
1.663     raeburn  15511: 
1.655     raeburn  15512: jsarray (reference to array of categories used to create Javascript arrays for
                   15513:          Domain Coordinator interface for editing Course Categories).
                   15514: 
1.665     raeburn  15515: subcats (reference to hash of arrays containing all subcategories within each 
                   15516:          category, -recursive)
                   15517: 
1.1075.2.132  raeburn  15518: maxd (reference to hash used to hold max depth for all top-level categories).
                   15519: 
1.655     raeburn  15520: Returns: nothing
                   15521: 
                   15522: Side effects: populates trails and allitems hash references.
                   15523: 
                   15524: =cut
                   15525: 
                   15526: sub extract_categories {
1.1075.2.132  raeburn  15527:     my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats,$maxd) = @_;
1.655     raeburn  15528:     if (ref($categories) eq 'HASH') {
                   15529:         &gather_categories($categories,$cats,$idx,$jsarray);
                   15530:         if (ref($cats->[0]) eq 'ARRAY') {
                   15531:             for (my $i=0; $i<@{$cats->[0]}; $i++) {
                   15532:                 my $name = $cats->[0][$i];
                   15533:                 my $item = &escape($name).'::0';
                   15534:                 my $trailstr;
                   15535:                 if ($name eq 'instcode') {
                   15536:                     $trailstr = &mt('Official courses (with institutional codes)');
1.919     raeburn  15537:                 } elsif ($name eq 'communities') {
                   15538:                     $trailstr = &mt('Communities');
1.655     raeburn  15539:                 } else {
                   15540:                     $trailstr = $name;
                   15541:                 }
                   15542:                 if ($allitems->{$item} eq '') {
                   15543:                     push(@{$trails},$trailstr);
                   15544:                     $allitems->{$item} = scalar(@{$trails})-1;
                   15545:                 }
                   15546:                 my @parents = ($name);
                   15547:                 if (ref($cats->[1]{$name}) eq 'ARRAY') {
                   15548:                     for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) {
                   15549:                         my $category = $cats->[1]{$name}[$j];
1.665     raeburn  15550:                         if (ref($subcats) eq 'HASH') {
                   15551:                             push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1');
                   15552:                         }
1.1075.2.132  raeburn  15553:                         &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats,$maxd);
1.665     raeburn  15554:                     }
                   15555:                 } else {
                   15556:                     if (ref($subcats) eq 'HASH') {
                   15557:                         $subcats->{$item} = [];
1.655     raeburn  15558:                     }
1.1075.2.132  raeburn  15559:                     if (ref($maxd) eq 'HASH') {
                   15560:                         $maxd->{$name} = 1;
                   15561:                     }
1.655     raeburn  15562:                 }
                   15563:             }
                   15564:         }
                   15565:     }
                   15566:     return;
                   15567: }
                   15568: 
                   15569: =pod
                   15570: 
1.1075.2.56  raeburn  15571: =item * &recurse_categories()
1.655     raeburn  15572: 
                   15573: Recursively used to generate breadcrumb trails for course categories.
                   15574: 
                   15575: Inputs:
1.663     raeburn  15576: 
1.655     raeburn  15577: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   15578:       categories and subcategories).
1.663     raeburn  15579: 
1.655     raeburn  15580: depth (current depth in hierarchy of categories and sub-categories - 0 indexed).
1.663     raeburn  15581: 
                   15582: category (current course category, for which breadcrumb trail is being generated).
                   15583: 
                   15584: trails (reference to array of breadcrumb trails for each category).
                   15585: 
1.655     raeburn  15586: allitems (reference to hash - key is category key
                   15587:          (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663     raeburn  15588: 
1.655     raeburn  15589: parents (array containing containers directories for current category, 
                   15590:          back to top level). 
                   15591: 
                   15592: Returns: nothing
                   15593: 
                   15594: Side effects: populates trails and allitems hash references
                   15595: 
                   15596: =cut
                   15597: 
                   15598: sub recurse_categories {
1.1075.2.132  raeburn  15599:     my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats,$maxd) = @_;
1.655     raeburn  15600:     my $shallower = $depth - 1;
                   15601:     if (ref($cats->[$depth]{$category}) eq 'ARRAY') {
                   15602:         for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
                   15603:             my $name = $cats->[$depth]{$category}[$k];
                   15604:             my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
1.1075.2.161.  .4(raebu 15605:22):             my $trailstr = join(' &raquo; ',(@{$parents},$category));
1.655     raeburn  15606:             if ($allitems->{$item} eq '') {
                   15607:                 push(@{$trails},$trailstr);
                   15608:                 $allitems->{$item} = scalar(@{$trails})-1;
                   15609:             }
                   15610:             my $deeper = $depth+1;
                   15611:             push(@{$parents},$category);
1.665     raeburn  15612:             if (ref($subcats) eq 'HASH') {
                   15613:                 my $subcat = &escape($name).':'.$category.':'.$depth;
                   15614:                 for (my $j=@{$parents}; $j>=0; $j--) {
                   15615:                     my $higher;
                   15616:                     if ($j > 0) {
                   15617:                         $higher = &escape($parents->[$j]).':'.
                   15618:                                   &escape($parents->[$j-1]).':'.$j;
                   15619:                     } else {
                   15620:                         $higher = &escape($parents->[$j]).'::'.$j;
                   15621:                     }
                   15622:                     push(@{$subcats->{$higher}},$subcat);
                   15623:                 }
                   15624:             }
                   15625:             &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents,
1.1075.2.132  raeburn  15626:                                 $subcats,$maxd);
1.655     raeburn  15627:             pop(@{$parents});
                   15628:         }
                   15629:     } else {
                   15630:         my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
1.1075.2.132  raeburn  15631:         my $trailstr = join(' &raquo; ',(@{$parents},$category));
1.655     raeburn  15632:         if ($allitems->{$item} eq '') {
                   15633:             push(@{$trails},$trailstr);
                   15634:             $allitems->{$item} = scalar(@{$trails})-1;
                   15635:         }
1.1075.2.132  raeburn  15636:         if (ref($maxd) eq 'HASH') {
                   15637:             if ($depth > $maxd->{$parents->[0]}) {
                   15638:                 $maxd->{$parents->[0]} = $depth;
                   15639:             }
                   15640:         }
1.655     raeburn  15641:     }
                   15642:     return;
                   15643: }
                   15644: 
1.663     raeburn  15645: =pod
                   15646: 
1.1075.2.56  raeburn  15647: =item * &assign_categories_table()
1.663     raeburn  15648: 
                   15649: Create a datatable for display of hierarchical categories in a domain,
                   15650: with checkboxes to allow a course to be categorized. 
                   15651: 
                   15652: Inputs:
                   15653: 
                   15654: cathash - reference to hash of categories defined for the domain (from
                   15655:           configuration.db)
                   15656: 
                   15657: currcat - scalar with an & separated list of categories assigned to a course. 
                   15658: 
1.919     raeburn  15659: type    - scalar contains course type (Course or Community).
                   15660: 
1.1075.2.117  raeburn  15661: disabled - scalar (optional) contains disabled="disabled" if input elements are
                   15662:            to be readonly (e.g., Domain Helpdesk role viewing course settings).
                   15663: 
1.663     raeburn  15664: Returns: $output (markup to be displayed) 
                   15665: 
                   15666: =cut
                   15667: 
                   15668: sub assign_categories_table {
1.1075.2.117  raeburn  15669:     my ($cathash,$currcat,$type,$disabled) = @_;
1.663     raeburn  15670:     my $output;
                   15671:     if (ref($cathash) eq 'HASH') {
1.1075.2.132  raeburn  15672:         my (@cats,@trails,%allitems,%idx,@jsarray,%maxd,@path,$maxdepth);
                   15673:         &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray,\%maxd);
1.663     raeburn  15674:         $maxdepth = scalar(@cats);
                   15675:         if (@cats > 0) {
                   15676:             my $itemcount = 0;
                   15677:             if (ref($cats[0]) eq 'ARRAY') {
                   15678:                 my @currcategories;
                   15679:                 if ($currcat ne '') {
                   15680:                     @currcategories = split('&',$currcat);
                   15681:                 }
1.919     raeburn  15682:                 my $table;
1.663     raeburn  15683:                 for (my $i=0; $i<@{$cats[0]}; $i++) {
                   15684:                     my $parent = $cats[0][$i];
1.919     raeburn  15685:                     next if ($parent eq 'instcode');
                   15686:                     if ($type eq 'Community') {
                   15687:                         next unless ($parent eq 'communities');
                   15688:                     } else {
                   15689:                         next if ($parent eq 'communities');
                   15690:                     }
1.663     raeburn  15691:                     my $css_class = $itemcount%2?' class="LC_odd_row"':'';
                   15692:                     my $item = &escape($parent).'::0';
                   15693:                     my $checked = '';
                   15694:                     if (@currcategories > 0) {
                   15695:                         if (grep(/^\Q$item\E$/,@currcategories)) {
1.772     bisitz   15696:                             $checked = ' checked="checked"';
1.663     raeburn  15697:                         }
                   15698:                     }
1.919     raeburn  15699:                     my $parent_title = $parent;
                   15700:                     if ($parent eq 'communities') {
                   15701:                         $parent_title = &mt('Communities');
                   15702:                     }
                   15703:                     $table .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.
                   15704:                               '<input type="checkbox" name="usecategory" value="'.
1.1075.2.117  raeburn  15705:                               $item.'"'.$checked.$disabled.' />'.$parent_title.'</span>'.
1.919     raeburn  15706:                               '<input type="hidden" name="catname" value="'.$parent.'" /></td>';
1.663     raeburn  15707:                     my $depth = 1;
                   15708:                     push(@path,$parent);
1.1075.2.117  raeburn  15709:                     $table .= &assign_category_rows($itemcount,\@cats,$depth,$parent,\@path,\@currcategories,$disabled);
1.663     raeburn  15710:                     pop(@path);
1.919     raeburn  15711:                     $table .= '</tr><tr><td colspan="'.$maxdepth.'" class="LC_row_separator"></td></tr>';
1.663     raeburn  15712:                     $itemcount ++;
                   15713:                 }
1.919     raeburn  15714:                 if ($itemcount) {
                   15715:                     $output = &Apache::loncommon::start_data_table().
                   15716:                               $table.
                   15717:                               &Apache::loncommon::end_data_table();
                   15718:                 }
1.663     raeburn  15719:             }
                   15720:         }
                   15721:     }
                   15722:     return $output;
                   15723: }
                   15724: 
                   15725: =pod
                   15726: 
1.1075.2.56  raeburn  15727: =item * &assign_category_rows()
1.663     raeburn  15728: 
                   15729: Create a datatable row for display of nested categories in a domain,
                   15730: with checkboxes to allow a course to be categorized,called recursively.
                   15731: 
                   15732: Inputs:
                   15733: 
                   15734: itemcount - track row number for alternating colors
                   15735: 
                   15736: cats - reference to array of arrays/hashes which encapsulates hierarchy of
                   15737:       categories and subcategories.
                   15738: 
                   15739: depth - current depth in hierarchy of categories and sub-categories - 0 indexed.
                   15740: 
                   15741: parent - parent of current category item
                   15742: 
                   15743: path - Array containing all categories back up through the hierarchy from the
                   15744:        current category to the top level.
                   15745: 
                   15746: currcategories - reference to array of current categories assigned to the course
                   15747: 
1.1075.2.117  raeburn  15748: disabled - scalar (optional) contains disabled="disabled" if input elements are
                   15749:            to be readonly (e.g., Domain Helpdesk role viewing course settings).
                   15750: 
1.663     raeburn  15751: Returns: $output (markup to be displayed).
                   15752: 
                   15753: =cut
                   15754: 
                   15755: sub assign_category_rows {
1.1075.2.117  raeburn  15756:     my ($itemcount,$cats,$depth,$parent,$path,$currcategories,$disabled) = @_;
1.663     raeburn  15757:     my ($text,$name,$item,$chgstr);
                   15758:     if (ref($cats) eq 'ARRAY') {
                   15759:         my $maxdepth = scalar(@{$cats});
                   15760:         if (ref($cats->[$depth]) eq 'HASH') {
                   15761:             if (ref($cats->[$depth]{$parent}) eq 'ARRAY') {
                   15762:                 my $numchildren = @{$cats->[$depth]{$parent}};
                   15763:                 my $css_class = $itemcount%2?' class="LC_odd_row"':'';
1.1075.2.45  raeburn  15764:                 $text .= '<td><table class="LC_data_table">';
1.663     raeburn  15765:                 for (my $j=0; $j<$numchildren; $j++) {
                   15766:                     $name = $cats->[$depth]{$parent}[$j];
                   15767:                     $item = &escape($name).':'.&escape($parent).':'.$depth;
                   15768:                     my $deeper = $depth+1;
                   15769:                     my $checked = '';
                   15770:                     if (ref($currcategories) eq 'ARRAY') {
                   15771:                         if (@{$currcategories} > 0) {
                   15772:                             if (grep(/^\Q$item\E$/,@{$currcategories})) {
1.772     bisitz   15773:                                 $checked = ' checked="checked"';
1.663     raeburn  15774:                             }
                   15775:                         }
                   15776:                     }
1.664     raeburn  15777:                     $text .= '<tr><td><span class="LC_nobreak"><label>'.
                   15778:                              '<input type="checkbox" name="usecategory" value="'.
1.1075.2.117  raeburn  15779:                              $item.'"'.$checked.$disabled.' />'.$name.'</label></span>'.
1.675     raeburn  15780:                              '<input type="hidden" name="catname" value="'.$name.'" />'.
                   15781:                              '</td><td>';
1.663     raeburn  15782:                     if (ref($path) eq 'ARRAY') {
                   15783:                         push(@{$path},$name);
1.1075.2.117  raeburn  15784:                         $text .= &assign_category_rows($itemcount,$cats,$deeper,$name,$path,$currcategories,$disabled);
1.663     raeburn  15785:                         pop(@{$path});
                   15786:                     }
                   15787:                     $text .= '</td></tr>';
                   15788:                 }
                   15789:                 $text .= '</table></td>';
                   15790:             }
                   15791:         }
                   15792:     }
                   15793:     return $text;
                   15794: }
                   15795: 
1.1075.2.69  raeburn  15796: =pod
                   15797: 
                   15798: =back
                   15799: 
                   15800: =cut
                   15801: 
1.655     raeburn  15802: ############################################################
                   15803: ############################################################
                   15804: 
                   15805: 
1.443     albertel 15806: sub commit_customrole {
1.664     raeburn  15807:     my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context) = @_;
1.630     raeburn  15808:     my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.
1.443     albertel 15809:                          ($start?', '.&mt('starting').' '.localtime($start):'').
                   15810:                          ($end?', ending '.localtime($end):'').': <b>'.
                   15811:               &Apache::lonnet::assigncustomrole(
1.664     raeburn  15812:                  $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context).
1.443     albertel 15813:                  '</b><br />';
                   15814:     return $output;
                   15815: }
                   15816: 
                   15817: sub commit_standardrole {
1.1075.2.31  raeburn  15818:     my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,$credits) = @_;
1.541     raeburn  15819:     my ($output,$logmsg,$linefeed);
                   15820:     if ($context eq 'auto') {
                   15821:         $linefeed = "\n";
                   15822:     } else {
                   15823:         $linefeed = "<br />\n";
                   15824:     }  
1.443     albertel 15825:     if ($three eq 'st') {
1.541     raeburn  15826:         my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
1.1075.2.31  raeburn  15827:                                          $one,$two,$sec,$context,$credits);
1.541     raeburn  15828:         if (($result =~ /^error/) || ($result eq 'not_in_class') || 
1.626     raeburn  15829:             ($result eq 'unknown_course') || ($result eq 'refused')) {
                   15830:             $output = $logmsg.' '.&mt('Error: ').$result."\n"; 
1.443     albertel 15831:         } else {
1.541     raeburn  15832:             $output = $logmsg.$linefeed.&mt('Assigning').' '.$three.' in '.$url.
1.443     albertel 15833:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  15834:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
                   15835:             if ($context eq 'auto') {
                   15836:                 $output .= $result.$linefeed.&mt('Add to classlist').': ok';
                   15837:             } else {
                   15838:                $output .= '<b>'.$result.'</b>'.$linefeed.
                   15839:                &mt('Add to classlist').': <b>ok</b>';
                   15840:             }
                   15841:             $output .= $linefeed;
1.443     albertel 15842:         }
                   15843:     } else {
                   15844:         $output = &mt('Assigning').' '.$three.' in '.$url.
                   15845:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  15846:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
1.652     raeburn  15847:         my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context);
1.541     raeburn  15848:         if ($context eq 'auto') {
                   15849:             $output .= $result.$linefeed;
                   15850:         } else {
                   15851:             $output .= '<b>'.$result.'</b>'.$linefeed;
                   15852:         }
1.443     albertel 15853:     }
                   15854:     return $output;
                   15855: }
                   15856: 
                   15857: sub commit_studentrole {
1.1075.2.31  raeburn  15858:     my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,
                   15859:         $credits) = @_;
1.626     raeburn  15860:     my ($result,$linefeed,$oldsecurl,$newsecurl);
1.541     raeburn  15861:     if ($context eq 'auto') {
                   15862:         $linefeed = "\n";
                   15863:     } else {
                   15864:         $linefeed = '<br />'."\n";
                   15865:     }
1.443     albertel 15866:     if (defined($one) && defined($two)) {
                   15867:         my $cid=$one.'_'.$two;
                   15868:         my $oldsec=&Apache::lonnet::getsection($udom,$uname,$cid);
                   15869:         my $secchange = 0;
                   15870:         my $expire_role_result;
                   15871:         my $modify_section_result;
1.628     raeburn  15872:         if ($oldsec ne '-1') { 
                   15873:             if ($oldsec ne $sec) {
1.443     albertel 15874:                 $secchange = 1;
1.628     raeburn  15875:                 my $now = time;
1.443     albertel 15876:                 my $uurl='/'.$cid;
                   15877:                 $uurl=~s/\_/\//g;
                   15878:                 if ($oldsec) {
                   15879:                     $uurl.='/'.$oldsec;
                   15880:                 }
1.626     raeburn  15881:                 $oldsecurl = $uurl;
1.628     raeburn  15882:                 $expire_role_result = 
1.1075.2.161.  .14(raeb 15883:-23):                     &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','','',$context);
1.628     raeburn  15884:                 if ($env{'request.course.sec'} ne '') { 
                   15885:                     if ($expire_role_result eq 'refused') {
                   15886:                         my @roles = ('st');
                   15887:                         my @statuses = ('previous');
                   15888:                         my @roledoms = ($one);
                   15889:                         my $withsec = 1;
                   15890:                         my %roleshash = 
                   15891:                             &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
                   15892:                                               \@statuses,\@roles,\@roledoms,$withsec);
                   15893:                         if (defined ($roleshash{$two.':'.$one.':st:'.$oldsec})) {
                   15894:                             my ($oldstart,$oldend) = 
                   15895:                                 split(':',$roleshash{$two.':'.$one.':st:'.$oldsec});
                   15896:                             if ($oldend > 0 && $oldend <= $now) {
                   15897:                                 $expire_role_result = 'ok';
                   15898:                             }
                   15899:                         }
                   15900:                     }
                   15901:                 }
1.443     albertel 15902:                 $result = $expire_role_result;
                   15903:             }
                   15904:         }
                   15905:         if (($expire_role_result eq 'ok') || ($secchange == 0)) {
1.1075.2.31  raeburn  15906:             $modify_section_result = 
                   15907:                 &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,
                   15908:                                                            undef,undef,undef,$sec,
                   15909:                                                            $end,$start,'','',$cid,
                   15910:                                                            '',$context,$credits);
1.443     albertel 15911:             if ($modify_section_result =~ /^ok/) {
                   15912:                 if ($secchange == 1) {
1.628     raeburn  15913:                     if ($sec eq '') {
                   15914:                         $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to student role without a section.',$uname,$oldsec).$linefeed;
                   15915:                     } else {
                   15916:                         $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to new section: [_3].',$uname,$oldsec,$sec).$linefeed;
                   15917:                     }
1.443     albertel 15918:                 } elsif ($oldsec eq '-1') {
1.628     raeburn  15919:                     if ($sec eq '') {
                   15920:                         $$logmsg .= &mt('New student role without a section for [_1] in course [_2].',$uname,$cid).$linefeed;
                   15921:                     } else {
                   15922:                         $$logmsg .= &mt('New student role for [_1] in section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
                   15923:                     }
1.443     albertel 15924:                 } else {
1.628     raeburn  15925:                     if ($sec eq '') {
                   15926:                         $$logmsg .= &mt('Student [_1] assigned to course [_2] without a section.',$uname,$cid).$linefeed;
                   15927:                     } else {
                   15928:                         $$logmsg .= &mt('Student [_1] assigned to section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
                   15929:                     }
1.443     albertel 15930:                 }
                   15931:             } else {
1.628     raeburn  15932:                 if ($secchange) {       
                   15933:                     $$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;
                   15934:                 } else {
                   15935:                     $$logmsg .= &mt('Error when attempting to modify role for [_1] for section: "[_2]" in course [_3] -error:',$uname,$sec,$cid).' '.$modify_section_result.$linefeed;
                   15936:                 }
1.443     albertel 15937:             }
                   15938:             $result = $modify_section_result;
                   15939:         } elsif ($secchange == 1) {
1.628     raeburn  15940:             if ($oldsec eq '') {
1.1075.2.20  raeburn  15941:                 $$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  15942:             } else {
                   15943:                 $$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;
                   15944:             }
1.626     raeburn  15945:             if ($expire_role_result eq 'refused') {
                   15946:                 my $newsecurl = '/'.$cid;
                   15947:                 $newsecurl =~ s/\_/\//g;
                   15948:                 if ($sec ne '') {
                   15949:                     $newsecurl.='/'.$sec;
                   15950:                 }
                   15951:                 if (&Apache::lonnet::allowed('cst',$newsecurl) && !(&Apache::lonnet::allowed('cst',$oldsecurl))) {
                   15952:                     if ($sec eq '') {
                   15953:                         $$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;
                   15954:                     } else {
                   15955:                         $$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;
                   15956:                     }
                   15957:                 }
                   15958:             }
1.443     albertel 15959:         }
                   15960:     } else {
1.626     raeburn  15961:         $$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 15962:         $result = "error: incomplete course id\n";
                   15963:     }
                   15964:     return $result;
                   15965: }
                   15966: 
1.1075.2.25  raeburn  15967: sub show_role_extent {
                   15968:     my ($scope,$context,$role) = @_;
                   15969:     $scope =~ s{^/}{};
                   15970:     my @courseroles = &Apache::lonuserutils::roles_by_context('course',1);
                   15971:     push(@courseroles,'co');
                   15972:     my @authorroles = &Apache::lonuserutils::roles_by_context('author');
                   15973:     if (($context eq 'course') || (grep(/^\Q$role\E/,@courseroles))) {
                   15974:         $scope =~ s{/}{_};
                   15975:         return '<span class="LC_cusr_emph">'.$env{'course.'.$scope.'.description'}.'</span>';
                   15976:     } elsif (($context eq 'author') || (grep(/^\Q$role\E/,@authorroles))) {
                   15977:         my ($audom,$auname) = split(/\//,$scope);
                   15978:         return &mt('[_1] Author Space','<span class="LC_cusr_emph">'.
                   15979:                    &Apache::loncommon::plainname($auname,$audom).'</span>');
                   15980:     } else {
                   15981:         $scope =~ s{/$}{};
                   15982:         return &mt('Domain: [_1]','<span class="LC_cusr_emph">'.
                   15983:                    &Apache::lonnet::domain($scope,'description').'</span>');
                   15984:     }
                   15985: }
                   15986: 
1.443     albertel 15987: ############################################################
                   15988: ############################################################
                   15989: 
1.566     albertel 15990: sub check_clone {
1.578     raeburn  15991:     my ($args,$linefeed) = @_;
1.566     albertel 15992:     my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
                   15993:     my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
                   15994:     my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
1.1075.2.161.  .1(raebu 15995:21):     my $clonetitle;
                   15996:21):     my @clonemsg;
1.566     albertel 15997:     my $can_clone = 0;
1.944     raeburn  15998:     my $lctype = lc($args->{'crstype'});
1.908     raeburn  15999:     if ($lctype ne 'community') {
                   16000:         $lctype = 'course';
                   16001:     }
1.566     albertel 16002:     if ($clonehome eq 'no_host') {
1.944     raeburn  16003:         if ($args->{'crstype'} eq 'Community') {
1.1075.2.161.  .1(raebu 16004:21):             push(@clonemsg,({
                   16005:21):                               mt => 'No new community created.',
                   16006:21):                               args => [],
                   16007:21):                             },
                   16008:21):                             {
                   16009:21):                               mt => 'A new community could not be cloned from the specified original - [_1] - because it is a non-existent community.',
                   16010:21):                               args => [$args->{'clonedomain'}.':'.$args->{'clonedomain'}],
                   16011:21):                             }));
1.908     raeburn  16012:         } else {
1.1075.2.161.  .1(raebu 16013:21):             push(@clonemsg,({
                   16014:21):                               mt => 'No new course created.',
                   16015:21):                               args => [],
                   16016:21):                             },
                   16017:21):                             {
                   16018:21):                               mt => 'A new course could not be cloned from the specified original - [_1] - because it is a non-existent course.',
                   16019:21):                               args => [$args->{'clonecourse'}.':'.$args->{'clonedomain'}],
                   16020:21):                             }));
                   16021:21):         }
1.566     albertel 16022:     } else {
                   16023: 	my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
1.1075.2.161.  .1(raebu 16024:21):         $clonetitle = $clonedesc{'description'};
1.944     raeburn  16025:         if ($args->{'crstype'} eq 'Community') {
1.908     raeburn  16026:             if ($clonedesc{'type'} ne 'Community') {
1.1075.2.161.  .1(raebu 16027:21):                 push(@clonemsg,({
                   16028:21):                                   mt => 'No new community created.',
                   16029:21):                                   args => [],
                   16030:21):                                 },
                   16031:21):                                 {
                   16032:21):                                   mt => 'A new community could not be cloned from the specified original - [_1] - because it is a course not a community.',
                   16033:21):                                   args => [$args->{'clonecourse'}.':'.$args->{'clonedomain'}],
                   16034:21):                                 }));
                   16035:21):                 return ($can_clone,\@clonemsg,$cloneid,$clonehome);
1.908     raeburn  16036:             }
                   16037:         }
1.1075.2.119  raeburn  16038: 	if (($env{'request.role.domain'} eq $args->{'clonedomain'}) &&
1.882     raeburn  16039:             (&Apache::lonnet::allowed('ccc',$env{'request.role.domain'}))) {
1.566     albertel 16040: 	    $can_clone = 1;
                   16041: 	} else {
1.1075.2.95  raeburn  16042: 	    my %clonehash = &Apache::lonnet::get('environment',['cloners','internal.coursecode'],
1.566     albertel 16043: 						 $args->{'clonedomain'},$args->{'clonecourse'});
1.1075.2.95  raeburn  16044:             if ($clonehash{'cloners'} eq '') {
                   16045:                 my %domdefs = &Apache::lonnet::get_domain_defaults($args->{'course_domain'});
                   16046:                 if ($domdefs{'canclone'}) {
                   16047:                     unless ($domdefs{'canclone'} eq 'none') {
                   16048:                         if ($domdefs{'canclone'} eq 'domain') {
                   16049:                             if ($args->{'ccdomain'} eq $args->{'clonedomain'}) {
                   16050:                                 $can_clone = 1;
                   16051:                             }
                   16052:                         } elsif (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) &&
                   16053:                                  ($args->{'clonedomain'} eq  $args->{'course_domain'})) {
                   16054:                             if (&Apache::lonnet::default_instcode_cloning($args->{'clonedomain'},$domdefs{'canclone'},
                   16055:                                                                           $clonehash{'internal.coursecode'},$args->{'crscode'})) {
                   16056:                                 $can_clone = 1;
                   16057:                             }
                   16058:                         }
                   16059:                     }
1.908     raeburn  16060:                 }
1.1075.2.95  raeburn  16061:             } else {
                   16062: 	        my @cloners = split(/,/,$clonehash{'cloners'});
                   16063:                 if (grep(/^\*$/,@cloners)) {
1.942     raeburn  16064:                     $can_clone = 1;
1.1075.2.95  raeburn  16065:                 } elsif (grep(/^\*\:\Q$args->{'ccdomain'}\E$/,@cloners)) {
1.942     raeburn  16066:                     $can_clone = 1;
1.1075.2.96  raeburn  16067:                 } elsif (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners)) {
                   16068:                     $can_clone = 1;
1.1075.2.95  raeburn  16069:                 }
                   16070:                 unless ($can_clone) {
1.1075.2.96  raeburn  16071:                     if (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) &&
                   16072:                         ($args->{'clonedomain'} eq  $args->{'course_domain'})) {
1.1075.2.95  raeburn  16073:                         my (%gotdomdefaults,%gotcodedefaults);
                   16074:                         foreach my $cloner (@cloners) {
                   16075:                             if (($cloner ne '*') && ($cloner !~ /^\*\:$match_domain$/) &&
                   16076:                                 ($cloner !~ /^$match_username\:$match_domain$/) && ($cloner ne '')) {
                   16077:                                 my (%codedefaults,@code_order);
                   16078:                                 if (ref($gotcodedefaults{$args->{'clonedomain'}}) eq 'HASH') {
                   16079:                                     if (ref($gotcodedefaults{$args->{'clonedomain'}}{'defaults'}) eq 'HASH') {
                   16080:                                         %codedefaults = %{$gotcodedefaults{$args->{'clonedomain'}}{'defaults'}};
                   16081:                                     }
                   16082:                                     if (ref($gotcodedefaults{$args->{'clonedomain'}}{'order'}) eq 'ARRAY') {
                   16083:                                         @code_order = @{$gotcodedefaults{$args->{'clonedomain'}}{'order'}};
                   16084:                                     }
                   16085:                                 } else {
                   16086:                                     &Apache::lonnet::auto_instcode_defaults($args->{'clonedomain'},
                   16087:                                                                             \%codedefaults,
                   16088:                                                                             \@code_order);
                   16089:                                     $gotcodedefaults{$args->{'clonedomain'}}{'defaults'} = \%codedefaults;
                   16090:                                     $gotcodedefaults{$args->{'clonedomain'}}{'order'} = \@code_order;
                   16091:                                 }
                   16092:                                 if (@code_order > 0) {
                   16093:                                     if (&Apache::lonnet::check_instcode_cloning(\%codedefaults,\@code_order,
                   16094:                                                                                 $cloner,$clonehash{'internal.coursecode'},
                   16095:                                                                                 $args->{'crscode'})) {
                   16096:                                         $can_clone = 1;
                   16097:                                         last;
                   16098:                                     }
                   16099:                                 }
                   16100:                             }
                   16101:                         }
                   16102:                     }
1.1075.2.96  raeburn  16103:                 }
                   16104:             }
                   16105:             unless ($can_clone) {
                   16106:                 my $ccrole = 'cc';
                   16107:                 if ($args->{'crstype'} eq 'Community') {
                   16108:                     $ccrole = 'co';
                   16109:                 }
                   16110:                 my %roleshash =
                   16111:                     &Apache::lonnet::get_my_roles($args->{'ccuname'},
                   16112:                                                   $args->{'ccdomain'},
                   16113:                                                   'userroles',['active'],[$ccrole],
                   16114:                                                   [$args->{'clonedomain'}]);
                   16115:                 if ($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':'.$ccrole}) {
                   16116:                     $can_clone = 1;
                   16117:                 } elsif (&Apache::lonnet::is_course_owner($args->{'clonedomain'},$args->{'clonecourse'},
                   16118:                                                           $args->{'ccuname'},$args->{'ccdomain'})) {
                   16119:                     $can_clone = 1;
1.1075.2.95  raeburn  16120:                 }
                   16121:             }
                   16122:             unless ($can_clone) {
                   16123:                 if ($args->{'crstype'} eq 'Community') {
1.1075.2.161.  .1(raebu 16124:21):                     push(@clonemsg,({
                   16125:21):                                       mt => 'No new community created.',
                   16126:21):                                       args => [],
                   16127:21):                                     },
                   16128:21):                                     {
                   16129:21):                                       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]).',
                   16130:21):                                       args => [$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'}],
                   16131:21):                                     }));
1.1075.2.95  raeburn  16132:                 } else {
1.1075.2.161.  .1(raebu 16133:21):                     push(@clonemsg,({
                   16134:21):                                       mt => 'No new course created.',
                   16135:21):                                       args => [],
                   16136:21):                                     },
                   16137:21):                                     {
                   16138:21):                                       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]).',
                   16139:21):                                       args => [$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'}],
                   16140:21):                                     }));
1.578     raeburn  16141: 	        }
1.566     albertel 16142: 	    }
1.578     raeburn  16143:         }
1.566     albertel 16144:     }
1.1075.2.161.  .1(raebu 16145:21):     return ($can_clone,\@clonemsg,$cloneid,$clonehome,$clonetitle);
1.566     albertel 16146: }
                   16147: 
1.444     albertel 16148: sub construct_course {
1.1075.2.119  raeburn  16149:     my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context,
1.1075.2.161.  .1(raebu 16150:21):         $cnum,$category,$coderef,$callercontext,$user_lh) = @_;
                   16151:21):     my ($outcome,$msgref,$clonemsgref);
1.541     raeburn  16152:     my $linefeed =  '<br />'."\n";
                   16153:     if ($context eq 'auto') {
                   16154:         $linefeed = "\n";
                   16155:     }
1.566     albertel 16156: 
                   16157: #
                   16158: # Are we cloning?
                   16159: #
1.1075.2.161.  .1(raebu 16160:21):     my ($can_clone,$cloneid,$clonehome,$clonetitle);
1.566     albertel 16161:     if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
1.1075.2.161.  .1(raebu 16162:21): 	($can_clone,$clonemsgref,$cloneid,$clonehome,$clonetitle) = &check_clone($args,$linefeed);
1.566     albertel 16163:         if (!$can_clone) {
1.1075.2.161.  .1(raebu 16164:21): 	    return (0,$outcome,$clonemsgref);
1.566     albertel 16165: 	}
                   16166:     }
                   16167: 
1.444     albertel 16168: #
                   16169: # Open course
                   16170: #
                   16171:     my $crstype = lc($args->{'crstype'});
                   16172:     my %cenv=();
                   16173:     $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},
                   16174:                                              $args->{'cdescr'},
                   16175:                                              $args->{'curl'},
                   16176:                                              $args->{'course_home'},
                   16177:                                              $args->{'nonstandard'},
                   16178:                                              $args->{'crscode'},
                   16179:                                              $args->{'ccuname'}.':'.
                   16180:                                              $args->{'ccdomain'},
1.882     raeburn  16181:                                              $args->{'crstype'},
1.1075.2.161.  .1(raebu 16182:21):                                              $cnum,$context,$category,
                   16183:21):                                              $callercontext);
1.444     albertel 16184: 
                   16185:     # Note: The testing routines depend on this being output; see 
                   16186:     # Utils::Course. This needs to at least be output as a comment
                   16187:     # if anyone ever decides to not show this, and Utils::Course::new
                   16188:     # will need to be suitably modified.
1.1075.2.161.  .1(raebu 16189:21):     if (($callercontext eq 'auto') && ($user_lh ne '')) {
                   16190:21):         $outcome .= &mt_user($user_lh,'New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed;
                   16191:21):     } else {
                   16192:21):         $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed;
                   16193:21):     }
1.943     raeburn  16194:     if ($$courseid =~ /^error:/) {
1.1075.2.161.  .1(raebu 16195:21):         return (0,$outcome,$clonemsgref);
1.943     raeburn  16196:     }
                   16197: 
1.444     albertel 16198: #
                   16199: # Check if created correctly
                   16200: #
1.479     albertel 16201:     ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
1.444     albertel 16202:     my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
1.943     raeburn  16203:     if ($crsuhome eq 'no_host') {
1.1075.2.161.  .1(raebu 16204:21):         if (($callercontext eq 'auto') && ($user_lh ne '')) {
                   16205:21):             $outcome .= &mt_user($user_lh,
                   16206:21):                             'Course creation failed, unrecognized course home server.');
                   16207:21):         } else {
                   16208:21):             $outcome .= &mt('Course creation failed, unrecognized course home server.');
                   16209:21):         }
                   16210:21):         $outcome .= $linefeed;
                   16211:21):         return (0,$outcome,$clonemsgref);
1.943     raeburn  16212:     }
1.541     raeburn  16213:     $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
1.566     albertel 16214: 
1.444     albertel 16215: #
1.566     albertel 16216: # Do the cloning
1.1075.2.161.  .1(raebu 16217:21): #
                   16218:21):     my @clonemsg;
1.566     albertel 16219:     if ($can_clone && $cloneid) {
1.1075.2.161.  .1(raebu 16220:21):         push(@clonemsg,
                   16221:21):                       {
                   16222:21):                           mt => 'Created [_1] by cloning from [_2]',
                   16223:21):                           args => [$crstype,$clonetitle],
                   16224:21):                       });
1.566     albertel 16225: 	my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
1.444     albertel 16226: # Copy all files
1.1075.2.161.  .1(raebu 16227:21):         my @info =
                   16228:21):             &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},
                   16229:21):                                                      $args->{'dateshift'},$args->{'crscode'},
                   16230:21):                                                      $args->{'ccuname'}.':'.$args->{'ccdomain'},
                   16231:21):                                                      $args->{'tinyurls'});
                   16232:21):         if (@info) {
                   16233:21):             push(@clonemsg,@info);
                   16234:21):         }
1.444     albertel 16235: # Restore URL
1.566     albertel 16236: 	$cenv{'url'}=$oldcenv{'url'};
1.444     albertel 16237: # Restore title
1.566     albertel 16238: 	$cenv{'description'}=$oldcenv{'description'};
1.955     raeburn  16239: # Restore creation date, creator and creation context.
                   16240:         $cenv{'internal.created'}=$oldcenv{'internal.created'};
                   16241:         $cenv{'internal.creator'}=$oldcenv{'internal.creator'};
                   16242:         $cenv{'internal.creationcontext'}=$oldcenv{'internal.creationcontext'};
1.444     albertel 16243: # Mark as cloned
1.566     albertel 16244: 	$cenv{'clonedfrom'}=$cloneid;
1.638     www      16245: # Need to clone grading mode
                   16246:         my %newenv=&Apache::lonnet::get('environment',['grading'],$$crsudom,$$crsunum);
                   16247:         $cenv{'grading'}=$newenv{'grading'};
                   16248: # Do not clone these environment entries
                   16249:         &Apache::lonnet::del('environment',
                   16250:                   ['default_enrollment_start_date',
                   16251:                    'default_enrollment_end_date',
                   16252:                    'question.email',
                   16253:                    'policy.email',
                   16254:                    'comment.email',
                   16255:                    'pch.users.denied',
1.725     raeburn  16256:                    'plc.users.denied',
                   16257:                    'hidefromcat',
1.1075.2.36  raeburn  16258:                    'checkforpriv',
1.1075.2.158  raeburn  16259:                    'categories'],
1.638     www      16260:                    $$crsudom,$$crsunum);
1.1075.2.63  raeburn  16261:         if ($args->{'textbook'}) {
                   16262:             $cenv{'internal.textbook'} = $args->{'textbook'};
                   16263:         }
1.444     albertel 16264:     }
1.566     albertel 16265: 
1.444     albertel 16266: #
                   16267: # Set environment (will override cloned, if existing)
                   16268: #
                   16269:     my @sections = ();
                   16270:     my @xlists = ();
                   16271:     if ($args->{'crstype'}) {
                   16272:         $cenv{'type'}=$args->{'crstype'};
                   16273:     }
1.1075.2.161.  .17(raeb 16274:-23):     if ($args->{'lti'}) {
                   16275:-23):         $cenv{'internal.lti'}=$args->{'lti'};
                   16276:-23):     }
1.444     albertel 16277:     if ($args->{'crsid'}) {
                   16278:         $cenv{'courseid'}=$args->{'crsid'};
                   16279:     }
                   16280:     if ($args->{'crscode'}) {
                   16281:         $cenv{'internal.coursecode'}=$args->{'crscode'};
                   16282:     }
                   16283:     if ($args->{'crsquota'} ne '') {
                   16284:         $cenv{'internal.coursequota'}=$args->{'crsquota'};
                   16285:     } else {
                   16286:         $cenv{'internal.coursequota'}=$args->{'crsquota'} = 20;
                   16287:     }
                   16288:     if ($args->{'ccuname'}) {
                   16289:         $cenv{'internal.courseowner'} = $args->{'ccuname'}.
                   16290:                                         ':'.$args->{'ccdomain'};
                   16291:     } else {
                   16292:         $cenv{'internal.courseowner'} = $args->{'curruser'};
                   16293:     }
1.1075.2.31  raeburn  16294:     if ($args->{'defaultcredits'}) {
                   16295:         $cenv{'internal.defaultcredits'} = $args->{'defaultcredits'};
                   16296:     }
1.444     albertel 16297:     my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
1.1075.2.161.  .20(raeb 16298:-23):     my @oklcsecs = (); # Used to accumulate LON-CAPA sections for validated institutional sections.
1.444     albertel 16299:     if ($args->{'crssections'}) {
                   16300:         $cenv{'internal.sectionnums'} = '';
                   16301:         if ($args->{'crssections'} =~ m/,/) {
                   16302:             @sections = split/,/,$args->{'crssections'};
                   16303:         } else {
                   16304:             $sections[0] = $args->{'crssections'};
                   16305:         }
                   16306:         if (@sections > 0) {
                   16307:             foreach my $item (@sections) {
                   16308:                 my ($sec,$gp) = split/:/,$item;
                   16309:                 my $class = $args->{'crscode'}.$sec;
                   16310:                 my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});
                   16311:                 $cenv{'internal.sectionnums'} .= $item.',';
1.1075.2.161.  .20(raeb 16312:-23):                 if ($addcheck eq 'ok') {
                   16313:-23):                     unless (grep(/^\Q$gp\E$/,@oklcsecs)) {
                   16314:-23):                         push(@oklcsecs,$gp);
                   16315:-23):                     }
                   16316:-23):                 } else {
1.1075.2.119  raeburn  16317:                     push(@badclasses,$class);
1.444     albertel 16318:                 }
                   16319:             }
                   16320:             $cenv{'internal.sectionnums'} =~ s/,$//;
                   16321:         }
                   16322:     }
                   16323: # do not hide course coordinator from staff listing, 
                   16324: # even if privileged
                   16325:     $cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
1.1075.2.36  raeburn  16326: # add course coordinator's domain to domains to check for privileged users
                   16327: # if different to course domain
                   16328:     if ($$crsudom ne $args->{'ccdomain'}) {
                   16329:         $cenv{'checkforpriv'} = $args->{'ccdomain'};
                   16330:     }
1.444     albertel 16331: # add crosslistings
                   16332:     if ($args->{'crsxlist'}) {
                   16333:         $cenv{'internal.crosslistings'}='';
                   16334:         if ($args->{'crsxlist'} =~ m/,/) {
                   16335:             @xlists = split/,/,$args->{'crsxlist'};
                   16336:         } else {
                   16337:             $xlists[0] = $args->{'crsxlist'};
                   16338:         }
                   16339:         if (@xlists > 0) {
                   16340:             foreach my $item (@xlists) {
                   16341:                 my ($xl,$gp) = split/:/,$item;
                   16342:                 my $addcheck =  &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});
                   16343:                 $cenv{'internal.crosslistings'} .= $item.',';
1.1075.2.161.  .20(raeb 16344:-23):                 if ($addcheck eq 'ok') {
                   16345:-23):                     unless (grep(/^\Q$gp\E$/,@oklcsecs)) {
                   16346:-23):                         push(@oklcsecs,$gp);
                   16347:-23):                     }
                   16348:-23):                 } else {
1.1075.2.119  raeburn  16349:                     push(@badclasses,$xl);
1.444     albertel 16350:                 }
                   16351:             }
                   16352:             $cenv{'internal.crosslistings'} =~ s/,$//;
                   16353:         }
                   16354:     }
                   16355:     if ($args->{'autoadds'}) {
                   16356:         $cenv{'internal.autoadds'}=$args->{'autoadds'};
                   16357:     }
                   16358:     if ($args->{'autodrops'}) {
                   16359:         $cenv{'internal.autodrops'}=$args->{'autodrops'};
                   16360:     }
                   16361: # check for notification of enrollment changes
                   16362:     my @notified = ();
                   16363:     if ($args->{'notify_owner'}) {
                   16364:         if ($args->{'ccuname'} ne '') {
                   16365:             push(@notified,$args->{'ccuname'}.':'.$args->{'ccdomain'});
                   16366:         }
                   16367:     }
                   16368:     if ($args->{'notify_dc'}) {
                   16369:         if ($uname ne '') { 
1.630     raeburn  16370:             push(@notified,$uname.':'.$udom);
1.444     albertel 16371:         }
                   16372:     }
                   16373:     if (@notified > 0) {
                   16374:         my $notifylist;
                   16375:         if (@notified > 1) {
                   16376:             $notifylist = join(',',@notified);
                   16377:         } else {
                   16378:             $notifylist = $notified[0];
                   16379:         }
                   16380:         $cenv{'internal.notifylist'} = $notifylist;
                   16381:     }
                   16382:     if (@badclasses > 0) {
                   16383:         my %lt=&Apache::lonlocal::texthash(
1.1075.2.119  raeburn  16384:                 'tclb' => 'The courses listed below were included as sections or crosslistings affiliated with your new LON-CAPA course.',
                   16385:                 'howi' => 'However, if automated course roster updates are enabled for this class, these particular sections/crosslistings are not guaranteed to contribute towards enrollment.',
                   16386:                 'itis' => 'It is possible that rights to access enrollment for these classes will be available through assignment of co-owners.',
1.444     albertel 16387:         );
1.1075.2.119  raeburn  16388:         my $badclass_msg = $lt{'tclb'}.$linefeed.$lt{'howi'}.$linefeed.
                   16389:                            &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  16390:         if ($context eq 'auto') {
                   16391:             $outcome .= $badclass_msg.$linefeed;
1.1075.2.119  raeburn  16392:         } else {
1.566     albertel 16393:             $outcome .= '<div class="LC_warning">'.$badclass_msg.$linefeed.'<ul>'."\n";
1.1075.2.119  raeburn  16394:         }
                   16395:         foreach my $item (@badclasses) {
1.541     raeburn  16396:             if ($context eq 'auto') {
1.1075.2.119  raeburn  16397:                 $outcome .= " - $item\n";
1.541     raeburn  16398:             } else {
1.1075.2.119  raeburn  16399:                 $outcome .= "<li>$item</li>\n";
1.541     raeburn  16400:             }
1.1075.2.119  raeburn  16401:         }
                   16402:         if ($context eq 'auto') {
                   16403:             $outcome .= $linefeed;
                   16404:         } else {
                   16405:             $outcome .= "</ul><br /><br /></div>\n";
                   16406:         }
1.444     albertel 16407:     }
                   16408:     if ($args->{'no_end_date'}) {
                   16409:         $args->{'endaccess'} = 0;
                   16410:     }
1.1075.2.161.  .20(raeb 16411:-23): #  If an official course with institutional sections is created by cloning
                   16412:-23): #  an existing course, section-specific hiding of course totals in student's
                   16413:-23): #  view of grades as copied from cloned course, will be checked for valid
                   16414:-23): #  sections.
                   16415:-23):     if (($can_clone && $cloneid) &&
                   16416:-23):         ($cenv{'internal.coursecode'} ne '') &&
                   16417:-23):         ($cenv{'grading'} eq 'standard') &&
                   16418:-23):         ($cenv{'hidetotals'} ne '') &&
                   16419:-23):         ($cenv{'hidetotals'} ne 'all')) {
                   16420:-23):         my @hidesecs;
                   16421:-23):         my $deletehidetotals;
                   16422:-23):         if (@oklcsecs) {
                   16423:-23):             foreach my $sec (split(/,/,$cenv{'hidetotals'})) {
                   16424:-23):                 if (grep(/^\Q$sec$/,@oklcsecs)) {
                   16425:-23):                     push(@hidesecs,$sec);
                   16426:-23):                 }
                   16427:-23):             }
                   16428:-23):             if (@hidesecs) {
                   16429:-23):                 $cenv{'hidetotals'} = join(',',@hidesecs);
                   16430:-23):             } else {
                   16431:-23):                 $deletehidetotals = 1;
                   16432:-23):             }
                   16433:-23):         } else {
                   16434:-23):             $deletehidetotals = 1;
                   16435:-23):         }
                   16436:-23):         if ($deletehidetotals) {
                   16437:-23):             delete($cenv{'hidetotals'});
                   16438:-23):             &Apache::lonnet::del('environment',['hidetotals'],$$crsudom,$$crsunum);
                   16439:-23):         }
                   16440:-23):     }
1.444     albertel 16441:     $cenv{'internal.autostart'}=$args->{'enrollstart'};
                   16442:     $cenv{'internal.autoend'}=$args->{'enrollend'};
                   16443:     $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};
                   16444:     $cenv{'default_enrollment_end_date'}=$args->{'endaccess'};
                   16445:     if ($args->{'showphotos'}) {
                   16446:       $cenv{'internal.showphotos'}=$args->{'showphotos'};
                   16447:     }
                   16448:     $cenv{'internal.authtype'} = $args->{'authtype'};
                   16449:     $cenv{'internal.autharg'} = $args->{'autharg'}; 
                   16450:     if ( ($cenv{'internal.authtype'} =~ /^krb/) && ($cenv{'internal.autoadds'} == 1)) {
                   16451:         if (! defined($cenv{'internal.autharg'}) || $cenv{'internal.autharg'}  eq '') {
1.541     raeburn  16452:             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'); 
                   16453:             if ($context eq 'auto') {
                   16454:                 $outcome .= $krb_msg;
                   16455:             } else {
1.566     albertel 16456:                 $outcome .= '<span class="LC_error">'.$krb_msg.'</span>';
1.541     raeburn  16457:             }
                   16458:             $outcome .= $linefeed;
1.444     albertel 16459:         }
                   16460:     }
                   16461:     if (($args->{'ccdomain'}) && ($args->{'ccuname'})) {
                   16462:        if ($args->{'setpolicy'}) {
                   16463:            $cenv{'policy.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   16464:        }
                   16465:        if ($args->{'setcontent'}) {
                   16466:            $cenv{'question.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   16467:        }
1.1075.2.110  raeburn  16468:        if ($args->{'setcomment'}) {
                   16469:            $cenv{'comment.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   16470:        }
1.444     albertel 16471:     }
                   16472:     if ($args->{'reshome'}) {
                   16473: 	$cenv{'reshome'}=$args->{'reshome'}.'/';
                   16474: 	$cenv{'reshome'}=~s/\/+$/\//;
                   16475:     }
                   16476: #
                   16477: # course has keyed access
                   16478: #
                   16479:     if ($args->{'setkeys'}) {
                   16480:        $cenv{'keyaccess'}='yes';
                   16481:     }
                   16482: # if specified, key authority is not course, but user
                   16483: # only active if keyaccess is yes
                   16484:     if ($args->{'keyauth'}) {
1.487     albertel 16485: 	my ($user,$domain) = split(':',$args->{'keyauth'});
                   16486: 	$user = &LONCAPA::clean_username($user);
                   16487: 	$domain = &LONCAPA::clean_username($domain);
1.488     foxr     16488: 	if ($user ne '' && $domain ne '') {
1.487     albertel 16489: 	    $cenv{'keyauth'}=$user.':'.$domain;
1.444     albertel 16490: 	}
                   16491:     }
                   16492: 
1.1075.2.59  raeburn  16493: #
                   16494: #  generate and store uniquecode (available to course requester), if course should have one.
                   16495: #
                   16496:     if ($args->{'uniquecode'}) {
                   16497:         my ($code,$error) = &make_unique_code($$crsudom,$$crsunum);
                   16498:         if ($code) {
                   16499:             $cenv{'internal.uniquecode'} = $code;
                   16500:             my %crsinfo =
                   16501:                 &Apache::lonnet::courseiddump($$crsudom,'.',1,'.','.',$$crsunum,undef,undef,'.');
                   16502:             if (ref($crsinfo{$$crsudom.'_'.$$crsunum}) eq 'HASH') {
                   16503:                 $crsinfo{$$crsudom.'_'.$$crsunum}{'uniquecode'} = $code;
                   16504:                 my $putres = &Apache::lonnet::courseidput($$crsudom,\%crsinfo,$crsuhome,'notime');
                   16505:             }
                   16506:             if (ref($coderef)) {
                   16507:                 $$coderef = $code;
                   16508:             }
                   16509:         }
                   16510:     }
                   16511: 
1.444     albertel 16512:     if ($args->{'disresdis'}) {
                   16513:         $cenv{'pch.roles.denied'}='st';
                   16514:     }
                   16515:     if ($args->{'disablechat'}) {
                   16516:         $cenv{'plc.roles.denied'}='st';
                   16517:     }
                   16518: 
                   16519:     # Record we've not yet viewed the Course Initialization Helper for this 
                   16520:     # course
                   16521:     $cenv{'course.helper.not.run'} = 1;
                   16522:     #
                   16523:     # Use new Randomseed
                   16524:     #
                   16525:     $cenv{'rndseed'}=&Apache::lonnet::latest_rnd_algorithm_id();;
                   16526:     $cenv{'receiptalg'}=&Apache::lonnet::latest_receipt_algorithm_id();;
                   16527:     #
                   16528:     # The encryption code and receipt prefix for this course
                   16529:     #
                   16530:     $cenv{'internal.encseed'}=$Apache::lonnet::perlvar{'lonReceipt'}.$$.time.int(rand(9999));
                   16531:     $cenv{'internal.encpref'}=100+int(9*rand(99));
                   16532:     #
                   16533:     # By default, use standard grading
                   16534:     if (!defined($cenv{'grading'})) { $cenv{'grading'} = 'standard'; }
                   16535: 
1.541     raeburn  16536:     $outcome .= $linefeed.&mt('Setting environment').': '.                 
                   16537:           &Apache::lonnet::put('environment',\%cenv,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 16538: #
                   16539: # Open all assignments
                   16540: #
                   16541:     if ($args->{'openall'}) {
1.1075.2.146  raeburn  16542:        my $opendate = time;
                   16543:        if ($args->{'openallfrom'} =~ /^\d+$/) {
                   16544:            $opendate = $args->{'openallfrom'};
                   16545:        }
1.444     albertel 16546:        my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate';
1.1075.2.146  raeburn  16547:        my %storecontent = ($storeunder         => $opendate,
1.444     albertel 16548:                            $storeunder.'.type' => 'date_start');
1.1075.2.146  raeburn  16549:        $outcome .= &mt('All assignments open starting [_1]',
                   16550:                        &Apache::lonlocal::locallocaltime($opendate)).': '.
                   16551:                    &Apache::lonnet::cput
                   16552:                        ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 16553:    }
                   16554: #
                   16555: # Set first page
                   16556: #
                   16557:     unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
                   16558: 	    || ($cloneid)) {
                   16559: 	$outcome .= &mt('Setting first resource').': ';
1.445     albertel 16560: 
                   16561: 	my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
                   16562:         my ($errtext,$fatal)=&LONCAPA::map::mapread($map);
                   16563: 
1.444     albertel 16564:         $outcome .= ($fatal?$errtext:'read ok').' - ';
                   16565:         my $title; my $url;
                   16566:         if ($args->{'firstres'} eq 'syl') {
1.690     bisitz   16567: 	    $title=&mt('Syllabus');
1.444     albertel 16568:             $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus';
                   16569:         } else {
1.963     raeburn  16570:             $title=&mt('Table of Contents');
1.444     albertel 16571:             $url='/adm/navmaps';
                   16572:         }
1.445     albertel 16573: 
                   16574:         $LONCAPA::map::resources[1]=$title.':'.$url.':false:start:res';
                   16575: 	(my $outtext,$errtext) = &LONCAPA::map::storemap($map,1);
                   16576: 
                   16577: 	if ($errtext) { $fatal=2; }
1.541     raeburn  16578:         $outcome .= ($fatal?$errtext:'write ok').$linefeed;
1.444     albertel 16579:     }
1.566     albertel 16580: 
1.1075.2.161.  .1(raebu 16581:21):     return (1,$outcome,\@clonemsg);
1.444     albertel 16582: }
                   16583: 
1.1075.2.59  raeburn  16584: sub make_unique_code {
                   16585:     my ($cdom,$cnum) = @_;
                   16586:     # get lock on uniquecodes db
                   16587:     my $lockhash = {
                   16588:                       $cnum."\0".'uniquecodes' => $env{'user.name'}.
                   16589:                                                   ':'.$env{'user.domain'},
                   16590:                    };
                   16591:     my $tries = 0;
                   16592:     my $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
                   16593:     my ($code,$error);
                   16594: 
                   16595:     while (($gotlock ne 'ok') && ($tries<3)) {
                   16596:         $tries ++;
                   16597:         sleep 1;
                   16598:         $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
                   16599:     }
                   16600:     if ($gotlock eq 'ok') {
                   16601:         my %currcodes = &Apache::lonnet::dump_dom('uniquecodes',$cdom);
                   16602:         my $gotcode;
                   16603:         my $attempts = 0;
                   16604:         while ((!$gotcode) && ($attempts < 100)) {
                   16605:             $code = &generate_code();
                   16606:             if (!exists($currcodes{$code})) {
                   16607:                 $gotcode = 1;
                   16608:                 unless (&Apache::lonnet::newput_dom('uniquecodes',{ $code => $cnum },$cdom) eq 'ok') {
                   16609:                     $error = 'nostore';
                   16610:                 }
                   16611:             }
                   16612:             $attempts ++;
                   16613:         }
                   16614:         my @del_lock = ($cnum."\0".'uniquecodes');
                   16615:         my $dellockoutcome = &Apache::lonnet::del_dom('uniquecodes',\@del_lock,$cdom);
                   16616:     } else {
                   16617:         $error = 'nolock';
                   16618:     }
                   16619:     return ($code,$error);
                   16620: }
                   16621: 
                   16622: sub generate_code {
                   16623:     my $code;
                   16624:     my @letts = qw(B C D G H J K M N P Q R S T V W X Z);
                   16625:     for (my $i=0; $i<6; $i++) {
                   16626:         my $lettnum = int (rand 2);
                   16627:         my $item = '';
                   16628:         if ($lettnum) {
                   16629:             $item = $letts[int( rand(18) )];
                   16630:         } else {
                   16631:             $item = 1+int( rand(8) );
                   16632:         }
                   16633:         $code .= $item;
                   16634:     }
                   16635:     return $code;
                   16636: }
                   16637: 
1.444     albertel 16638: ############################################################
                   16639: ############################################################
                   16640: 
1.953     droeschl 16641: #SD
                   16642: # only Community and Course, or anything else?
1.378     raeburn  16643: sub course_type {
                   16644:     my ($cid) = @_;
                   16645:     if (!defined($cid)) {
                   16646:         $cid = $env{'request.course.id'};
                   16647:     }
1.404     albertel 16648:     if (defined($env{'course.'.$cid.'.type'})) {
                   16649:         return $env{'course.'.$cid.'.type'};
1.378     raeburn  16650:     } else {
                   16651:         return 'Course';
1.377     raeburn  16652:     }
                   16653: }
1.156     albertel 16654: 
1.406     raeburn  16655: sub group_term {
                   16656:     my $crstype = &course_type();
                   16657:     my %names = (
                   16658:                   'Course' => 'group',
1.865     raeburn  16659:                   'Community' => 'group',
1.406     raeburn  16660:                 );
                   16661:     return $names{$crstype};
                   16662: }
                   16663: 
1.902     raeburn  16664: sub course_types {
1.1075.2.161.  .17(raeb 16665:-23):     my @types = ('official','unofficial','community','textbook','lti');
1.902     raeburn  16666:     my %typename = (
                   16667:                          official   => 'Official course',
                   16668:                          unofficial => 'Unofficial course',
                   16669:                          community  => 'Community',
1.1075.2.59  raeburn  16670:                          textbook   => 'Textbook course',
1.1075.2.161.  .17(raeb 16671:-23):                          lti        => 'LTI provider',
1.902     raeburn  16672:                    );
                   16673:     return (\@types,\%typename);
                   16674: }
                   16675: 
1.156     albertel 16676: sub icon {
                   16677:     my ($file)=@_;
1.505     albertel 16678:     my $curfext = lc((split(/\./,$file))[-1]);
1.168     albertel 16679:     my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif';
1.156     albertel 16680:     my $embstyle = &Apache::loncommon::fileembstyle($curfext);
1.168     albertel 16681:     if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) {
                   16682: 	if (-e  $Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
                   16683: 	          $Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   16684: 	            $curfext.".gif") {
                   16685: 	    $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   16686: 		$curfext.".gif";
                   16687: 	}
                   16688:     }
1.249     albertel 16689:     return &lonhttpdurl($iconname);
1.154     albertel 16690: } 
1.84      albertel 16691: 
1.575     albertel 16692: sub lonhttpdurl {
1.692     www      16693: #
                   16694: # Had been used for "small fry" static images on separate port 8080.
                   16695: # Modify here if lightweight http functionality desired again.
                   16696: # Currently eliminated due to increasing firewall issues.
                   16697: #
1.575     albertel 16698:     my ($url)=@_;
1.692     www      16699:     return $url;
1.215     albertel 16700: }
                   16701: 
1.213     albertel 16702: sub connection_aborted {
                   16703:     my ($r)=@_;
                   16704:     $r->print(" ");$r->rflush();
                   16705:     my $c = $r->connection;
                   16706:     return $c->aborted();
                   16707: }
                   16708: 
1.221     foxr     16709: #    Escapes strings that may have embedded 's that will be put into
1.222     foxr     16710: #    strings as 'strings'.
                   16711: sub escape_single {
1.221     foxr     16712:     my ($input) = @_;
1.223     albertel 16713:     $input =~ s/\\/\\\\/g;	# Escape the \'s..(must be first)>
1.221     foxr     16714:     $input =~ s/\'/\\\'/g;	# Esacpe the 's....
                   16715:     return $input;
                   16716: }
1.223     albertel 16717: 
1.222     foxr     16718: #  Same as escape_single, but escape's "'s  This 
                   16719: #  can be used for  "strings"
                   16720: sub escape_double {
                   16721:     my ($input) = @_;
                   16722:     $input =~ s/\\/\\\\/g;	# Escape the /'s..(must be first)>
                   16723:     $input =~ s/\"/\\\"/g;	# Esacpe the "s....
                   16724:     return $input;
                   16725: }
1.223     albertel 16726:  
1.222     foxr     16727: #   Escapes the last element of a full URL.
                   16728: sub escape_url {
                   16729:     my ($url)   = @_;
1.238     raeburn  16730:     my @urlslices = split(/\//, $url,-1);
1.369     www      16731:     my $lastitem = &escape(pop(@urlslices));
1.1075.2.83  raeburn  16732:     return &HTML::Entities::encode(join('/',@urlslices),"'").'/'.$lastitem;
1.222     foxr     16733: }
1.462     albertel 16734: 
1.820     raeburn  16735: sub compare_arrays {
                   16736:     my ($arrayref1,$arrayref2) = @_;
                   16737:     my (@difference,%count);
                   16738:     @difference = ();
                   16739:     %count = ();
                   16740:     if ((ref($arrayref1) eq 'ARRAY') && (ref($arrayref2) eq 'ARRAY')) {
                   16741:         foreach my $element (@{$arrayref1}, @{$arrayref2}) { $count{$element}++; }
                   16742:         foreach my $element (keys(%count)) {
                   16743:             if ($count{$element} == 1) {
                   16744:                 push(@difference,$element);
                   16745:             }
                   16746:         }
                   16747:     }
                   16748:     return @difference;
                   16749: }
                   16750: 
1.1075.2.152  raeburn  16751: sub lon_status_items {
                   16752:     my %defaults = (
                   16753:                      E         => 100,
                   16754:                      W         => 4,
                   16755:                      N         => 1,
                   16756:                      U         => 5,
                   16757:                      threshold => 200,
                   16758:                      sysmail   => 2500,
                   16759:                    );
                   16760:     my %names = (
                   16761:                    E => 'Errors',
                   16762:                    W => 'Warnings',
                   16763:                    N => 'Notices',
                   16764:                    U => 'Unsent',
                   16765:                 );
                   16766:     return (\%defaults,\%names);
                   16767: }
                   16768: 
1.817     bisitz   16769: # -------------------------------------------------------- Initialize user login
1.462     albertel 16770: sub init_user_environment {
1.463     albertel 16771:     my ($r, $username, $domain, $authhost, $form, $args) = @_;
1.462     albertel 16772:     my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
                   16773: 
                   16774:     my $public=($username eq 'public' && $domain eq 'public');
                   16775: 
                   16776: # See if old ID present, if so, remove
                   16777: 
1.1062    raeburn  16778:     my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv);
1.462     albertel 16779:     my $now=time;
                   16780: 
                   16781:     if ($public) {
                   16782: 	my $max_public=100;
                   16783: 	my $oldest;
                   16784: 	my $oldest_time=0;
                   16785: 	for(my $next=1;$next<=$max_public;$next++) {
                   16786: 	    if (-e $lonids."/publicuser_$next.id") {
                   16787: 		my $mtime=(stat($lonids."/publicuser_$next.id"))[9];
                   16788: 		if ($mtime<$oldest_time || !$oldest_time) {
                   16789: 		    $oldest_time=$mtime;
                   16790: 		    $oldest=$next;
                   16791: 		}
                   16792: 	    } else {
                   16793: 		$cookie="publicuser_$next";
                   16794: 		last;
                   16795: 	    }
                   16796: 	}
                   16797: 	if (!$cookie) { $cookie="publicuser_$oldest"; }
                   16798:     } else {
1.463     albertel 16799: 	# if this isn't a robot, kill any existing non-robot sessions
                   16800: 	if (!$args->{'robot'}) {
                   16801: 	    opendir(DIR,$lonids);
                   16802: 	    while ($filename=readdir(DIR)) {
                   16803: 		if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
1.1075.2.136  raeburn  16804:                     if (tie(my %oldenv,'GDBM_File',"$lonids/$filename",
                   16805:                             &GDBM_READER(),0640)) {
                   16806:                         my $linkedfile;
                   16807:                         if (exists($oldenv{'user.linkedenv'})) {
                   16808:                             $linkedfile = $oldenv{'user.linkedenv'};
                   16809:                         }
                   16810:                         untie(%oldenv);
                   16811:                         if (unlink("$lonids/$filename")) {
                   16812:                             if ($linkedfile =~ /^[a-f0-9]+_linked$/) {
                   16813:                                 if (-l "$lonids/$linkedfile.id") {
                   16814:                                     unlink("$lonids/$linkedfile.id");
                   16815:                                 }
                   16816:                             }
                   16817:                         }
                   16818:                     } else {
                   16819:                         unlink($lonids.'/'.$filename);
                   16820:                     }
1.463     albertel 16821: 		}
1.462     albertel 16822: 	    }
1.463     albertel 16823: 	    closedir(DIR);
1.1075.2.84  raeburn  16824: # If there is a undeleted lockfile for the user's paste buffer remove it.
                   16825:             my $namespace = 'nohist_courseeditor';
                   16826:             my $lockingkey = 'paste'."\0".'locked_num';
                   16827:             my %lockhash = &Apache::lonnet::get($namespace,[$lockingkey],
                   16828:                                                 $domain,$username);
                   16829:             if (exists($lockhash{$lockingkey})) {
                   16830:                 my $delresult = &Apache::lonnet::del($namespace,[$lockingkey],$domain,$username);
                   16831:                 unless ($delresult eq 'ok') {
                   16832:                     &Apache::lonnet::logthis("Failed to delete paste buffer locking key in $namespace for ".$username.":".$domain." Result was: $delresult");
                   16833:                 }
                   16834:             }
1.462     albertel 16835: 	}
                   16836: # Give them a new cookie
1.463     albertel 16837: 	my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
1.684     www      16838: 		                   : $now.$$.int(rand(10000)));
1.463     albertel 16839: 	$cookie="$username\_$id\_$domain\_$authhost";
1.462     albertel 16840:     
                   16841: # Initialize roles
                   16842: 
1.1062    raeburn  16843: 	($userroles,$firstaccenv,$timerintenv) = 
                   16844:             &Apache::lonnet::rolesinit($domain,$username,$authhost);
1.462     albertel 16845:     }
                   16846: # ------------------------------------ Check browser type and MathML capability
                   16847: 
1.1075.2.77  raeburn  16848:     my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,$clientunicode,
                   16849:         $clientos,$clientmobile,$clientinfo,$clientosversion) = &decode_user_agent($r);
1.462     albertel 16850: 
                   16851: # ------------------------------------------------------------- Get environment
                   16852: 
                   16853:     my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
                   16854:     my ($tmp) = keys(%userenv);
                   16855:     if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   16856:     } else {
                   16857: 	undef(%userenv);
                   16858:     }
                   16859:     if (($userenv{'interface'}) && (!$form->{'interface'})) {
                   16860: 	$form->{'interface'}=$userenv{'interface'};
                   16861:     }
                   16862:     if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
                   16863: 
                   16864: # --------------- Do not trust query string to be put directly into environment
1.817     bisitz   16865:     foreach my $option ('interface','localpath','localres') {
                   16866:         $form->{$option}=~s/[\n\r\=]//gs;
1.462     albertel 16867:     }
                   16868: # --------------------------------------------------------- Write first profile
                   16869: 
                   16870:     {
1.1075.2.150  raeburn  16871:         my $ip = &Apache::lonnet::get_requestor_ip();
1.462     albertel 16872: 	my %initial_env = 
                   16873: 	    ("user.name"          => $username,
                   16874: 	     "user.domain"        => $domain,
                   16875: 	     "user.home"          => $authhost,
                   16876: 	     "browser.type"       => $clientbrowser,
                   16877: 	     "browser.version"    => $clientversion,
                   16878: 	     "browser.mathml"     => $clientmathml,
                   16879: 	     "browser.unicode"    => $clientunicode,
                   16880: 	     "browser.os"         => $clientos,
1.1075.2.42  raeburn  16881:              "browser.mobile"     => $clientmobile,
                   16882:              "browser.info"       => $clientinfo,
1.1075.2.77  raeburn  16883:              "browser.osversion"  => $clientosversion,
1.462     albertel 16884: 	     "server.domain"      => $Apache::lonnet::perlvar{'lonDefDomain'},
                   16885: 	     "request.course.fn"  => '',
                   16886: 	     "request.course.uri" => '',
                   16887: 	     "request.course.sec" => '',
                   16888: 	     "request.role"       => 'cm',
                   16889: 	     "request.role.adv"   => $env{'user.adv'},
1.1075.2.150  raeburn  16890: 	     "request.host"       => $ip,);
1.462     albertel 16891: 
                   16892:         if ($form->{'localpath'}) {
                   16893: 	    $initial_env{"browser.localpath"}  = $form->{'localpath'};
                   16894: 	    $initial_env{"browser.localres"}   = $form->{'localres'};
                   16895:         }
                   16896: 	
                   16897: 	if ($form->{'interface'}) {
                   16898: 	    $form->{'interface'}=~s/\W//gs;
                   16899: 	    $initial_env{"browser.interface"} = $form->{'interface'};
                   16900: 	    $env{'browser.interface'}=$form->{'interface'};
                   16901: 	}
                   16902: 
1.1075.2.54  raeburn  16903:         if ($form->{'iptoken'}) {
                   16904:             my $lonhost = $r->dir_config('lonHostID');
                   16905:             $initial_env{"user.noloadbalance"} = $lonhost;
                   16906:             $env{'user.noloadbalance'} = $lonhost;
                   16907:         }
                   16908: 
1.1075.2.120  raeburn  16909:         if ($form->{'noloadbalance'}) {
                   16910:             my @hosts = &Apache::lonnet::current_machine_ids();
                   16911:             my $hosthere = $form->{'noloadbalance'};
                   16912:             if (grep(/^\Q$hosthere\E$/,@hosts)) {
                   16913:                 $initial_env{"user.noloadbalance"} = $hosthere;
                   16914:                 $env{'user.noloadbalance'} = $hosthere;
                   16915:             }
                   16916:         }
                   16917: 
1.1016    raeburn  16918:         unless ($domain eq 'public') {
1.1075.2.125  raeburn  16919:             my %is_adv = ( is_adv => $env{'user.adv'} );
                   16920:             my %domdef = &Apache::lonnet::get_domain_defaults($domain);
1.980     raeburn  16921: 
1.1075.2.161.  .10(raeb 16922:-22):             foreach my $tool ('aboutme','blog','webdav','portfolio','timezone') {
1.1075.2.125  raeburn  16923:                 $userenv{'availabletools.'.$tool} = 
                   16924:                     &Apache::lonnet::usertools_access($username,$domain,$tool,'reload',
                   16925:                                                       undef,\%userenv,\%domdef,\%is_adv);
                   16926:             }
1.724     raeburn  16927: 
1.1075.2.161.  .17(raeb 16928:-23):             foreach my $crstype ('official','unofficial','community','textbook','lti') {
1.1075.2.125  raeburn  16929:                 $userenv{'canrequest.'.$crstype} =
                   16930:                     &Apache::lonnet::usertools_access($username,$domain,$crstype,
                   16931:                                                       'reload','requestcourses',
                   16932:                                                       \%userenv,\%domdef,\%is_adv);
                   16933:             }
1.765     raeburn  16934: 
1.1075.2.125  raeburn  16935:             $userenv{'canrequest.author'} =
                   16936:                 &Apache::lonnet::usertools_access($username,$domain,'requestauthor',
                   16937:                                                   'reload','requestauthor',
                   16938:                                                   \%userenv,\%domdef,\%is_adv);
                   16939:             my %reqauthor = &Apache::lonnet::get('requestauthor',['author_status','author'],
                   16940:                                                  $domain,$username);
                   16941:             my $reqstatus = $reqauthor{'author_status'};
                   16942:             if ($reqstatus eq 'approval' || $reqstatus eq 'approved') {
                   16943:                 if (ref($reqauthor{'author'}) eq 'HASH') {
                   16944:                     $userenv{'requestauthorqueued'} = $reqstatus.':'.
                   16945:                                                       $reqauthor{'author'}{'timestamp'};
                   16946:                 }
1.1075.2.14  raeburn  16947:             }
                   16948:         }
                   16949: 
1.462     albertel 16950: 	$env{'user.environment'} = "$lonids/$cookie.id";
1.1062    raeburn  16951: 
1.462     albertel 16952: 	if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
                   16953: 		 &GDBM_WRCREAT(),0640)) {
                   16954: 	    &_add_to_env(\%disk_env,\%initial_env);
                   16955: 	    &_add_to_env(\%disk_env,\%userenv,'environment.');
                   16956: 	    &_add_to_env(\%disk_env,$userroles);
1.1062    raeburn  16957:             if (ref($firstaccenv) eq 'HASH') {
                   16958:                 &_add_to_env(\%disk_env,$firstaccenv);
                   16959:             }
                   16960:             if (ref($timerintenv) eq 'HASH') {
                   16961:                 &_add_to_env(\%disk_env,$timerintenv);
                   16962:             }
1.463     albertel 16963: 	    if (ref($args->{'extra_env'})) {
                   16964: 		&_add_to_env(\%disk_env,$args->{'extra_env'});
                   16965: 	    }
1.462     albertel 16966: 	    untie(%disk_env);
                   16967: 	} else {
1.705     tempelho 16968: 	    &Apache::lonnet::logthis("<span style=\"color:blue;\">WARNING: ".
                   16969: 			   'Could not create environment storage in lonauth: '.$!.'</span>');
1.462     albertel 16970: 	    return 'error: '.$!;
                   16971: 	}
                   16972:     }
                   16973:     $env{'request.role'}='cm';
                   16974:     $env{'request.role.adv'}=$env{'user.adv'};
                   16975:     $env{'browser.type'}=$clientbrowser;
                   16976: 
                   16977:     return $cookie;
                   16978: 
                   16979: }
                   16980: 
                   16981: sub _add_to_env {
                   16982:     my ($idf,$env_data,$prefix) = @_;
1.676     raeburn  16983:     if (ref($env_data) eq 'HASH') {
                   16984:         while (my ($key,$value) = each(%$env_data)) {
                   16985: 	    $idf->{$prefix.$key} = $value;
                   16986: 	    $env{$prefix.$key}   = $value;
                   16987:         }
1.462     albertel 16988:     }
                   16989: }
                   16990: 
1.685     tempelho 16991: # --- Get the symbolic name of a problem and the url
                   16992: sub get_symb {
                   16993:     my ($request,$silent) = @_;
1.726     raeburn  16994:     (my $url=$env{'form.url'}) =~ s-^https?\://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
1.685     tempelho 16995:     my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
                   16996:     if ($symb eq '') {
                   16997:         if (!$silent) {
1.1071    raeburn  16998:             if (ref($request)) { 
                   16999:                 $request->print("Unable to handle ambiguous references:$url:.");
                   17000:             }
1.685     tempelho 17001:             return ();
                   17002:         }
                   17003:     }
                   17004:     &Apache::lonenc::check_decrypt(\$symb);
                   17005:     return ($symb);
                   17006: }
                   17007: 
                   17008: # --------------------------------------------------------------Get annotation
                   17009: 
                   17010: sub get_annotation {
                   17011:     my ($symb,$enc) = @_;
                   17012: 
                   17013:     my $key = $symb;
                   17014:     if (!$enc) {
                   17015:         $key =
                   17016:             &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]);
                   17017:     }
                   17018:     my %annotation=&Apache::lonnet::get('nohist_annotations',[$key]);
                   17019:     return $annotation{$key};
                   17020: }
                   17021: 
                   17022: sub clean_symb {
1.731     raeburn  17023:     my ($symb,$delete_enc) = @_;
1.685     tempelho 17024: 
                   17025:     &Apache::lonenc::check_decrypt(\$symb);
                   17026:     my $enc = $env{'request.enc'};
1.731     raeburn  17027:     if ($delete_enc) {
1.730     raeburn  17028:         delete($env{'request.enc'});
                   17029:     }
1.685     tempelho 17030: 
                   17031:     return ($symb,$enc);
                   17032: }
1.462     albertel 17033: 
1.1075.2.69  raeburn  17034: ############################################################
                   17035: ############################################################
                   17036: 
                   17037: =pod
                   17038: 
                   17039: =head1 Routines for building display used to search for courses
                   17040: 
                   17041: 
                   17042: =over 4
                   17043: 
                   17044: =item * &build_filters()
                   17045: 
                   17046: Create markup for a table used to set filters to use when selecting
                   17047: courses in a domain.  Used by lonpickcourse.pm, lonmodifycourse.pm
                   17048: and quotacheck.pl
                   17049: 
                   17050: 
                   17051: Inputs:
                   17052: 
                   17053: filterlist - anonymous array of fields to include as potential filters
                   17054: 
                   17055: crstype - course type
                   17056: 
                   17057: roleelement - fifth arg in selectcourse_link() populates fifth arg in javascript: opencrsbrowser() function, used
                   17058:               to pop-open a course selector (will contain "extra element").
                   17059: 
                   17060: multelement - if multiple course selections will be allowed, this will be a hidden form element: name: multiple; value: 1
                   17061: 
                   17062: filter - anonymous hash of criteria and their values
                   17063: 
                   17064: action - form action
                   17065: 
                   17066: numfiltersref - ref to scalar (count of number of elements in institutional codes -- e.g., 4 for year, semester, department, and number)
                   17067: 
                   17068: caller - caller context (e.g., set to 'modifycourse' when routine is called from lonmodifycourse.pm)
                   17069: 
                   17070: cloneruname - username of owner of new course who wants to clone
                   17071: 
                   17072: clonerudom - domain of owner of new course who wants to clone
                   17073: 
                   17074: typeelem - text to use for left column in row containing course type (i.e., Course, Community or Course/Community)
                   17075: 
                   17076: codetitlesref - reference to array of titles of components in institutional codes (official courses)
                   17077: 
                   17078: codedom - domain
                   17079: 
                   17080: formname - value of form element named "form".
                   17081: 
                   17082: fixeddom - domain, if fixed.
                   17083: 
                   17084: prevphase - value to assign to form element named "phase" when going back to the previous screen
                   17085: 
                   17086: cnameelement - name of form element in form on opener page which will receive title of selected course
                   17087: 
                   17088: cnumelement - name of form element in form on opener page which will receive courseID  of selected course
                   17089: 
                   17090: cdomelement - name of form element in form on opener page which will receive domain of selected course
                   17091: 
                   17092: setroles - includes access constraint identifier when setting a roles-based condition for acces to a portfolio file
                   17093: 
                   17094: clonetext - hidden form elements containing list of courses cloneable by intended course owner when DC creates a course
                   17095: 
                   17096: clonewarning - warning message about missing information for intended course owner when DC creates a course
                   17097: 
                   17098: 
                   17099: Returns: $output - HTML for display of search criteria, and hidden form elements.
                   17100: 
                   17101: 
                   17102: Side Effects: None
                   17103: 
                   17104: =cut
                   17105: 
                   17106: # ---------------------------------------------- search for courses based on last activity etc.
                   17107: 
                   17108: sub build_filters {
                   17109:     my ($filterlist,$crstype,$roleelement,$multelement,$filter,$action,
                   17110:         $numtitlesref,$caller,$cloneruname,$clonerudom,$typeelement,
                   17111:         $codetitlesref,$codedom,$formname,$fixeddom,$prevphase,
                   17112:         $cnameelement,$cnumelement,$cdomelement,$setroles,
                   17113:         $clonetext,$clonewarning) = @_;
                   17114:     my ($list,$jscript);
                   17115:     my $onchange = 'javascript:updateFilters(this)';
                   17116:     my ($domainselectform,$sincefilterform,$createdfilterform,
                   17117:         $ownerdomselectform,$persondomselectform,$instcodeform,
                   17118:         $typeselectform,$instcodetitle);
                   17119:     if ($formname eq '') {
                   17120:         $formname = $caller;
                   17121:     }
                   17122:     foreach my $item (@{$filterlist}) {
                   17123:         unless (($item eq 'descriptfilter') || ($item eq 'instcodefilter') ||
                   17124:                 ($item eq 'sincefilter') || ($item eq 'createdfilter')) {
                   17125:             if ($item eq 'domainfilter') {
                   17126:                 $filter->{$item} = &LONCAPA::clean_domain($filter->{$item});
                   17127:             } elsif ($item eq 'coursefilter') {
                   17128:                 $filter->{$item} = &LONCAPA::clean_courseid($filter->{$item});
                   17129:             } elsif ($item eq 'ownerfilter') {
                   17130:                 $filter->{$item} = &LONCAPA::clean_username($filter->{$item});
                   17131:             } elsif ($item eq 'ownerdomfilter') {
                   17132:                 $filter->{'ownerdomfilter'} =
                   17133:                     &LONCAPA::clean_domain($filter->{$item});
                   17134:                 $ownerdomselectform = &select_dom_form($filter->{'ownerdomfilter'},
                   17135:                                                        'ownerdomfilter',1);
                   17136:             } elsif ($item eq 'personfilter') {
                   17137:                 $filter->{$item} = &LONCAPA::clean_username($filter->{$item});
                   17138:             } elsif ($item eq 'persondomfilter') {
                   17139:                 $persondomselectform = &select_dom_form($filter->{'persondomfilter'},
                   17140:                                                         'persondomfilter',1);
                   17141:             } else {
                   17142:                 $filter->{$item} =~ s/\W//g;
                   17143:             }
                   17144:             if (!$filter->{$item}) {
                   17145:                 $filter->{$item} = '';
                   17146:             }
                   17147:         }
                   17148:         if ($item eq 'domainfilter') {
                   17149:             my $allow_blank = 1;
                   17150:             if ($formname eq 'portform') {
                   17151:                 $allow_blank=0;
                   17152:             } elsif ($formname eq 'studentform') {
                   17153:                 $allow_blank=0;
                   17154:             }
                   17155:             if ($fixeddom) {
                   17156:                 $domainselectform = '<input type="hidden" name="domainfilter"'.
                   17157:                                     ' value="'.$codedom.'" />'.
                   17158:                                     &Apache::lonnet::domain($codedom,'description');
                   17159:             } else {
                   17160:                 $domainselectform = &select_dom_form($filter->{$item},
                   17161:                                                      'domainfilter',
                   17162:                                                       $allow_blank,'',$onchange);
                   17163:             }
                   17164:         } else {
                   17165:             $list->{$item} = &HTML::Entities::encode($filter->{$item},'<>&"');
                   17166:         }
                   17167:     }
                   17168: 
                   17169:     # last course activity filter and selection
                   17170:     $sincefilterform = &timebased_select_form('sincefilter',$filter);
                   17171: 
                   17172:     # course created filter and selection
                   17173:     if (exists($filter->{'createdfilter'})) {
                   17174:         $createdfilterform = &timebased_select_form('createdfilter',$filter);
                   17175:     }
                   17176: 
                   17177:     my %lt = &Apache::lonlocal::texthash(
                   17178:                 'cac' => "$crstype Activity",
                   17179:                 'ccr' => "$crstype Created",
                   17180:                 'cde' => "$crstype Title",
                   17181:                 'cdo' => "$crstype Domain",
                   17182:                 'ins' => 'Institutional Code',
                   17183:                 'inc' => 'Institutional Categorization',
                   17184:                 'cow' => "$crstype Owner/Co-owner",
                   17185:                 'cop' => "$crstype Personnel Includes",
                   17186:                 'cog' => 'Type',
                   17187:              );
                   17188: 
                   17189:     if (($formname eq 'ccrs') || ($formname eq 'requestcrs')) {
                   17190:         my $typeval = 'Course';
                   17191:         if ($crstype eq 'Community') {
                   17192:             $typeval = 'Community';
                   17193:         }
                   17194:         $typeselectform = '<input type="hidden" name="type" value="'.$typeval.'" />';
                   17195:     } else {
                   17196:         $typeselectform =  '<select name="type" size="1"';
                   17197:         if ($onchange) {
                   17198:             $typeselectform .= ' onchange="'.$onchange.'"';
                   17199:         }
                   17200:         $typeselectform .= '>'."\n";
                   17201:         foreach my $posstype ('Course','Community') {
                   17202:             $typeselectform.='<option value="'.$posstype.'"'.
                   17203:                 ($posstype eq $crstype ? ' selected="selected" ' : ''). ">".&mt($posstype)."</option>\n";
                   17204:         }
                   17205:         $typeselectform.="</select>";
                   17206:     }
                   17207: 
                   17208:     my ($cloneableonlyform,$cloneabletitle);
                   17209:     if (exists($filter->{'cloneableonly'})) {
                   17210:         my $cloneableon = '';
                   17211:         my $cloneableoff = ' checked="checked"';
                   17212:         if ($filter->{'cloneableonly'}) {
                   17213:             $cloneableon = $cloneableoff;
                   17214:             $cloneableoff = '';
                   17215:         }
                   17216:         $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>';
                   17217:         if ($formname eq 'ccrs') {
1.1075.2.71  raeburn  17218:             $cloneabletitle = &mt('Cloneable for [_1]',$cloneruname.':'.$clonerudom);
1.1075.2.69  raeburn  17219:         } else {
                   17220:             $cloneabletitle = &mt('Cloneable by you');
                   17221:         }
                   17222:     }
                   17223:     my $officialjs;
                   17224:     if ($crstype eq 'Course') {
                   17225:         if (exists($filter->{'instcodefilter'})) {
                   17226: #            if (($fixeddom) || ($formname eq 'requestcrs') ||
                   17227: #                ($formname eq 'modifycourse') || ($formname eq 'filterpicker')) {
                   17228:             if ($codedom) {
                   17229:                 $officialjs = 1;
                   17230:                 ($instcodeform,$jscript,$$numtitlesref) =
                   17231:                     &Apache::courseclassifier::instcode_selectors($codedom,'filterpicker',
                   17232:                                                                   $officialjs,$codetitlesref);
                   17233:                 if ($jscript) {
                   17234:                     $jscript = '<script type="text/javascript">'."\n".
                   17235:                                '// <![CDATA['."\n".
                   17236:                                $jscript."\n".
                   17237:                                '// ]]>'."\n".
                   17238:                                '</script>'."\n";
                   17239:                 }
                   17240:             }
                   17241:             if ($instcodeform eq '') {
                   17242:                 $instcodeform =
                   17243:                     '<input type="text" name="instcodefilter" size="10" value="'.
                   17244:                     $list->{'instcodefilter'}.'" />';
                   17245:                 $instcodetitle = $lt{'ins'};
                   17246:             } else {
                   17247:                 $instcodetitle = $lt{'inc'};
                   17248:             }
                   17249:             if ($fixeddom) {
                   17250:                 $instcodetitle .= '<br />('.$codedom.')';
                   17251:             }
                   17252:         }
                   17253:     }
                   17254:     my $output = qq|
                   17255: <form method="post" name="filterpicker" action="$action">
                   17256: <input type="hidden" name="form" value="$formname" />
                   17257: |;
                   17258:     if ($formname eq 'modifycourse') {
                   17259:         $output .= '<input type="hidden" name="phase" value="courselist" />'."\n".
                   17260:                    '<input type="hidden" name="prevphase" value="'.
                   17261:                    $prevphase.'" />'."\n";
1.1075.2.82  raeburn  17262:     } elsif ($formname eq 'quotacheck') {
                   17263:         $output .= qq|
                   17264: <input type="hidden" name="sortby" value="" />
                   17265: <input type="hidden" name="sortorder" value="" />
                   17266: |;
                   17267:     } else {
1.1075.2.69  raeburn  17268:         my $name_input;
                   17269:         if ($cnameelement ne '') {
                   17270:             $name_input = '<input type="hidden" name="cnameelement" value="'.
                   17271:                           $cnameelement.'" />';
                   17272:         }
                   17273:         $output .= qq|
                   17274: <input type="hidden" name="cnumelement" value="$cnumelement" />
                   17275: <input type="hidden" name="cdomelement" value="$cdomelement" />
                   17276: $name_input
                   17277: $roleelement
                   17278: $multelement
                   17279: $typeelement
                   17280: |;
                   17281:         if ($formname eq 'portform') {
                   17282:             $output .= '<input type="hidden" name="setroles" value="'.$setroles.'" />'."\n";
                   17283:         }
                   17284:     }
                   17285:     if ($fixeddom) {
                   17286:         $output .= '<input type="hidden" name="fixeddom" value="'.$fixeddom.'" />'."\n";
                   17287:     }
                   17288:     $output .= "<br />\n".&Apache::lonhtmlcommon::start_pick_box();
                   17289:     if ($sincefilterform) {
                   17290:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cac'})
                   17291:                   .$sincefilterform
                   17292:                   .&Apache::lonhtmlcommon::row_closure();
                   17293:     }
                   17294:     if ($createdfilterform) {
                   17295:         $output .= &Apache::lonhtmlcommon::row_title($lt{'ccr'})
                   17296:                   .$createdfilterform
                   17297:                   .&Apache::lonhtmlcommon::row_closure();
                   17298:     }
                   17299:     if ($domainselectform) {
                   17300:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cdo'})
                   17301:                   .$domainselectform
                   17302:                   .&Apache::lonhtmlcommon::row_closure();
                   17303:     }
                   17304:     if ($typeselectform) {
                   17305:         if (($formname eq 'ccrs') || ($formname eq 'requestcrs')) {
                   17306:             $output .= $typeselectform;
                   17307:         } else {
                   17308:             $output .= &Apache::lonhtmlcommon::row_title($lt{'cog'})
                   17309:                       .$typeselectform
                   17310:                       .&Apache::lonhtmlcommon::row_closure();
                   17311:         }
                   17312:     }
                   17313:     if ($instcodeform) {
                   17314:         $output .= &Apache::lonhtmlcommon::row_title($instcodetitle)
                   17315:                   .$instcodeform
                   17316:                   .&Apache::lonhtmlcommon::row_closure();
                   17317:     }
                   17318:     if (exists($filter->{'ownerfilter'})) {
                   17319:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cow'}).
                   17320:                    '<table><tr><td>'.&mt('Username').'<br />'.
                   17321:                    '<input type="text" name="ownerfilter" size="20" value="'.
                   17322:                    $list->{'ownerfilter'}.'" /></td><td>'.&mt('Domain').'<br />'.
                   17323:                    $ownerdomselectform.'</td></tr></table>'.
                   17324:                    &Apache::lonhtmlcommon::row_closure();
                   17325:     }
                   17326:     if (exists($filter->{'personfilter'})) {
                   17327:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cop'}).
                   17328:                    '<table><tr><td>'.&mt('Username').'<br />'.
                   17329:                    '<input type="text" name="personfilter" size="20" value="'.
                   17330:                    $list->{'personfilter'}.'" /></td><td>'.&mt('Domain').'<br />'.
                   17331:                    $persondomselectform.'</td></tr></table>'.
                   17332:                    &Apache::lonhtmlcommon::row_closure();
                   17333:     }
                   17334:     if (exists($filter->{'coursefilter'})) {
                   17335:         $output .= &Apache::lonhtmlcommon::row_title(&mt('LON-CAPA course ID'))
                   17336:                   .'<input type="text" name="coursefilter" size="25" value="'
                   17337:                   .$list->{'coursefilter'}.'" />'
                   17338:                   .&Apache::lonhtmlcommon::row_closure();
                   17339:     }
                   17340:     if ($cloneableonlyform) {
                   17341:         $output .= &Apache::lonhtmlcommon::row_title($cloneabletitle).
                   17342:                    $cloneableonlyform.&Apache::lonhtmlcommon::row_closure();
                   17343:     }
                   17344:     if (exists($filter->{'descriptfilter'})) {
                   17345:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cde'})
                   17346:                   .'<input type="text" name="descriptfilter" size="40" value="'
                   17347:                   .$list->{'descriptfilter'}.'" />'
                   17348:                   .&Apache::lonhtmlcommon::row_closure(1);
                   17349:     }
                   17350:     $output .= &Apache::lonhtmlcommon::end_pick_box().'<p>'.$clonetext."\n".
                   17351:                '<input type="hidden" name="updater" value="" />'."\n".
                   17352:                '<input type="submit" name="gosearch" value="'.
                   17353:                &mt('Search').'" /></p>'."\n".'</form>'."\n".'<hr />'."\n";
                   17354:     return $jscript.$clonewarning.$output;
                   17355: }
                   17356: 
                   17357: =pod
                   17358: 
                   17359: =item * &timebased_select_form()
                   17360: 
                   17361: Create markup for a dropdown list used to select a time-based
                   17362: filter e.g., Course Activity, Course Created, when searching for courses
                   17363: or communities
                   17364: 
                   17365: Inputs:
                   17366: 
                   17367: item - name of form element (sincefilter or createdfilter)
                   17368: 
                   17369: filter - anonymous hash of criteria and their values
                   17370: 
                   17371: Returns: HTML for a select box contained a blank, then six time selections,
                   17372:          with value set in incoming form variables currently selected.
                   17373: 
                   17374: Side Effects: None
                   17375: 
                   17376: =cut
                   17377: 
                   17378: sub timebased_select_form {
                   17379:     my ($item,$filter) = @_;
                   17380:     if (ref($filter) eq 'HASH') {
                   17381:         $filter->{$item} =~ s/[^\d-]//g;
                   17382:         if (!$filter->{$item}) { $filter->{$item}=-1; }
                   17383:         return &select_form(
                   17384:                             $filter->{$item},
                   17385:                             $item,
                   17386:                             {      '-1' => '',
                   17387:                                 '86400' => &mt('today'),
                   17388:                                '604800' => &mt('last week'),
                   17389:                               '2592000' => &mt('last month'),
                   17390:                               '7776000' => &mt('last three months'),
                   17391:                              '15552000' => &mt('last six months'),
                   17392:                              '31104000' => &mt('last year'),
                   17393:                     'select_form_order' =>
                   17394:                            ['-1','86400','604800','2592000','7776000',
                   17395:                             '15552000','31104000']});
                   17396:     }
                   17397: }
                   17398: 
                   17399: =pod
                   17400: 
                   17401: =item * &js_changer()
                   17402: 
                   17403: Create script tag containing Javascript used to submit course search form
                   17404: when course type or domain is changed, and also to hide 'Searching ...' on
                   17405: page load completion for page showing search result.
                   17406: 
                   17407: Inputs: None
                   17408: 
                   17409: Returns: markup containing updateFilters() and hideSearching() javascript functions.
                   17410: 
                   17411: Side Effects: None
                   17412: 
                   17413: =cut
                   17414: 
                   17415: sub js_changer {
                   17416:     return <<ENDJS;
                   17417: <script type="text/javascript">
                   17418: // <![CDATA[
                   17419: function updateFilters(caller) {
                   17420:     if (typeof(caller) != "undefined") {
                   17421:         document.filterpicker.updater.value = caller.name;
                   17422:     }
                   17423:     document.filterpicker.submit();
                   17424: }
                   17425: 
                   17426: function hideSearching() {
                   17427:     if (document.getElementById('searching')) {
                   17428:         document.getElementById('searching').style.display = 'none';
                   17429:     }
                   17430:     return;
                   17431: }
                   17432: 
                   17433: // ]]>
                   17434: </script>
                   17435: 
                   17436: ENDJS
                   17437: }
                   17438: 
                   17439: =pod
                   17440: 
                   17441: =item * &search_courses()
                   17442: 
                   17443: Process selected filters form course search form and pass to lonnet::courseiddump
                   17444: to retrieve a hash for which keys are courseIDs which match the selected filters.
                   17445: 
                   17446: Inputs:
                   17447: 
                   17448: dom - domain being searched
                   17449: 
                   17450: type - course type ('Course' or 'Community' or '.' if any).
                   17451: 
                   17452: filter - anonymous hash of criteria and their values
                   17453: 
                   17454: numtitles - for institutional codes - number of categories
                   17455: 
                   17456: cloneruname - optional username of new course owner
                   17457: 
                   17458: clonerudom - optional domain of new course owner
                   17459: 
1.1075.2.95  raeburn  17460: domcloner - optional "domcloner" flag; has value=1 if user has ccc priv in domain being filtered by,
1.1075.2.69  raeburn  17461:             (used when DC is using course creation form)
                   17462: 
                   17463: codetitles - reference to array of titles of components in institutional codes (official courses).
                   17464: 
1.1075.2.95  raeburn  17465: cc_clone - escaped comma separated list of courses for which course cloner has active CC role
                   17466:            (and so can clone automatically)
                   17467: 
                   17468: reqcrsdom - domain of new course, where search_courses is used to identify potential courses to clone
                   17469: 
                   17470: reqinstcode - institutional code of new course, where search_courses is used to identify potential
                   17471:               courses to clone
1.1075.2.69  raeburn  17472: 
                   17473: Returns: %courses - hash of courses satisfying search criteria, keys = course IDs, values are corresponding colon-separated escaped description, institutional code, owner and type.
                   17474: 
                   17475: 
                   17476: Side Effects: None
                   17477: 
                   17478: =cut
                   17479: 
                   17480: 
                   17481: sub search_courses {
1.1075.2.95  raeburn  17482:     my ($dom,$type,$filter,$numtitles,$cloneruname,$clonerudom,$domcloner,$codetitles,
                   17483:         $cc_clone,$reqcrsdom,$reqinstcode) = @_;
1.1075.2.69  raeburn  17484:     my (%courses,%showcourses,$cloner);
                   17485:     if (($filter->{'ownerfilter'} ne '') ||
                   17486:         ($filter->{'ownerdomfilter'} ne '')) {
                   17487:         $filter->{'combownerfilter'} = $filter->{'ownerfilter'}.':'.
                   17488:                                        $filter->{'ownerdomfilter'};
                   17489:     }
                   17490:     foreach my $item ('descriptfilter','coursefilter','combownerfilter') {
                   17491:         if (!$filter->{$item}) {
                   17492:             $filter->{$item}='.';
                   17493:         }
                   17494:     }
                   17495:     my $now = time;
                   17496:     my $timefilter =
                   17497:        ($filter->{'sincefilter'}==-1?1:$now-$filter->{'sincefilter'});
                   17498:     my ($createdbefore,$createdafter);
                   17499:     if (($filter->{'createdfilter'} ne '') && ($filter->{'createdfilter'} !=-1)) {
                   17500:         $createdbefore = $now;
                   17501:         $createdafter = $now-$filter->{'createdfilter'};
                   17502:     }
                   17503:     my ($instcodefilter,$regexpok);
                   17504:     if ($numtitles) {
                   17505:         if ($env{'form.official'} eq 'on') {
                   17506:             $instcodefilter =
                   17507:                 &Apache::courseclassifier::instcode_search_str($dom,$numtitles,$codetitles);
                   17508:             $regexpok = 1;
                   17509:         } elsif ($env{'form.official'} eq 'off') {
                   17510:             $instcodefilter = &Apache::courseclassifier::instcode_search_str($dom,$numtitles,$codetitles);
                   17511:             unless ($instcodefilter eq '') {
                   17512:                 $regexpok = -1;
                   17513:             }
                   17514:         }
                   17515:     } else {
                   17516:         $instcodefilter = $filter->{'instcodefilter'};
                   17517:     }
                   17518:     if ($instcodefilter eq '') { $instcodefilter = '.'; }
                   17519:     if ($type eq '') { $type = '.'; }
                   17520: 
                   17521:     if (($clonerudom ne '') && ($cloneruname ne '')) {
                   17522:         $cloner = $cloneruname.':'.$clonerudom;
                   17523:     }
                   17524:     %courses = &Apache::lonnet::courseiddump($dom,
                   17525:                                              $filter->{'descriptfilter'},
                   17526:                                              $timefilter,
                   17527:                                              $instcodefilter,
                   17528:                                              $filter->{'combownerfilter'},
                   17529:                                              $filter->{'coursefilter'},
                   17530:                                              undef,undef,$type,$regexpok,undef,undef,
1.1075.2.95  raeburn  17531:                                              undef,undef,$cloner,$cc_clone,
1.1075.2.69  raeburn  17532:                                              $filter->{'cloneableonly'},
                   17533:                                              $createdbefore,$createdafter,undef,
1.1075.2.95  raeburn  17534:                                              $domcloner,undef,$reqcrsdom,$reqinstcode);
1.1075.2.69  raeburn  17535:     if (($filter->{'personfilter'} ne '') && ($filter->{'persondomfilter'} ne '')) {
                   17536:         my $ccrole;
                   17537:         if ($type eq 'Community') {
                   17538:             $ccrole = 'co';
                   17539:         } else {
                   17540:             $ccrole = 'cc';
                   17541:         }
                   17542:         my %rolehash = &Apache::lonnet::get_my_roles($filter->{'personfilter'},
                   17543:                                                      $filter->{'persondomfilter'},
                   17544:                                                      'userroles',undef,
                   17545:                                                      [$ccrole,'in','ad','ep','ta','cr'],
                   17546:                                                      $dom);
                   17547:         foreach my $role (keys(%rolehash)) {
                   17548:             my ($cnum,$cdom,$courserole) = split(':',$role);
                   17549:             my $cid = $cdom.'_'.$cnum;
                   17550:             if (exists($courses{$cid})) {
                   17551:                 if (ref($courses{$cid}) eq 'HASH') {
                   17552:                     if (ref($courses{$cid}{roles}) eq 'ARRAY') {
                   17553:                         if (!grep(/^\Q$courserole\E$/,@{$courses{$cid}{roles}})) {
1.1075.2.119  raeburn  17554:                             push(@{$courses{$cid}{roles}},$courserole);
1.1075.2.69  raeburn  17555:                         }
                   17556:                     } else {
                   17557:                         $courses{$cid}{roles} = [$courserole];
                   17558:                     }
                   17559:                     $showcourses{$cid} = $courses{$cid};
                   17560:                 }
                   17561:             }
                   17562:         }
                   17563:         %courses = %showcourses;
                   17564:     }
                   17565:     return %courses;
                   17566: }
                   17567: 
                   17568: =pod
                   17569: 
                   17570: =back
                   17571: 
1.1075.2.88  raeburn  17572: =head1 Routines for version requirements for current course.
                   17573: 
                   17574: =over 4
                   17575: 
                   17576: =item * &check_release_required()
                   17577: 
                   17578: Compares required LON-CAPA version with version on server, and
                   17579: if required version is newer looks for a server with the required version.
                   17580: 
                   17581: Looks first at servers in user's owen domain; if none suitable, looks at
                   17582: servers in course's domain are permitted to host sessions for user's domain.
                   17583: 
                   17584: Inputs:
                   17585: 
                   17586: $loncaparev - Version on current server (format: Major.Minor.Subrelease-datestamp)
                   17587: 
                   17588: $courseid - Course ID of current course
                   17589: 
                   17590: $rolecode - User's current role in course (for switchserver query string).
                   17591: 
                   17592: $required - LON-CAPA version needed by course (format: Major.Minor).
                   17593: 
                   17594: 
                   17595: Returns:
                   17596: 
                   17597: $switchserver - query string tp append to /adm/switchserver call (if
                   17598:                 current server's LON-CAPA version is too old.
                   17599: 
                   17600: $warning - Message is displayed if no suitable server could be found.
                   17601: 
                   17602: =cut
                   17603: 
                   17604: sub check_release_required {
                   17605:     my ($loncaparev,$courseid,$rolecode,$required) = @_;
                   17606:     my ($switchserver,$warning);
                   17607:     if ($required ne '') {
                   17608:         my ($reqdmajor,$reqdminor) = ($required =~ /^(\d+)\.(\d+)$/);
                   17609:         my ($major,$minor) = ($loncaparev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
                   17610:         if ($reqdmajor ne '' && $reqdminor ne '') {
                   17611:             my $otherserver;
                   17612:             if (($major eq '' && $minor eq '') ||
                   17613:                 (($reqdmajor > $major) || (($reqdmajor == $major) && ($reqdminor > $minor)))) {
                   17614:                 my ($userdomserver) = &Apache::lonnet::choose_server($env{'user.domain'},undef,$required,1);
                   17615:                 my $switchlcrev =
                   17616:                     &Apache::lonnet::get_server_loncaparev($env{'user.domain'},
                   17617:                                                            $userdomserver);
                   17618:                 my ($swmajor,$swminor) = ($switchlcrev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
                   17619:                 if (($swmajor eq '' && $swminor eq '') || ($reqdmajor > $swmajor) ||
                   17620:                     (($reqdmajor == $swmajor) && ($reqdminor > $swminor))) {
                   17621:                     my $cdom = $env{'course.'.$courseid.'.domain'};
                   17622:                     if ($cdom ne $env{'user.domain'}) {
                   17623:                         my ($coursedomserver,$coursehostname) = &Apache::lonnet::choose_server($cdom,undef,$required,1);
                   17624:                         my $serverhomeID = &Apache::lonnet::get_server_homeID($coursehostname);
                   17625:                         my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
                   17626:                         my %defdomdefaults = &Apache::lonnet::get_domain_defaults($serverhomedom);
                   17627:                         my %udomdefaults = &Apache::lonnet::get_domain_defaults($env{'user.domain'});
                   17628:                         my $remoterev = &Apache::lonnet::get_server_loncaparev($serverhomedom,$coursedomserver);
                   17629:                         my $canhost =
                   17630:                             &Apache::lonnet::can_host_session($env{'user.domain'},
                   17631:                                                               $coursedomserver,
                   17632:                                                               $remoterev,
                   17633:                                                               $udomdefaults{'remotesessions'},
                   17634:                                                               $defdomdefaults{'hostedsessions'});
                   17635: 
                   17636:                         if ($canhost) {
                   17637:                             $otherserver = $coursedomserver;
                   17638:                         } else {
                   17639:                             $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.");
                   17640:                         }
                   17641:                     } else {
                   17642:                         $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).");
                   17643:                     }
                   17644:                 } else {
                   17645:                     $otherserver = $userdomserver;
                   17646:                 }
                   17647:             }
                   17648:             if ($otherserver ne '') {
                   17649:                 $switchserver = 'otherserver='.$otherserver.'&amp;role='.$rolecode;
                   17650:             }
                   17651:         }
                   17652:     }
                   17653:     return ($switchserver,$warning);
                   17654: }
                   17655: 
                   17656: =pod
                   17657: 
                   17658: =item * &check_release_result()
                   17659: 
                   17660: Inputs:
                   17661: 
                   17662: $switchwarning - Warning message if no suitable server found to host session.
                   17663: 
                   17664: $switchserver - query string to append to /adm/switchserver containing lonHostID
                   17665:                 and current role.
                   17666: 
                   17667: Returns: HTML to display with information about requirement to switch server.
                   17668:          Either displaying warning with link to Roles/Courses screen or
                   17669:          display link to switchserver.
                   17670: 
1.1075.2.69  raeburn  17671: =cut
                   17672: 
1.1075.2.88  raeburn  17673: sub check_release_result {
                   17674:     my ($switchwarning,$switchserver) = @_;
                   17675:     my $output = &start_page('Selected course unavailable on this server').
                   17676:                  '<p class="LC_warning">';
                   17677:     if ($switchwarning) {
                   17678:         $output .= $switchwarning.'<br /><a href="/adm/roles">';
                   17679:         if (&show_course()) {
                   17680:             $output .= &mt('Display courses');
                   17681:         } else {
                   17682:             $output .= &mt('Display roles');
                   17683:         }
                   17684:         $output .= '</a>';
                   17685:     } elsif ($switchserver) {
                   17686:         $output .= &mt('This course requires a newer version of LON-CAPA than is installed on this server.').
                   17687:                    '<br />'.
                   17688:                    '<a href="/adm/switchserver?'.$switchserver.'">'.
                   17689:                    &mt('Switch Server').
                   17690:                    '</a>';
                   17691:     }
                   17692:     $output .= '</p>'.&end_page();
                   17693:     return $output;
                   17694: }
                   17695: 
                   17696: =pod
                   17697: 
                   17698: =item * &needs_coursereinit()
                   17699: 
                   17700: Determine if course contents stored for user's session needs to be
                   17701: refreshed, because content has changed since "Big Hash" last tied.
                   17702: 
                   17703: Check for change is made if time last checked is more than 10 minutes ago
                   17704: (by default).
                   17705: 
                   17706: Inputs:
                   17707: 
                   17708: $loncaparev - Version on current server (format: Major.Minor.Subrelease-datestamp)
                   17709: 
                   17710: $interval (optional) - Time which may elapse (in s) between last check for content
                   17711:                        change in current course. (default: 600 s).
                   17712: 
                   17713: Returns: an array; first element is:
                   17714: 
                   17715: =over 4
                   17716: 
                   17717: 'switch' - if content updates mean user's session
                   17718:            needs to be switched to a server running a newer LON-CAPA version
                   17719: 
                   17720: 'update' - if course session needs to be refreshed (i.e., Big Hash needs to be reloaded)
                   17721:            on current server hosting user's session
                   17722: 
                   17723: ''       - if no action required.
                   17724: 
                   17725: =back
                   17726: 
                   17727: If first item element is 'switch':
                   17728: 
                   17729: second item is $switchwarning - Warning message if no suitable server found to host session.
                   17730: 
                   17731: third item is $switchserver - query string to append to /adm/switchserver containing lonHostID
                   17732:                               and current role.
                   17733: 
                   17734: otherwise: no other elements returned.
                   17735: 
                   17736: =back
                   17737: 
                   17738: =cut
                   17739: 
                   17740: sub needs_coursereinit {
                   17741:     my ($loncaparev,$interval) = @_;
                   17742:     return() unless ($env{'request.course.id'} && $env{'request.course.tied'});
                   17743:     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   17744:     my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
                   17745:     my $now = time;
                   17746:     if ($interval eq '') {
                   17747:         $interval = 600;
                   17748:     }
                   17749:     if (($now-$env{'request.course.timechecked'})>$interval) {
                   17750:         &Apache::lonnet::appenv({'request.course.timechecked'=>$now});
1.1075.2.161.  .4(raebu 17751:22):         my $blocked = &blocking_status('reinit',undef,$cnum,$cdom,undef,1);
          .1(raebu 17752:21):         if ($blocked) {
                   17753:21):             return ();
                   17754:21):         }
          .13(raeb 17755:-23):         my $update;
                   17756:-23):         my $lastmainchange = &Apache::lonnet::get_coursechange($cdom,$cnum);
                   17757:-23):         my $lastsuppchange = &Apache::lonnet::get_suppchange($cdom,$cnum);
                   17758:-23):         if ($lastmainchange > $env{'request.course.tied'}) {
                   17759:-23):             my ($needswitch,$switchwarning,$switchserver) = &switch_for_update($loncaparev,$cdom,$cnum);
                   17760:-23):             if ($needswitch) {
                   17761:-23):                 return ('switch',$switchwarning,$switchserver);
                   17762:-23):             }
                   17763:-23):             $update = 'main';
                   17764:-23):         }
                   17765:-23):         if ($lastsuppchange > $env{'request.course.suppupdated'}) {
                   17766:-23):             if ($update) {
                   17767:-23):                 $update = 'both';
                   17768:-23):             } else {
                   17769:-23):                 my ($needswitch,$switchwarning,$switchserver) = &switch_for_update($loncaparev,$cdom,$cnum);
                   17770:-23):                 if ($needswitch) {
                   17771:-23):                     return ('switch',$switchwarning,$switchserver);
                   17772:-23):                 } else {
                   17773:-23):                     $update = 'supp';
1.1075.2.88  raeburn  17774:                 }
                   17775:             }
1.1075.2.161.  .13(raeb 17776:-23):             return ($update);
                   17777:-23):         }
                   17778:-23):     }
                   17779:-23):     return ();
                   17780:-23): }
                   17781:-23): 
                   17782:-23): sub switch_for_update {
                   17783:-23):     my ($loncaparev,$cdom,$cnum) = @_;
                   17784:-23):     my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
                   17785:-23):     if ($curr_reqd_hash{'internal.releaserequired'} ne '') {
                   17786:-23):         my $required = $env{'course.'.$cdom.'_'.$cnum.'.internal.releaserequired'};
                   17787:-23):         if ($curr_reqd_hash{'internal.releaserequired'} ne $required) {
                   17788:-23):             &Apache::lonnet::appenv({'course.'.$cdom.'_'.$cnum.'.internal.releaserequired' =>
                   17789:-23):                                     $curr_reqd_hash{'internal.releaserequired'}});
                   17790:-23):             my ($switchserver,$switchwarning) =
                   17791:-23):                 &check_release_required($loncaparev,$cdom.'_'.$cnum,$env{'request.role'},
                   17792:-23):                                         $curr_reqd_hash{'internal.releaserequired'});
                   17793:-23):             if ($switchwarning ne '' || $switchserver ne '') {
                   17794:-23):                 return ('switch',$switchwarning,$switchserver);
                   17795:-23):             }
1.1075.2.88  raeburn  17796:         }
                   17797:     }
                   17798:     return ();
                   17799: }
1.1075.2.69  raeburn  17800: 
1.1075.2.11  raeburn  17801: sub update_content_constraints {
                   17802:     my ($cdom,$cnum,$chome,$cid) = @_;
                   17803:     my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
                   17804:     my ($reqdmajor,$reqdminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'});
                   17805:     my %checkresponsetypes;
                   17806:     foreach my $key (keys(%Apache::lonnet::needsrelease)) {
                   17807:         my ($item,$name,$value) = split(/:/,$key);
                   17808:         if ($item eq 'resourcetag') {
                   17809:             if ($name eq 'responsetype') {
                   17810:                 $checkresponsetypes{$value} = $Apache::lonnet::needsrelease{$key}
                   17811:             }
                   17812:         }
                   17813:     }
                   17814:     my $navmap = Apache::lonnavmaps::navmap->new();
                   17815:     if (defined($navmap)) {
                   17816:         my %allresponses;
                   17817:         foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_problem() },1,0)) {
                   17818:             my %responses = $res->responseTypes();
                   17819:             foreach my $key (keys(%responses)) {
                   17820:                 next unless(exists($checkresponsetypes{$key}));
                   17821:                 $allresponses{$key} += $responses{$key};
                   17822:             }
                   17823:         }
                   17824:         foreach my $key (keys(%allresponses)) {
                   17825:             my ($major,$minor) = split(/\./,$checkresponsetypes{$key});
                   17826:             if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
                   17827:                 ($reqdmajor,$reqdminor) = ($major,$minor);
                   17828:             }
                   17829:         }
                   17830:         undef($navmap);
                   17831:     }
                   17832:     unless (($reqdmajor eq '') && ($reqdminor eq '')) {
                   17833:         &Apache::lonnet::update_released_required($reqdmajor.'.'.$reqdminor,$cdom,$cnum,$chome,$cid);
                   17834:     }
                   17835:     return;
                   17836: }
                   17837: 
1.1075.2.27  raeburn  17838: sub allmaps_incourse {
                   17839:     my ($cdom,$cnum,$chome,$cid) = @_;
                   17840:     if ($cdom eq '' || $cnum eq '' || $chome eq '' || $cid eq '') {
                   17841:         $cid = $env{'request.course.id'};
                   17842:         $cdom = $env{'course.'.$cid.'.domain'};
                   17843:         $cnum = $env{'course.'.$cid.'.num'};
                   17844:         $chome = $env{'course.'.$cid.'.home'};
                   17845:     }
                   17846:     my %allmaps = ();
                   17847:     my $lastchange =
                   17848:         &Apache::lonnet::get_coursechange($cdom,$cnum);
                   17849:     if ($lastchange > $env{'request.course.tied'}) {
                   17850:         my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum");
                   17851:         unless ($ferr) {
                   17852:             &update_content_constraints($cdom,$cnum,$chome,$cid);
                   17853:         }
                   17854:     }
                   17855:     my $navmap = Apache::lonnavmaps::navmap->new();
                   17856:     if (defined($navmap)) {
                   17857:         foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_map() },1,0,1)) {
                   17858:             $allmaps{$res->src()} = 1;
                   17859:         }
                   17860:     }
                   17861:     return \%allmaps;
                   17862: }
                   17863: 
1.1075.2.11  raeburn  17864: sub parse_supplemental_title {
                   17865:     my ($title) = @_;
                   17866: 
                   17867:     my ($foldertitle,$renametitle);
                   17868:     if ($title =~ /&amp;&amp;&amp;/) {
                   17869:         $title = &HTML::Entites::decode($title);
                   17870:     }
                   17871:     if ($title =~ m/^(\d+)___&&&___($match_username)___&&&___($match_domain)___&&&___(.*)$/) {
                   17872:         $renametitle=$4;
                   17873:         my ($time,$uname,$udom) = ($1,$2,$3);
                   17874:         $foldertitle=&Apache::lontexconvert::msgtexconverted($4);
                   17875:         my $name =  &plainname($uname,$udom);
                   17876:         $name = &HTML::Entities::encode($name,'"<>&\'');
                   17877:         $renametitle = &HTML::Entities::encode($renametitle,'"<>&\'');
1.1075.2.161.  .16(raeb 17878:-23):         $title='<i>'.&Apache::lonlocal::locallocaltime($time).'</i> '.$name;
                   17879:-23):         if ($foldertitle ne '') {
                   17880:-23):             $title .= ': <br />'.$foldertitle;
                   17881:-23):         }
1.1075.2.11  raeburn  17882:     }
                   17883:     if (wantarray) {
                   17884:         return ($title,$foldertitle,$renametitle);
                   17885:     }
                   17886:     return $title;
                   17887: }
                   17888: 
1.1075.2.161.  .13(raeb 17889:-23): sub get_supplemental {
                   17890:-23):     my ($cnum,$cdom,$ignorecache,$possdel)=@_;
                   17891:-23):     my $hashid=$cnum.':'.$cdom;
                   17892:-23):     my ($supplemental,$cached,$set_httprefs);
                   17893:-23):     unless ($ignorecache) {
                   17894:-23):         ($supplemental,$cached) = &Apache::lonnet::is_cached_new('supplemental',$hashid);
                   17895:-23):     }
                   17896:-23):     unless (defined($cached)) {
                   17897:-23):         my $chome=&Apache::lonnet::homeserver($cnum,$cdom);
                   17898:-23):         unless ($chome eq 'no_host') {
                   17899:-23):             my @order = @LONCAPA::map::order;
                   17900:-23):             my @resources = @LONCAPA::map::resources;
                   17901:-23):             my @resparms = @LONCAPA::map::resparms;
                   17902:-23):             my @zombies = @LONCAPA::map::zombies;
                   17903:-23):             my ($errors,%ids,%hidden);
                   17904:-23):             $errors =
                   17905:-23):                 &recurse_supplemental($cnum,$cdom,'supplemental.sequence',
                   17906:-23):                                       $errors,$possdel,\%ids,\%hidden);
                   17907:-23):             @LONCAPA::map::order = @order;
                   17908:-23):             @LONCAPA::map::resources = @resources;
                   17909:-23):             @LONCAPA::map::resparms = @resparms;
                   17910:-23):             @LONCAPA::map::zombies = @zombies;
                   17911:-23):             $set_httprefs = 1;
                   17912:-23):             if ($env{'request.course.id'} eq $cdom.'_'.$cnum) {
                   17913:-23):                 &Apache::lonnet::appenv({'request.course.suppupdated' => time});
                   17914:-23):             }
                   17915:-23):             $supplemental = {
                   17916:-23):                                ids => \%ids,
                   17917:-23):                                hidden => \%hidden,
                   17918:-23):                             };
                   17919:-23):             &Apache::lonnet::do_cache_new('supplemental',$hashid,$supplemental,600);
                   17920:-23):         }
                   17921:-23):     }
                   17922:-23):     return ($supplemental,$set_httprefs);
                   17923:-23): }
                   17924:-23): 
1.1075.2.43  raeburn  17925: sub recurse_supplemental {
1.1075.2.161.  .13(raeb 17926:-23):     my ($cnum,$cdom,$suppmap,$errors,$possdel,$suppids,$hiddensupp,$hidden) = @_;
                   17927:-23):     if (($suppmap) && (ref($suppids) eq 'HASH') && (ref($hiddensupp) eq 'HASH')) {
                   17928:-23):         my $mapnum;
                   17929:-23):         if ($suppmap eq 'supplemental.sequence') {
                   17930:-23):             $mapnum = 0;
                   17931:-23):         } else {
                   17932:-23):             ($mapnum) = ($suppmap =~ /^supplemental_(\d+)\.sequence$/);
                   17933:-23):         }
1.1075.2.43  raeburn  17934:         my ($errtext,$fatal) = &LONCAPA::map::mapread('/uploaded/'.$cdom.'/'.$cnum.'/'.$suppmap);
                   17935:         if ($fatal) {
                   17936:             $errors ++;
                   17937:         } else {
1.1075.2.161.  .13(raeb 17938:-23):             my @order = @LONCAPA::map::order;
                   17939:-23):             if (@order > 0) {
                   17940:-23):                 my @resources = @LONCAPA::map::resources;
                   17941:-23):                 my @resparms = @LONCAPA::map::resparms;
                   17942:-23):                 foreach my $idx (@order) {
                   17943:-23):                     my ($title,$src,$ext,$type,$status)=split(/\:/,$resources[$idx]);
1.1075.2.43  raeburn  17944:                     if (($src ne '') && ($status eq 'res')) {
1.1075.2.161.  .13(raeb 17945:-23):                         my $id = $mapnum.':'.$idx;
                   17946:-23):                         push(@{$suppids->{$src}},$id);
                   17947:-23):                         if (($hidden) || (&get_supp_parameter($resparms[$idx],'parameter_hiddenresource') =~ /^yes/i)) {
                   17948:-23):                             $hiddensupp->{$id} = 1;
                   17949:-23):                         }
1.1075.2.46  raeburn  17950:                         if ($src =~ m{^\Q/uploaded/$cdom/$cnum/\E(supplemental_\d+\.sequence)$}) {
1.1075.2.161.  .13(raeb 17951:-23):                             $errors = &recurse_supplemental($cnum,$cdom,$1,$errors,$possdel,$suppids,
                   17952:-23):                                                             $hiddensupp,$hiddensupp->{$id});
1.1075.2.43  raeburn  17953:                         } else {
1.1075.2.161.  .13(raeb 17954:-23):                             my $allowed;
                   17955:-23):                             if (($env{'request.role.adv'}) || (!$hiddensupp->{$id})) {
                   17956:-23):                                 $allowed = 1;
                   17957:-23):                             } elsif ($possdel) {
                   17958:-23):                                 foreach my $item (@{$suppids->{$src}}) {
                   17959:-23):                                     next if ($item eq $id);
                   17960:-23):                                     unless ($hiddensupp->{$item}) {
                   17961:-23):                                        $allowed = 1;
                   17962:-23):                                        last;
                   17963:-23):                                     }
                   17964:-23):                                 }
                   17965:-23):                                 if ((!$allowed) && (exists($env{'httpref.'.$src}))) {
                   17966:-23):                                     &Apache::lonnet::delenv('httpref.'.$src);
                   17967:-23):                                 }
                   17968:-23):                             }
                   17969:-23):                             if ($allowed && (!exists($env{'httpref.'.$src}))) {
                   17970:-23):                                 &Apache::lonnet::allowuploaded('/adm/coursedoc',$src);
                   17971:-23):                             }
                   17972:-23):                         }
                   17973:-23):                     }
                   17974:-23):                 }
                   17975:-23):             }
                   17976:-23):         }
                   17977:-23):     }
                   17978:-23):     return $errors;
                   17979:-23): }
                   17980:-23): 
                   17981:-23): sub set_supp_httprefs {
                   17982:-23):     my ($cnum,$cdom,$supplemental,$possdel) = @_;
                   17983:-23):     if (ref($supplemental) eq 'HASH') {
                   17984:-23):         if ((ref($supplemental->{'ids'}) eq 'HASH') && (ref($supplemental->{'hidden'}) eq 'HASH')) {
                   17985:-23):             foreach my $src (keys(%{$supplemental->{'ids'}})) {
                   17986:-23):                 next if ($src =~ /\.sequence$/);
                   17987:-23):                 if (ref($supplemental->{'ids'}->{$src}) eq 'ARRAY') {
                   17988:-23):                     my $allowed;
                   17989:-23):                     if ($env{'request.role.adv'}) {
                   17990:-23):                         $allowed = 1;
                   17991:-23):                     } else {
                   17992:-23):                         foreach my $id (@{$supplemental->{'ids'}->{$src}}) {
                   17993:-23):                             unless ($supplemental->{'hidden'}->{$id}) {
                   17994:-23):                                 $allowed = 1;
                   17995:-23):                                 last;
                   17996:-23):                             }
                   17997:-23):                         }
                   17998:-23):                     }
                   17999:-23):                     if (exists($env{'httpref.'.$src})) {
                   18000:-23):                         if ($possdel) {
                   18001:-23):                             unless ($allowed) {
                   18002:-23):                                 &Apache::lonnet::delenv('httpref.'.$src);
                   18003:-23):                             }
1.1075.2.43  raeburn  18004:                         }
1.1075.2.161.  .13(raeb 18005:-23):                     } elsif ($allowed) {
                   18006:-23):                         &Apache::lonnet::allowuploaded('/adm/coursedoc',$src);
1.1075.2.43  raeburn  18007:                     }
                   18008:                 }
                   18009:             }
1.1075.2.161.  .13(raeb 18010:-23):             if ($env{'request.course.id'} eq $cdom.'_'.$cnum) {
                   18011:-23):                 &Apache::lonnet::appenv({'request.course.suppupdated' => time});
                   18012:-23):             }
1.1075.2.43  raeburn  18013:         }
                   18014:     }
1.1075.2.161.  .13(raeb 18015:-23): }
                   18016:-23): 
                   18017:-23): sub get_supp_parameter {
                   18018:-23):     my ($resparm,$name)=@_;
                   18019:-23):     return if ($resparm eq '');
                   18020:-23):     my $value=undef;
                   18021:-23):     my $ptype=undef;
                   18022:-23):     foreach (split('&&&',$resparm)) {
                   18023:-23):         my ($thistype,$thisname,$thisvalue)=split('___',$_);
                   18024:-23):         if ($thisname eq $name) {
                   18025:-23):             $value=$thisvalue;
                   18026:-23):             $ptype=$thistype;
                   18027:-23):         }
                   18028:-23):     }
                   18029:-23):     return $value;
1.1075.2.43  raeburn  18030: }
                   18031: 
1.1075.2.18  raeburn  18032: sub symb_to_docspath {
1.1075.2.119  raeburn  18033:     my ($symb,$navmapref) = @_;
                   18034:     return unless ($symb && ref($navmapref));
1.1075.2.18  raeburn  18035:     my ($mapurl,$id,$resurl) = &Apache::lonnet::decode_symb($symb);
                   18036:     if ($resurl=~/\.(sequence|page)$/) {
                   18037:         $mapurl=$resurl;
                   18038:     } elsif ($resurl eq 'adm/navmaps') {
                   18039:         $mapurl=$env{'course.'.$env{'request.course.id'}.'.url'};
                   18040:     }
                   18041:     my $mapresobj;
1.1075.2.119  raeburn  18042:     unless (ref($$navmapref)) {
                   18043:         $$navmapref = Apache::lonnavmaps::navmap->new();
                   18044:     }
                   18045:     if (ref($$navmapref)) {
                   18046:         $mapresobj = $$navmapref->getResourceByUrl($mapurl);
1.1075.2.18  raeburn  18047:     }
                   18048:     $mapurl=~s{^.*/([^/]+)\.(\w+)$}{$1};
                   18049:     my $type=$2;
                   18050:     my $path;
                   18051:     if (ref($mapresobj)) {
                   18052:         my $pcslist = $mapresobj->map_hierarchy();
                   18053:         if ($pcslist ne '') {
                   18054:             foreach my $pc (split(/,/,$pcslist)) {
                   18055:                 next if ($pc <= 1);
1.1075.2.119  raeburn  18056:                 my $res = $$navmapref->getByMapPc($pc);
1.1075.2.18  raeburn  18057:                 if (ref($res)) {
                   18058:                     my $thisurl = $res->src();
                   18059:                     $thisurl=~s{^.*/([^/]+)\.\w+$}{$1};
                   18060:                     my $thistitle = $res->title();
                   18061:                     $path .= '&'.
                   18062:                              &Apache::lonhtmlcommon::entity_encode($thisurl).'&'.
1.1075.2.46  raeburn  18063:                              &escape($thistitle).
1.1075.2.18  raeburn  18064:                              ':'.$res->randompick().
                   18065:                              ':'.$res->randomout().
                   18066:                              ':'.$res->encrypted().
                   18067:                              ':'.$res->randomorder().
                   18068:                              ':'.$res->is_page();
                   18069:                 }
                   18070:             }
                   18071:         }
                   18072:         $path =~ s/^\&//;
                   18073:         my $maptitle = $mapresobj->title();
                   18074:         if ($mapurl eq 'default') {
1.1075.2.38  raeburn  18075:             $maptitle = 'Main Content';
1.1075.2.18  raeburn  18076:         }
                   18077:         $path .= (($path ne '')? '&' : '').
                   18078:                  &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
1.1075.2.46  raeburn  18079:                  &escape($maptitle).
1.1075.2.18  raeburn  18080:                  ':'.$mapresobj->randompick().
                   18081:                  ':'.$mapresobj->randomout().
                   18082:                  ':'.$mapresobj->encrypted().
                   18083:                  ':'.$mapresobj->randomorder().
                   18084:                  ':'.$mapresobj->is_page();
                   18085:     } else {
                   18086:         my $maptitle = &Apache::lonnet::gettitle($mapurl);
                   18087:         my $ispage = (($type eq 'page')? 1 : '');
                   18088:         if ($mapurl eq 'default') {
1.1075.2.38  raeburn  18089:             $maptitle = 'Main Content';
1.1075.2.18  raeburn  18090:         }
                   18091:         $path = &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
1.1075.2.46  raeburn  18092:                 &escape($maptitle).':::::'.$ispage;
1.1075.2.18  raeburn  18093:     }
                   18094:     unless ($mapurl eq 'default') {
                   18095:         $path = 'default&'.
1.1075.2.46  raeburn  18096:                 &escape('Main Content').
1.1075.2.18  raeburn  18097:                 ':::::&'.$path;
                   18098:     }
                   18099:     return $path;
                   18100: }
                   18101: 
1.1075.2.161.  .13(raeb 18102:-23): sub validate_folderpath {
                   18103:-23):     my ($supplementalflag,$allowed,$coursenum,$coursedom) = @_;
                   18104:-23):     if ($env{'form.folderpath'} ne '') {
                   18105:-23):         my @items = split(/\&/,$env{'form.folderpath'});
                   18106:-23):         my ($badpath,$changed,$got_supp,$supppath,%supphidden,%suppids);
                   18107:-23):         for (my $i=0; $i<@items; $i++) {
                   18108:-23):             my $odd = $i%2;
                   18109:-23):             if (($odd) && (!$supplementalflag) && ($items[$i] !~ /^[^:]*:(|\d+):(|1):(|1):(|1):(|1)$/)) {
                   18110:-23):                 $badpath = 1;
                   18111:-23):             } elsif ($odd && $supplementalflag) {
                   18112:-23):                 my $idx = $i-1;
                   18113:-23):                 if ($items[$i] =~ /^([^:]*)::(|1):::$/) {
                   18114:-23):                     my $esc_name = $1;
                   18115:-23):                     if ((!$allowed) || ($items[$idx] eq 'supplemental')) {
                   18116:-23):                         $supppath .= '&'.$esc_name;
                   18117:-23):                         $changed = 1;
                   18118:-23):                     } else {
                   18119:-23):                         $supppath .= '&'.$items[$i];
                   18120:-23):                     }
                   18121:-23):                 } elsif (($allowed) && ($items[$idx] ne 'supplemental')) {
                   18122:-23):                     $changed = 1;
                   18123:-23):                     my $is_hidden;
                   18124:-23):                     unless ($got_supp) {
                   18125:-23):                         my ($supplemental) = &get_supplemental($coursenum,$coursedom);
                   18126:-23):                         if (ref($supplemental) eq 'HASH') {
                   18127:-23):                             if (ref($supplemental->{'hidden'}) eq 'HASH') {
                   18128:-23):                                 %supphidden = %{$supplemental->{'hidden'}};
                   18129:-23):                             }
                   18130:-23):                             if (ref($supplemental->{'ids'}) eq 'HASH') {
                   18131:-23):                                 %suppids = %{$supplemental->{'ids'}};
                   18132:-23):                             }
                   18133:-23):                         }
                   18134:-23):                         $got_supp = 1;
                   18135:-23):                     }
                   18136:-23):                     if (ref($suppids{"/uploaded/$coursedom/$coursenum/$items[$idx].sequence"}) eq 'ARRAY') {
                   18137:-23):                         my $mapid = $suppids{"/uploaded/$coursedom/$coursenum/$items[$idx].sequence"}->[0];
                   18138:-23):                         if ($supphidden{$mapid}) {
                   18139:-23):                             $is_hidden = 1;
                   18140:-23):                         }
                   18141:-23):                     }
                   18142:-23):                     $supppath .= '&'.$items[$i].'::'.$is_hidden.':::';
                   18143:-23):                 } else {
                   18144:-23):                     $supppath .= '&'.$items[$i];
                   18145:-23):                 }
                   18146:-23):             } elsif ((!$odd) && ($items[$i] !~ /^(default|supplemental)(|_\d+)$/)) {
                   18147:-23):                 $badpath = 1;
                   18148:-23):             } elsif ($supplementalflag) {
                   18149:-23):                 $supppath .= '&'.$items[$i];
                   18150:-23):             }
                   18151:-23):             last if ($badpath);
                   18152:-23):         }
                   18153:-23):         if ($badpath) {
                   18154:-23):             delete($env{'form.folderpath'});
                   18155:-23):         } elsif ($changed && $supplementalflag) {
                   18156:-23):             $supppath =~ s/^\&//;
                   18157:-23):             $env{'form.folderpath'} = $supppath;
                   18158:-23):         }
                   18159:-23):     }
                   18160:-23):     return;
                   18161:-23): }
                   18162:-23): 
1.1075.2.14  raeburn  18163: sub captcha_display {
1.1075.2.137  raeburn  18164:     my ($context,$lonhost,$defdom) = @_;
1.1075.2.14  raeburn  18165:     my ($output,$error);
1.1075.2.107  raeburn  18166:     my ($captcha,$pubkey,$privkey,$version) =
1.1075.2.137  raeburn  18167:         &get_captcha_config($context,$lonhost,$defdom);
1.1075.2.14  raeburn  18168:     if ($captcha eq 'original') {
                   18169:         $output = &create_captcha();
                   18170:         unless ($output) {
                   18171:             $error = 'captcha';
                   18172:         }
                   18173:     } elsif ($captcha eq 'recaptcha') {
1.1075.2.107  raeburn  18174:         $output = &create_recaptcha($pubkey,$version);
1.1075.2.14  raeburn  18175:         unless ($output) {
                   18176:             $error = 'recaptcha';
                   18177:         }
                   18178:     }
1.1075.2.107  raeburn  18179:     return ($output,$error,$captcha,$version);
1.1075.2.14  raeburn  18180: }
                   18181: 
                   18182: sub captcha_response {
1.1075.2.137  raeburn  18183:     my ($context,$lonhost,$defdom) = @_;
1.1075.2.14  raeburn  18184:     my ($captcha_chk,$captcha_error);
1.1075.2.137  raeburn  18185:     my ($captcha,$pubkey,$privkey,$version) = &get_captcha_config($context,$lonhost,$defdom);
1.1075.2.14  raeburn  18186:     if ($captcha eq 'original') {
                   18187:         ($captcha_chk,$captcha_error) = &check_captcha();
                   18188:     } elsif ($captcha eq 'recaptcha') {
1.1075.2.107  raeburn  18189:         $captcha_chk = &check_recaptcha($privkey,$version);
1.1075.2.14  raeburn  18190:     } else {
                   18191:         $captcha_chk = 1;
                   18192:     }
                   18193:     return ($captcha_chk,$captcha_error);
                   18194: }
                   18195: 
                   18196: sub get_captcha_config {
1.1075.2.137  raeburn  18197:     my ($context,$lonhost,$dom_in_effect) = @_;
1.1075.2.107  raeburn  18198:     my ($captcha,$pubkey,$privkey,$version,$hashtocheck);
1.1075.2.14  raeburn  18199:     my $hostname = &Apache::lonnet::hostname($lonhost);
                   18200:     my $serverhomeID = &Apache::lonnet::get_server_homeID($hostname);
                   18201:     my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
                   18202:     if ($context eq 'usercreation') {
                   18203:         my %domconfig = &Apache::lonnet::get_dom('configuration',[$context],$serverhomedom);
                   18204:         if (ref($domconfig{$context}) eq 'HASH') {
                   18205:             $hashtocheck = $domconfig{$context}{'cancreate'};
                   18206:             if (ref($hashtocheck) eq 'HASH') {
                   18207:                 if ($hashtocheck->{'captcha'} eq 'recaptcha') {
                   18208:                     if (ref($hashtocheck->{'recaptchakeys'}) eq 'HASH') {
                   18209:                         $pubkey = $hashtocheck->{'recaptchakeys'}{'public'};
                   18210:                         $privkey = $hashtocheck->{'recaptchakeys'}{'private'};
                   18211:                     }
                   18212:                     if ($privkey && $pubkey) {
                   18213:                         $captcha = 'recaptcha';
1.1075.2.107  raeburn  18214:                         $version = $hashtocheck->{'recaptchaversion'};
                   18215:                         if ($version ne '2') {
                   18216:                             $version = 1;
                   18217:                         }
1.1075.2.14  raeburn  18218:                     } else {
                   18219:                         $captcha = 'original';
                   18220:                     }
                   18221:                 } elsif ($hashtocheck->{'captcha'} ne 'notused') {
                   18222:                     $captcha = 'original';
                   18223:                 }
                   18224:             }
                   18225:         } else {
                   18226:             $captcha = 'captcha';
                   18227:         }
                   18228:     } elsif ($context eq 'login') {
                   18229:         my %domconfhash = &Apache::loncommon::get_domainconf($serverhomedom);
                   18230:         if ($domconfhash{$serverhomedom.'.login.captcha'} eq 'recaptcha') {
                   18231:             $pubkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_public'};
                   18232:             $privkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_private'};
                   18233:             if ($privkey && $pubkey) {
                   18234:                 $captcha = 'recaptcha';
1.1075.2.107  raeburn  18235:                 $version = $domconfhash{$serverhomedom.'.login.recaptchaversion'};
                   18236:                 if ($version ne '2') {
                   18237:                     $version = 1;
                   18238:                 }
1.1075.2.14  raeburn  18239:             } else {
                   18240:                 $captcha = 'original';
                   18241:             }
                   18242:         } elsif ($domconfhash{$serverhomedom.'.login.captcha'} eq 'original') {
                   18243:             $captcha = 'original';
                   18244:         }
1.1075.2.137  raeburn  18245:     } elsif ($context eq 'passwords') {
                   18246:         if ($dom_in_effect) {
                   18247:             my %passwdconf = &Apache::lonnet::get_passwdconf($dom_in_effect);
                   18248:             if ($passwdconf{'captcha'} eq 'recaptcha') {
                   18249:                 if (ref($passwdconf{'recaptchakeys'}) eq 'HASH') {
                   18250:                     $pubkey = $passwdconf{'recaptchakeys'}{'public'};
                   18251:                     $privkey = $passwdconf{'recaptchakeys'}{'private'};
                   18252:                 }
                   18253:                 if ($privkey && $pubkey) {
                   18254:                     $captcha = 'recaptcha';
                   18255:                     $version = $passwdconf{'recaptchaversion'};
                   18256:                     if ($version ne '2') {
                   18257:                         $version = 1;
                   18258:                     }
                   18259:                 } else {
                   18260:                     $captcha = 'original';
                   18261:                 }
                   18262:             } elsif ($passwdconf{'captcha'} ne 'notused') {
                   18263:                 $captcha = 'original';
                   18264:             }
                   18265:         }
1.1075.2.14  raeburn  18266:     }
1.1075.2.107  raeburn  18267:     return ($captcha,$pubkey,$privkey,$version);
1.1075.2.14  raeburn  18268: }
                   18269: 
                   18270: sub create_captcha {
                   18271:     my %captcha_params = &captcha_settings();
                   18272:     my ($output,$maxtries,$tries) = ('',10,0);
                   18273:     while ($tries < $maxtries) {
                   18274:         $tries ++;
                   18275:         my $captcha = Authen::Captcha->new (
                   18276:                                            output_folder => $captcha_params{'output_dir'},
                   18277:                                            data_folder   => $captcha_params{'db_dir'},
                   18278:                                           );
                   18279:         my $md5sum = $captcha->generate_code($captcha_params{'numchars'});
                   18280: 
                   18281:         if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') {
                   18282:             $output = '<input type="hidden" name="crypt" value="'.$md5sum.'" />'."\n".
1.1075.2.158  raeburn  18283:                       '<span class="LC_nobreak">'.
1.1075.2.14  raeburn  18284:                       &mt('Type in the letters/numbers shown below').'&nbsp;'.
1.1075.2.161.  .15(raeb 18285:-23):                       '<input type="text" size="5" name="code" value="" autocomplete="new-password" />'.
1.1075.2.158  raeburn  18286:                       '</span><br />'.
1.1075.2.66  raeburn  18287:                       '<img src="'.$captcha_params{'www_output_dir'}.'/'.$md5sum.'.png" alt="captcha" />';
1.1075.2.14  raeburn  18288:             last;
                   18289:         }
                   18290:     }
1.1075.2.158  raeburn  18291:     if ($output eq '') {
                   18292:         &Apache::lonnet::logthis("Failed to create Captcha code after $tries attempts.");
                   18293:     }
1.1075.2.14  raeburn  18294:     return $output;
                   18295: }
                   18296: 
                   18297: sub captcha_settings {
                   18298:     my %captcha_params = (
                   18299:                            output_dir     => $Apache::lonnet::perlvar{'lonCaptchaDir'},
                   18300:                            www_output_dir => "/captchaspool",
                   18301:                            db_dir         => $Apache::lonnet::perlvar{'lonCaptchaDb'},
                   18302:                            numchars       => '5',
                   18303:                          );
                   18304:     return %captcha_params;
                   18305: }
                   18306: 
                   18307: sub check_captcha {
                   18308:     my ($captcha_chk,$captcha_error);
                   18309:     my $code = $env{'form.code'};
                   18310:     my $md5sum = $env{'form.crypt'};
                   18311:     my %captcha_params = &captcha_settings();
                   18312:     my $captcha = Authen::Captcha->new(
                   18313:                       output_folder => $captcha_params{'output_dir'},
                   18314:                       data_folder   => $captcha_params{'db_dir'},
                   18315:                   );
1.1075.2.26  raeburn  18316:     $captcha_chk = $captcha->check_code($code,$md5sum);
1.1075.2.14  raeburn  18317:     my %captcha_hash = (
                   18318:                         0       => 'Code not checked (file error)',
                   18319:                        -1      => 'Failed: code expired',
                   18320:                        -2      => 'Failed: invalid code (not in database)',
                   18321:                        -3      => 'Failed: invalid code (code does not match crypt)',
                   18322:     );
                   18323:     if ($captcha_chk != 1) {
                   18324:         $captcha_error = $captcha_hash{$captcha_chk}
                   18325:     }
                   18326:     return ($captcha_chk,$captcha_error);
                   18327: }
                   18328: 
                   18329: sub create_recaptcha {
1.1075.2.107  raeburn  18330:     my ($pubkey,$version) = @_;
                   18331:     if ($version >= 2) {
1.1075.2.158  raeburn  18332:         return '<div class="g-recaptcha" data-sitekey="'.$pubkey.'"></div>'.
                   18333:                '<div style="padding:0;clear:both;margin:0;border:0"></div>';
1.1075.2.107  raeburn  18334:     } else {
                   18335:         my $use_ssl;
                   18336:         if ($ENV{'SERVER_PORT'} == 443) {
                   18337:             $use_ssl = 1;
                   18338:         }
                   18339:         my $captcha = Captcha::reCAPTCHA->new;
                   18340:         return $captcha->get_options_setter({theme => 'white'})."\n".
                   18341:                $captcha->get_html($pubkey,undef,$use_ssl).
                   18342:                &mt('If the text is hard to read, [_1] will replace them.',
                   18343:                    '<img src="/res/adm/pages/refresh.gif" alt="reCAPTCHA refresh" />').
                   18344:                '<br /><br />';
                   18345:      }
1.1075.2.14  raeburn  18346: }
                   18347: 
                   18348: sub check_recaptcha {
1.1075.2.107  raeburn  18349:     my ($privkey,$version) = @_;
1.1075.2.14  raeburn  18350:     my $captcha_chk;
1.1075.2.150  raeburn  18351:     my $ip = &Apache::lonnet::get_requestor_ip(); 
1.1075.2.107  raeburn  18352:     if ($version >= 2) {
                   18353:         my $ua = LWP::UserAgent->new;
                   18354:         $ua->timeout(10);
                   18355:         my %info = (
                   18356:                      secret   => $privkey,
                   18357:                      response => $env{'form.g-recaptcha-response'},
1.1075.2.150  raeburn  18358:                      remoteip => $ip,
1.1075.2.107  raeburn  18359:                    );
                   18360:         my $response = $ua->post('https://www.google.com/recaptcha/api/siteverify',\%info);
                   18361:         if ($response->is_success)  {
                   18362:             my $data = JSON::DWIW->from_json($response->decoded_content);
                   18363:             if (ref($data) eq 'HASH') {
                   18364:                 if ($data->{'success'}) {
                   18365:                     $captcha_chk = 1;
                   18366:                 }
                   18367:             }
                   18368:         }
                   18369:     } else {
                   18370:         my $captcha = Captcha::reCAPTCHA->new;
                   18371:         my $captcha_result =
                   18372:             $captcha->check_answer(
                   18373:                                     $privkey,
1.1075.2.150  raeburn  18374:                                     $ip,
1.1075.2.107  raeburn  18375:                                     $env{'form.recaptcha_challenge_field'},
                   18376:                                     $env{'form.recaptcha_response_field'},
                   18377:                                   );
                   18378:         if ($captcha_result->{is_valid}) {
                   18379:             $captcha_chk = 1;
                   18380:         }
1.1075.2.14  raeburn  18381:     }
                   18382:     return $captcha_chk;
                   18383: }
                   18384: 
1.1075.2.64  raeburn  18385: sub emailusername_info {
1.1075.2.103  raeburn  18386:     my @fields = ('firstname','lastname','institution','web','location','officialemail','id');
1.1075.2.64  raeburn  18387:     my %titles = &Apache::lonlocal::texthash (
                   18388:                      lastname      => 'Last Name',
                   18389:                      firstname     => 'First Name',
                   18390:                      institution   => 'School/college/university',
                   18391:                      location      => "School's city, state/province, country",
                   18392:                      web           => "School's web address",
                   18393:                      officialemail => 'E-mail address at institution (if different)',
1.1075.2.103  raeburn  18394:                      id            => 'Student/Employee ID',
1.1075.2.64  raeburn  18395:                  );
                   18396:     return (\@fields,\%titles);
                   18397: }
                   18398: 
1.1075.2.56  raeburn  18399: sub cleanup_html {
                   18400:     my ($incoming) = @_;
                   18401:     my $outgoing;
                   18402:     if ($incoming ne '') {
                   18403:         $outgoing = $incoming;
                   18404:         $outgoing =~ s/;/&#059;/g;
                   18405:         $outgoing =~ s/\#/&#035;/g;
                   18406:         $outgoing =~ s/\&/&#038;/g;
                   18407:         $outgoing =~ s/</&#060;/g;
                   18408:         $outgoing =~ s/>/&#062;/g;
                   18409:         $outgoing =~ s/\(/&#040/g;
                   18410:         $outgoing =~ s/\)/&#041;/g;
                   18411:         $outgoing =~ s/"/&#034;/g;
                   18412:         $outgoing =~ s/'/&#039;/g;
                   18413:         $outgoing =~ s/\$/&#036;/g;
                   18414:         $outgoing =~ s{/}{&#047;}g;
                   18415:         $outgoing =~ s/=/&#061;/g;
                   18416:         $outgoing =~ s/\\/&#092;/g
                   18417:     }
                   18418:     return $outgoing;
                   18419: }
                   18420: 
1.1075.2.74  raeburn  18421: # Checks for critical messages and returns a redirect url if one exists.
                   18422: # $interval indicates how often to check for messages.
1.1075.2.161.  .1(raebu 18423:21): # $context is the calling context -- roles, grades, contents, menu or flip.
1.1075.2.74  raeburn  18424: sub critical_redirect {
1.1075.2.161.  .1(raebu 18425:21):     my ($interval,$context) = @_;
1.1075.2.158  raeburn  18426:     unless (($env{'user.domain'} ne '') && ($env{'user.name'} ne '')) {
                   18427:         return ();
                   18428:     }
1.1075.2.74  raeburn  18429:     if ((time-$env{'user.criticalcheck.time'})>$interval) {
1.1075.2.161.  .1(raebu 18430:21):         if (($env{'request.course.id'}) && (($context eq 'flip') || ($context eq 'contents'))) {
                   18431:21):             my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   18432:21):             my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
          .4(raebu 18433:22):             my $blocked = &blocking_status('alert',undef,$cnum,$cdom,undef,1);
          .1(raebu 18434:21):             if ($blocked) {
                   18435:21):                 my $checkrole = "cm./$cdom/$cnum";
                   18436:21):                 if ($env{'request.course.sec'} ne '') {
                   18437:21):                     $checkrole .= "/$env{'request.course.sec'}";
                   18438:21):                 }
                   18439:21):                 unless ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
                   18440:21):                         ($env{'request.role'} !~ m{^st\./$cdom/$cnum})) {
                   18441:21):                     return;
                   18442:21):                 }
                   18443:21):             }
                   18444:21):         }
1.1075.2.74  raeburn  18445:         my @what=&Apache::lonnet::dump('critical', $env{'user.domain'},
                   18446:                                         $env{'user.name'});
                   18447:         &Apache::lonnet::appenv({'user.criticalcheck.time'=>time});
                   18448:         my $redirecturl;
                   18449:         if ($what[0]) {
1.1075.2.158  raeburn  18450:             if (($what[0] ne 'con_lost') && ($what[0] ne 'no_such_host') && ($what[0]!~/^error\:/)) {
1.1075.2.74  raeburn  18451:                 $redirecturl='/adm/email?critical=display';
                   18452:                 my $url=&Apache::lonnet::absolute_url().$redirecturl;
                   18453:                 return (1, $url);
                   18454:             }
                   18455:         }
                   18456:     }
                   18457:     return ();
                   18458: }
                   18459: 
1.1075.2.64  raeburn  18460: # Use:
                   18461: #   my $answer=reply("encrypt:passwd:$udom:$uname:$upass",$tryserver);
                   18462: #
                   18463: ##################################################
                   18464: #          password associated functions         #
                   18465: ##################################################
                   18466: sub des_keys {
                   18467:     # Make a new key for DES encryption.
                   18468:     # Each key has two parts which are returned separately.
                   18469:     # Please note:  Each key must be passed through the &hex function
                   18470:     # before it is output to the web browser.  The hex versions cannot
                   18471:     # be used to decrypt.
                   18472:     my @hexstr=('0','1','2','3','4','5','6','7',
                   18473:                 '8','9','a','b','c','d','e','f');
                   18474:     my $lkey='';
                   18475:     for (0..7) {
                   18476:         $lkey.=$hexstr[rand(15)];
                   18477:     }
                   18478:     my $ukey='';
                   18479:     for (0..7) {
                   18480:         $ukey.=$hexstr[rand(15)];
                   18481:     }
                   18482:     return ($lkey,$ukey);
                   18483: }
                   18484: 
                   18485: sub des_decrypt {
                   18486:     my ($key,$cyphertext) = @_;
                   18487:     my $keybin=pack("H16",$key);
                   18488:     my $cypher;
                   18489:     if ($Crypt::DES::VERSION>=2.03) {
                   18490:         $cypher=new Crypt::DES $keybin;
                   18491:     } else {
                   18492:         $cypher=new DES $keybin;
                   18493:     }
1.1075.2.106  raeburn  18494:     my $plaintext='';
                   18495:     my $cypherlength = length($cyphertext);
                   18496:     my $numchunks = int($cypherlength/32);
                   18497:     for (my $j=0; $j<$numchunks; $j++) {
                   18498:         my $start = $j*32;
                   18499:         my $cypherblock = substr($cyphertext,$start,32);
                   18500:         my $chunk =
                   18501:             $cypher->decrypt(unpack("a8",pack("H16",substr($cypherblock,0,16))));
                   18502:         $chunk .=
                   18503:             $cypher->decrypt(unpack("a8",pack("H16",substr($cypherblock,16,16))));
                   18504:         $chunk=substr($chunk,1,ord(substr($chunk,0,1)) );
                   18505:         $plaintext .= $chunk;
                   18506:     }
1.1075.2.64  raeburn  18507:     return $plaintext;
                   18508: }
                   18509: 
1.1075.2.161.  .1(raebu 18510:21): sub get_requested_shorturls {
                   18511:21):     my ($cdom,$cnum,$navmap) = @_;
                   18512:21):     return unless (ref($navmap));
                   18513:21):     my ($numnew,$errors);
                   18514:21):     my @toshorten = &Apache::loncommon::get_env_multiple('form.addtiny');
                   18515:21):     if (@toshorten) {
                   18516:21):         my (%maps,%resources,%titles);
                   18517:21):         &Apache::loncourserespicker::enumerate_course_contents($navmap,\%maps,\%resources,\%titles,
                   18518:21):                                                                'shorturls',$cdom,$cnum);
                   18519:21):         if (keys(%resources)) {
                   18520:21):             my %tocreate;
                   18521:21):             foreach my $item (sort {$a <=> $b} (@toshorten)) {
                   18522:21):                 my $symb = $resources{$item};
                   18523:21):                 if ($symb) {
                   18524:21):                     $tocreate{$cnum.'&'.$symb} = 1;
                   18525:21):                 }
                   18526:21):             }
                   18527:21):             if (keys(%tocreate)) {
                   18528:21):                 ($numnew,$errors) = &make_short_symbs($cdom,$cnum,
                   18529:21):                                                       \%tocreate);
                   18530:21):             }
                   18531:21):         }
                   18532:21):     }
                   18533:21):     return ($numnew,$errors);
                   18534:21): }
                   18535:21): 
                   18536:21): sub make_short_symbs {
                   18537:21):     my ($cdom,$cnum,$tocreateref,$lockuser) = @_;
                   18538:21):     my ($numnew,@errors);
                   18539:21):     if (ref($tocreateref) eq 'HASH') {
                   18540:21):         my %tocreate = %{$tocreateref};
                   18541:21):         if (keys(%tocreate)) {
                   18542:21):             my %coursetiny = &Apache::lonnet::dump('tiny',$cdom,$cnum);
                   18543:21):             my $su = Short::URL->new(no_vowels => 1);
                   18544:21):             my $init = '';
                   18545:21):             my (%newunique,%addcourse,%courseonly,%failed);
                   18546:21):             # get lock on tiny db
                   18547:21):             my $now = time;
                   18548:21):             if ($lockuser eq '') {
                   18549:21):                 $lockuser = $env{'user.name'}.':'.$env{'user.domain'};
                   18550:21):             }
                   18551:21):             my $lockhash = {
                   18552:21):                                 "lock\0$now" => $lockuser,
                   18553:21):                             };
                   18554:21):             my $tries = 0;
                   18555:21):             my $gotlock = &Apache::lonnet::newput_dom('tiny',$lockhash,$cdom);
                   18556:21):             my ($code,$error);
                   18557:21):             while (($gotlock ne 'ok') && ($tries<3)) {
                   18558:21):                 $tries ++;
                   18559:21):                 sleep 1;
                   18560:21):                 $gotlock = &Apache::lonnet::newput_dom('tiny',$lockhash,$cdom);
                   18561:21):             }
                   18562:21):             if ($gotlock eq 'ok') {
                   18563:21):                 $init = &shorten_symbs($cdom,$init,$su,\%coursetiny,\%tocreate,\%newunique,
                   18564:21):                                        \%addcourse,\%courseonly,\%failed);
                   18565:21):                 if (keys(%failed)) {
                   18566:21):                     my $numfailed = scalar(keys(%failed));
                   18567:21):                     push(@errors,&mt('error: could not obtain unique six character URL for [quant,_1,resource]',$numfailed));
                   18568:21):                 }
                   18569:21):                 if (keys(%newunique)) {
                   18570:21):                     my $putres = &Apache::lonnet::newput_dom('tiny',\%newunique,$cdom);
                   18571:21):                     if ($putres eq 'ok') {
                   18572:21):                         $numnew = scalar(keys(%newunique));
                   18573:21):                         my $newputres = &Apache::lonnet::newput('tiny',\%addcourse,$cdom,$cnum);
                   18574:21):                         unless ($newputres eq 'ok') {
                   18575:21):                             push(@errors,&mt('error: could not store course look-up of short URLs'));
                   18576:21):                         }
                   18577:21):                     } else {
                   18578:21):                         push(@errors,&mt('error: could not store unique six character URLs'));
                   18579:21):                     }
                   18580:21):                 }
                   18581:21):                 my $dellockres = &Apache::lonnet::del_dom('tiny',["lock\0$now"],$cdom);
                   18582:21):                 unless ($dellockres eq 'ok') {
                   18583:21):                     push(@errors,&mt('error: could not release lockfile'));
                   18584:21):                 }
                   18585:21):             } else {
                   18586:21):                 push(@errors,&mt('error: could not obtain lockfile'));
                   18587:21):             }
                   18588:21):             if (keys(%courseonly)) {
                   18589:21):                 my $result = &Apache::lonnet::newput('tiny',\%courseonly,$cdom,$cnum);
                   18590:21):                 if ($result ne 'ok') {
                   18591:21):                     push(@errors,&mt('error: could not update course look-up of short URLs'));
                   18592:21):                 }
                   18593:21):             }
                   18594:21):         }
                   18595:21):     }
                   18596:21):     return ($numnew,\@errors);
                   18597:21): }
                   18598:21): 
                   18599:21): sub shorten_symbs {
                   18600:21):     my ($cdom,$init,$su,$coursetiny,$tocreate,$newunique,$addcourse,$courseonly,$failed) = @_;
                   18601:21):     return unless ((ref($su)) && (ref($coursetiny) eq 'HASH') && (ref($tocreate) eq 'HASH') &&
                   18602:21):                    (ref($newunique) eq 'HASH') && (ref($addcourse) eq 'HASH') &&
                   18603:21):                    (ref($courseonly) eq 'HASH') && (ref($failed) eq 'HASH'));
                   18604:21):     my (%possibles,%collisions);
                   18605:21):     foreach my $key (keys(%{$tocreate})) {
                   18606:21):         my $num = String::CRC32::crc32($key);
                   18607:21):         my $tiny = $su->encode($num,$init);
                   18608:21):         if ($tiny) {
                   18609:21):             $possibles{$tiny} = $key;
                   18610:21):         }
                   18611:21):     }
                   18612:21):     if (!$init) {
                   18613:21):         $init = 1;
                   18614:21):     } else {
                   18615:21):         $init ++;
                   18616:21):     }
                   18617:21):     if (keys(%possibles)) {
                   18618:21):         my @posstiny = keys(%possibles);
                   18619:21):         my $configuname = &Apache::lonnet::get_domainconfiguser($cdom);
                   18620:21):         my %currtiny = &Apache::lonnet::get('tiny',\@posstiny,$cdom,$configuname);
                   18621:21):         if (keys(%currtiny)) {
                   18622:21):             foreach my $key (keys(%currtiny)) {
                   18623:21):                 next if ($currtiny{$key} eq '');
                   18624:21):                 if ($currtiny{$key} eq $possibles{$key}) {
                   18625:21):                     my ($tcnum,$tsymb) = split(/\&/,$currtiny{$key});
                   18626:21):                     unless (($coursetiny->{$tsymb} eq $key) || ($addcourse->{$tsymb} eq $key) || ($courseonly->{$tsymb} eq $key)) {
                   18627:21):                         $courseonly->{$tsymb} = $key;
                   18628:21):                     }
                   18629:21):                 } else {
                   18630:21):                     $collisions{$possibles{$key}} = 1;
                   18631:21):                 }
                   18632:21):                 delete($possibles{$key});
                   18633:21):             }
                   18634:21):         }
                   18635:21):         foreach my $key (keys(%possibles)) {
                   18636:21):             $newunique->{$key} = $possibles{$key};
                   18637:21):             my ($tcnum,$tsymb) = split(/\&/,$possibles{$key});
                   18638:21):             unless (($coursetiny->{$tsymb} eq $key) || ($addcourse->{$tsymb} eq $key) || ($courseonly->{$tsymb} eq $key)) {
                   18639:21):                 $addcourse->{$tsymb} = $key;
                   18640:21):             }
                   18641:21):         }
                   18642:21):     }
                   18643:21):     if (keys(%collisions)) {
                   18644:21):         if ($init <5) {
                   18645:21):             if (!$init) {
                   18646:21):                 $init = 1;
                   18647:21):             } else {
                   18648:21):                 $init ++;
                   18649:21):             }
                   18650:21):             $init = &shorten_symbs($cdom,$init,$su,$coursetiny,\%collisions,
                   18651:21):                                    $newunique,$addcourse,$courseonly,$failed);
                   18652:21):         } else {
                   18653:21):             foreach my $key (keys(%collisions)) {
                   18654:21):                 $failed->{$key} = 1;
                   18655:21):                 $failed->{$key} = 1;
                   18656:21):             }
                   18657:21):         }
                   18658:21):     }
                   18659:21):     return $init;
                   18660:21): }
                   18661:21): 
1.1075.2.135  raeburn  18662: sub is_nonframeable {
                   18663:     my ($url,$absolute,$hostname,$ip,$nocache) = @_;
                   18664:     my ($remprotocol,$remhost) = ($url =~ m{^(https?)\://(([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,})}i);
                   18665:     return if (($remprotocol eq '') || ($remhost eq ''));
                   18666: 
                   18667:     $remprotocol = lc($remprotocol);
                   18668:     $remhost = lc($remhost);
                   18669:     my $remport = 80;
                   18670:     if ($remprotocol eq 'https') {
                   18671:         $remport = 443;
                   18672:     }
                   18673:     my ($result,$cached) = &Apache::lonnet::is_cached_new('noiframe',$remhost.':'.$remport);
                   18674:     if ($cached) {
                   18675:         unless ($nocache) {
                   18676:             if ($result) {
                   18677:                 return 1;
                   18678:             } else {
                   18679:                 return 0;
                   18680:             }
                   18681:         }
                   18682:     }
                   18683:     my $uselink;
                   18684:     my $request = new HTTP::Request('HEAD',$url);
1.1075.2.142  raeburn  18685:     my $ua = LWP::UserAgent->new;
                   18686:     $ua->timeout(5);
                   18687:     my $response=$ua->request($request);
1.1075.2.135  raeburn  18688:     if ($response->is_success()) {
                   18689:         my $secpolicy = lc($response->header('content-security-policy'));
                   18690:         my $xframeop = lc($response->header('x-frame-options'));
                   18691:         $secpolicy =~ s/^\s+|\s+$//g;
                   18692:         $xframeop =~ s/^\s+|\s+$//g;
                   18693:         if (($secpolicy ne '') || ($xframeop ne '')) {
                   18694:             my $remotehost = $remprotocol.'://'.$remhost;
                   18695:             my ($origin,$protocol,$port);
                   18696:             if ($ENV{'SERVER_PORT'} =~/^\d+$/) {
                   18697:                 $port = $ENV{'SERVER_PORT'};
                   18698:             } else {
                   18699:                 $port = 80;
                   18700:             }
                   18701:             if ($absolute eq '') {
                   18702:                 $protocol = 'http:';
                   18703:                 if ($port == 443) {
                   18704:                     $protocol = 'https:';
                   18705:                 }
                   18706:                 $origin = $protocol.'//'.lc($hostname);
                   18707:             } else {
                   18708:                 $origin = lc($absolute);
                   18709:                 ($protocol,$hostname) = ($absolute =~ m{^(https?:)//([^/]+)$});
                   18710:             }
                   18711:             if (($secpolicy) && ($secpolicy =~ /\Qframe-ancestors\E([^;]*)(;|$)/)) {
                   18712:                 my $framepolicy = $1;
                   18713:                 $framepolicy =~ s/^\s+|\s+$//g;
                   18714:                 my @policies = split(/\s+/,$framepolicy);
                   18715:                 if (@policies) {
                   18716:                     if (grep(/^\Q'none'\E$/,@policies)) {
                   18717:                         $uselink = 1;
                   18718:                     } else {
                   18719:                         $uselink = 1;
                   18720:                         if ((grep(/^\Q*\E$/,@policies)) || (grep(/^\Q$protocol\E$/,@policies)) ||
                   18721:                                 (($origin ne '') && (grep(/^\Q$origin\E$/,@policies))) ||
                   18722:                                 (($ip ne '') && (grep(/^\Q$ip\E$/,@policies)))) {
                   18723:                             undef($uselink);
                   18724:                         }
                   18725:                         if ($uselink) {
                   18726:                             if (grep(/^\Q'self'\E$/,@policies)) {
                   18727:                                 if (($origin ne '') && ($remotehost eq $origin)) {
                   18728:                                     undef($uselink);
                   18729:                                 }
                   18730:                             }
                   18731:                         }
                   18732:                         if ($uselink) {
                   18733:                             my @possok;
                   18734:                             if ($ip ne '') {
                   18735:                                 push(@possok,$ip);
                   18736:                             }
                   18737:                             my $hoststr = '';
                   18738:                             foreach my $part (reverse(split(/\./,$hostname))) {
                   18739:                                 if ($hoststr eq '') {
                   18740:                                     $hoststr = $part;
                   18741:                                 } else {
                   18742:                                     $hoststr = "$part.$hoststr";
                   18743:                                 }
                   18744:                                 if ($hoststr eq $hostname) {
                   18745:                                     push(@possok,$hostname);
                   18746:                                 } else {
                   18747:                                     push(@possok,"*.$hoststr");
                   18748:                                 }
                   18749:                             }
                   18750:                             if (@possok) {
                   18751:                                 foreach my $poss (@possok) {
                   18752:                                     last if (!$uselink);
                   18753:                                     foreach my $policy (@policies) {
                   18754:                                         if ($policy =~ m{^(\Q$protocol\E//|)\Q$poss\E(\Q:$port\E|)$}) {
                   18755:                                             undef($uselink);
                   18756:                                             last;
                   18757:                                         }
                   18758:                                     }
                   18759:                                 }
                   18760:                             }
                   18761:                         }
                   18762:                     }
                   18763:                 }
                   18764:             } elsif ($xframeop ne '') {
                   18765:                 $uselink = 1;
                   18766:                 my @policies = split(/\s*,\s*/,$xframeop);
                   18767:                 if (@policies) {
                   18768:                     unless (grep(/^deny$/,@policies)) {
                   18769:                         if ($origin ne '') {
                   18770:                             if (grep(/^sameorigin$/,@policies)) {
                   18771:                                 if ($remotehost eq $origin) {
                   18772:                                     undef($uselink);
                   18773:                                 }
                   18774:                             }
                   18775:                             if ($uselink) {
                   18776:                                 foreach my $policy (@policies) {
                   18777:                                     if ($policy =~ /^allow-from\s*(.+)$/) {
                   18778:                                         my $allowfrom = $1;
                   18779:                                         if (($allowfrom ne '') && ($allowfrom eq $origin)) {
                   18780:                                             undef($uselink);
                   18781:                                             last;
                   18782:                                         }
                   18783:                                     }
                   18784:                                 }
                   18785:                             }
                   18786:                         }
                   18787:                     }
                   18788:                 }
                   18789:             }
                   18790:         }
                   18791:     }
                   18792:     if ($nocache) {
                   18793:         if ($cached) {
                   18794:             my $devalidate;
                   18795:             if ($uselink && !$result) {
                   18796:                 $devalidate = 1;
                   18797:             } elsif (!$uselink && $result) {
                   18798:                 $devalidate = 1;
                   18799:             }
                   18800:             if ($devalidate) {
                   18801:                 &Apache::lonnet::devalidate_cache_new('noiframe',$remhost.':'.$remport);
                   18802:             }
                   18803:         }
                   18804:     } else {
                   18805:         if ($uselink) {
                   18806:             $result = 1;
                   18807:         } else {
                   18808:             $result = 0;
                   18809:         }
                   18810:         &Apache::lonnet::do_cache_new('noiframe',$remhost.':'.$remport,$result,3600);
                   18811:     }
                   18812:     return $uselink;
                   18813: }
                   18814: 
1.1075.2.161.  .1(raebu 18815:21): sub page_menu {
                   18816:21):     my ($menucolls,$menunum) = @_;
                   18817:21):     my %menu;
                   18818:21):     foreach my $item (split(/;/,$menucolls)) {
                   18819:21):         my ($num,$value) = split(/\%/,$item);
                   18820:21):         if ($num eq $menunum) {
                   18821:21):             my @entries = split(/\&/,$value);
                   18822:21):             foreach my $entry (@entries) {
                   18823:21):                 my ($name,$fields) = split(/=/,$entry);
                   18824:21):                 if (($name eq 'top') || ($name eq 'inline') || ($name eq 'foot') || ($name eq 'main')) {
                   18825:21):                     $menu{$name} = $fields;
                   18826:21):                 } else {
                   18827:21):                     my @shown;
                   18828:21):                     if ($fields =~ /,/) {
                   18829:21):                         @shown = split(/,/,$fields);
                   18830:21):                     } else {
                   18831:21):                         @shown = ($fields);
                   18832:21):                     }
                   18833:21):                     if (@shown) {
                   18834:21):                         foreach my $field (@shown) {
                   18835:21):                             next if ($field eq '');
                   18836:21):                             $menu{$field} = 1;
                   18837:21):                         }
                   18838:21):                     }
                   18839:21):                 }
                   18840:21):             }
                   18841:21):         }
                   18842:21):     }
                   18843:21):     return %menu;
                   18844:21): }
                   18845:21): 
1.112     bowersj2 18846: 1;
                   18847: __END__;
1.41      ng       18848: 

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