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

1.10      albertel    1: # The LearningOnline Network with CAPA
1.1       albertel    2: # a pile of common routines
1.10      albertel    3: #
1.1235  ! raeburn     4: # $Id: loncommon.pm,v 1.1234 2016/02/19 02:39:07 raeburn Exp $
1.10      albertel    5: #
                      6: # Copyright Michigan State University Board of Trustees
                      7: #
                      8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      9: #
                     10: # LON-CAPA is free software; you can redistribute it and/or modify
                     11: # it under the terms of the GNU General Public License as published by
                     12: # the Free Software Foundation; either version 2 of the License, or
                     13: # (at your option) any later version.
                     14: #
                     15: # LON-CAPA is distributed in the hope that it will be useful,
                     16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     18: # GNU General Public License for more details.
                     19: #
                     20: # You should have received a copy of the GNU General Public License
                     21: # along with LON-CAPA; if not, write to the Free Software
                     22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     23: #
                     24: # /home/httpd/html/adm/gpl.txt
                     25: #
                     26: # http://www.lon-capa.org/
                     27: #
1.1       albertel   28: 
                     29: # Makes a table out of the previous attempts
1.2       albertel   30: # Inputs result_from_symbread, user, domain, course_id
1.16      harris41   31: # Reads in non-network-related .tab files
1.1       albertel   32: 
1.35      matthew    33: # POD header:
                     34: 
1.45      matthew    35: =pod
                     36: 
1.35      matthew    37: =head1 NAME
                     38: 
                     39: Apache::loncommon - pile of common routines
                     40: 
                     41: =head1 SYNOPSIS
                     42: 
1.112     bowersj2   43: Common routines for manipulating connections, student answers,
                     44:     domains, common Javascript fragments, etc.
1.35      matthew    45: 
1.112     bowersj2   46: =head1 OVERVIEW
1.35      matthew    47: 
1.112     bowersj2   48: A collection of commonly used subroutines that don't have a natural
                     49: home anywhere else. This collection helps remove
1.35      matthew    50: redundancy from other modules and increase efficiency of memory usage.
                     51: 
                     52: =cut 
                     53: 
                     54: # End of POD header
1.1       albertel   55: package Apache::loncommon;
                     56: 
                     57: use strict;
1.258     albertel   58: use Apache::lonnet;
1.46      matthew    59: use GDBM_File;
1.51      www        60: use POSIX qw(strftime mktime);
1.82      www        61: use Apache::lonmenu();
1.498     albertel   62: use Apache::lonenc();
1.117     www        63: use Apache::lonlocal;
1.685     tempelho   64: use Apache::lonnet();
1.139     matthew    65: use HTML::Entities;
1.334     albertel   66: use Apache::lonhtmlcommon();
                     67: use Apache::loncoursedata();
1.344     albertel   68: use Apache::lontexconvert();
1.444     albertel   69: use Apache::lonclonecourse();
1.1108    raeburn    70: use Apache::lonuserutils();
1.1110    raeburn    71: use Apache::lonuserstate();
1.1182    raeburn    72: use Apache::courseclassifier();
1.479     albertel   73: use LONCAPA qw(:DEFAULT :match);
1.657     raeburn    74: use DateTime::TimeZone;
1.687     raeburn    75: use DateTime::Locale::Catalog;
1.1220    raeburn    76: use Encode();
1.1091    foxr       77: use Text::Aspell;
1.1094    raeburn    78: use Authen::Captcha;
                     79: use Captcha::reCAPTCHA;
1.1234    raeburn    80: use JSON::DWIW;
                     81: use LWP::UserAgent;
1.1174    raeburn    82: use Crypt::DES;
                     83: use DynaLoader; # for Crypt::DES version
1.1223    musolffc   84: use MIME::Lite;
                     85: use MIME::Types;
1.117     www        86: 
1.517     raeburn    87: # ---------------------------------------------- Designs
                     88: use vars qw(%defaultdesign);
                     89: 
1.22      www        90: my $readit;
                     91: 
1.517     raeburn    92: 
1.157     matthew    93: ##
                     94: ## Global Variables
                     95: ##
1.46      matthew    96: 
1.643     foxr       97: 
                     98: # ----------------------------------------------- SSI with retries:
                     99: #
                    100: 
                    101: =pod
                    102: 
1.648     raeburn   103: =head1 Server Side include with retries:
1.643     foxr      104: 
                    105: =over 4
                    106: 
1.648     raeburn   107: =item * &ssi_with_retries(resource,retries form)
1.643     foxr      108: 
                    109: Performs an ssi with some number of retries.  Retries continue either
                    110: until the result is ok or until the retry count supplied by the
                    111: caller is exhausted.  
                    112: 
                    113: Inputs:
1.648     raeburn   114: 
                    115: =over 4
                    116: 
1.643     foxr      117: resource   - Identifies the resource to insert.
1.648     raeburn   118: 
1.643     foxr      119: retries    - Count of the number of retries allowed.
1.648     raeburn   120: 
1.643     foxr      121: form       - Hash that identifies the rendering options.
                    122: 
1.648     raeburn   123: =back
                    124: 
                    125: Returns:
                    126: 
                    127: =over 4
                    128: 
1.643     foxr      129: content    - The content of the response.  If retries were exhausted this is empty.
1.648     raeburn   130: 
1.643     foxr      131: response   - The response from the last attempt (which may or may not have been successful.
                    132: 
1.648     raeburn   133: =back
                    134: 
                    135: =back
                    136: 
1.643     foxr      137: =cut
                    138: 
                    139: sub ssi_with_retries {
                    140:     my ($resource, $retries, %form) = @_;
                    141: 
                    142: 
                    143:     my $ok = 0;			# True if we got a good response.
                    144:     my $content;
                    145:     my $response;
                    146: 
                    147:     # Try to get the ssi done. within the retries count:
                    148: 
                    149:     do {
                    150: 	($content, $response) = &Apache::lonnet::ssi($resource, %form);
                    151: 	$ok      = $response->is_success;
1.650     www       152:         if (!$ok) {
                    153:             &Apache::lonnet::logthis("Failed ssi_with_retries on $resource: ".$response->is_success.', '.$response->code.', '.$response->message);
                    154:         }
1.643     foxr      155: 	$retries--;
                    156:     } while (!$ok && ($retries > 0));
                    157: 
                    158:     if (!$ok) {
                    159: 	$content = '';		# On error return an empty content.
                    160:     }
                    161:     return ($content, $response);
                    162: 
                    163: }
                    164: 
                    165: 
                    166: 
1.20      www       167: # ----------------------------------------------- Filetypes/Languages/Copyright
1.12      harris41  168: my %language;
1.124     www       169: my %supported_language;
1.1088    foxr      170: my %supported_codes;
1.1048    foxr      171: my %latex_language;		# For choosing hyphenation in <transl..>
                    172: my %latex_language_bykey;	# for choosing hyphenation from metadata
1.12      harris41  173: my %cprtag;
1.192     taceyjo1  174: my %scprtag;
1.351     www       175: my %fe; my %fd; my %fm;
1.41      ng        176: my %category_extensions;
1.12      harris41  177: 
1.46      matthew   178: # ---------------------------------------------- Thesaurus variables
1.144     matthew   179: #
                    180: # %Keywords:
                    181: #      A hash used by &keyword to determine if a word is considered a keyword.
                    182: # $thesaurus_db_file 
                    183: #      Scalar containing the full path to the thesaurus database.
1.46      matthew   184: 
                    185: my %Keywords;
                    186: my $thesaurus_db_file;
                    187: 
1.144     matthew   188: #
                    189: # Initialize values from language.tab, copyright.tab, filetypes.tab,
                    190: # thesaurus.tab, and filecategories.tab.
                    191: #
1.18      www       192: BEGIN {
1.46      matthew   193:     # Variable initialization
                    194:     $thesaurus_db_file = $Apache::lonnet::perlvar{'lonTabDir'}."/thesaurus.db";
                    195:     #
1.22      www       196:     unless ($readit) {
1.12      harris41  197: # ------------------------------------------------------------------- languages
                    198:     {
1.158     raeburn   199:         my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    200:                                    '/language.tab';
                    201:         if ( open(my $fh,"<$langtabfile") ) {
1.356     albertel  202:             while (my $line = <$fh>) {
                    203:                 next if ($line=~/^\#/);
                    204:                 chomp($line);
1.1088    foxr      205:                 my ($key,$code,$country,$three,$enc,$val,$sup,$latex)=(split(/\t/,$line));
1.158     raeburn   206:                 $language{$key}=$val.' - '.$enc;
                    207:                 if ($sup) {
                    208:                     $supported_language{$key}=$sup;
1.1088    foxr      209: 		    $supported_codes{$key}   = $code;
1.158     raeburn   210:                 }
1.1048    foxr      211: 		if ($latex) {
                    212: 		    $latex_language_bykey{$key} = $latex;
1.1088    foxr      213: 		    $latex_language{$code} = $latex;
1.1048    foxr      214: 		}
1.158     raeburn   215:             }
                    216:             close($fh);
                    217:         }
1.12      harris41  218:     }
                    219: # ------------------------------------------------------------------ copyrights
                    220:     {
1.158     raeburn   221:         my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                    222:                                   '/copyright.tab';
                    223:         if ( open (my $fh,"<$copyrightfile") ) {
1.356     albertel  224:             while (my $line = <$fh>) {
                    225:                 next if ($line=~/^\#/);
                    226:                 chomp($line);
                    227:                 my ($key,$val)=(split(/\s+/,$line,2));
1.158     raeburn   228:                 $cprtag{$key}=$val;
                    229:             }
                    230:             close($fh);
                    231:         }
1.12      harris41  232:     }
1.351     www       233: # ----------------------------------------------------------- source copyrights
1.192     taceyjo1  234:     {
                    235:         my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                    236:                                   '/source_copyright.tab';
                    237:         if ( open (my $fh,"<$sourcecopyrightfile") ) {
1.356     albertel  238:             while (my $line = <$fh>) {
                    239:                 next if ($line =~ /^\#/);
                    240:                 chomp($line);
                    241:                 my ($key,$val)=(split(/\s+/,$line,2));
1.192     taceyjo1  242:                 $scprtag{$key}=$val;
                    243:             }
                    244:             close($fh);
                    245:         }
                    246:     }
1.63      www       247: 
1.517     raeburn   248: # -------------------------------------------------------------- default domain designs
1.63      www       249:     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
1.517     raeburn   250:     my $designfile = $designdir.'/default.tab';
                    251:     if ( open (my $fh,"<$designfile") ) {
                    252:         while (my $line = <$fh>) {
                    253:             next if ($line =~ /^\#/);
                    254:             chomp($line);
                    255:             my ($key,$val)=(split(/\=/,$line));
                    256:             if ($val) { $defaultdesign{$key}=$val; }
                    257:         }
                    258:         close($fh);
1.63      www       259:     }
                    260: 
1.15      harris41  261: # ------------------------------------------------------------- file categories
                    262:     {
1.158     raeburn   263:         my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    264:                                   '/filecategories.tab';
                    265:         if ( open (my $fh,"<$categoryfile") ) {
1.356     albertel  266: 	    while (my $line = <$fh>) {
                    267: 		next if ($line =~ /^\#/);
                    268: 		chomp($line);
                    269:                 my ($extension,$category)=(split(/\s+/,$line,2));
1.158     raeburn   270:                 push @{$category_extensions{lc($category)}},$extension;
                    271:             }
                    272:             close($fh);
                    273:         }
                    274: 
1.15      harris41  275:     }
1.12      harris41  276: # ------------------------------------------------------------------ file types
                    277:     {
1.158     raeburn   278:         my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    279:                '/filetypes.tab';
                    280:         if ( open (my $fh,"<$typesfile") ) {
1.356     albertel  281:             while (my $line = <$fh>) {
                    282: 		next if ($line =~ /^\#/);
                    283: 		chomp($line);
                    284:                 my ($ending,$emb,$mime,$descr)=split(/\s+/,$line,4);
1.158     raeburn   285:                 if ($descr ne '') {
                    286:                     $fe{$ending}=lc($emb);
                    287:                     $fd{$ending}=$descr;
1.351     www       288:                     if ($mime ne 'unk') { $fm{$ending}=$mime; }
1.158     raeburn   289:                 }
                    290:             }
                    291:             close($fh);
                    292:         }
1.12      harris41  293:     }
1.22      www       294:     &Apache::lonnet::logthis(
1.705     tempelho  295:              "<span style='color:yellow;'>INFO: Read file types</span>");
1.22      www       296:     $readit=1;
1.46      matthew   297:     }  # end of unless($readit) 
1.32      matthew   298:     
                    299: }
1.112     bowersj2  300: 
1.42      matthew   301: ###############################################################
                    302: ##           HTML and Javascript Helper Functions            ##
                    303: ###############################################################
                    304: 
                    305: =pod 
                    306: 
1.112     bowersj2  307: =head1 HTML and Javascript Functions
1.42      matthew   308: 
1.112     bowersj2  309: =over 4
                    310: 
1.648     raeburn   311: =item * &browser_and_searcher_javascript()
1.112     bowersj2  312: 
                    313: X<browsing, javascript>X<searching, javascript>Returns a string
                    314: containing javascript with two functions, C<openbrowser> and
                    315: C<opensearcher>. Returned string does not contain E<lt>scriptE<gt>
                    316: tags.
1.42      matthew   317: 
1.648     raeburn   318: =item * &openbrowser(formname,elementname,only,omit) [javascript]
1.42      matthew   319: 
                    320: inputs: formname, elementname, only, omit
                    321: 
                    322: formname and elementname indicate the name of the html form and name of
                    323: the element that the results of the browsing selection are to be placed in. 
                    324: 
                    325: Specifying 'only' will restrict the browser to displaying only files
1.185     www       326: with the given extension.  Can be a comma separated list.
1.42      matthew   327: 
                    328: Specifying 'omit' will restrict the browser to NOT displaying files
1.185     www       329: with the given extension.  Can be a comma separated list.
1.42      matthew   330: 
1.648     raeburn   331: =item * &opensearcher(formname,elementname) [javascript]
1.42      matthew   332: 
                    333: Inputs: formname, elementname
                    334: 
                    335: formname and elementname specify the name of the html form and the name
                    336: of the element the selection from the search results will be placed in.
1.542     raeburn   337: 
1.42      matthew   338: =cut
                    339: 
                    340: sub browser_and_searcher_javascript {
1.199     albertel  341:     my ($mode)=@_;
                    342:     if (!defined($mode)) { $mode='edit'; }
1.453     albertel  343:     my $resurl=&escape_single(&lastresurl());
1.42      matthew   344:     return <<END;
1.219     albertel  345: // <!-- BEGIN LON-CAPA Internal
1.50      matthew   346:     var editbrowser = null;
1.135     albertel  347:     function openbrowser(formname,elementname,only,omit,titleelement) {
1.170     www       348:         var url = '$resurl/?';
1.42      matthew   349:         if (editbrowser == null) {
                    350:             url += 'launch=1&';
                    351:         }
                    352:         url += 'catalogmode=interactive&';
1.199     albertel  353:         url += 'mode=$mode&';
1.611     albertel  354:         url += 'inhibitmenu=yes&';
1.42      matthew   355:         url += 'form=' + formname + '&';
                    356:         if (only != null) {
                    357:             url += 'only=' + only + '&';
1.217     albertel  358:         } else {
                    359:             url += 'only=&';
                    360: 	}
1.42      matthew   361:         if (omit != null) {
                    362:             url += 'omit=' + omit + '&';
1.217     albertel  363:         } else {
                    364:             url += 'omit=&';
                    365: 	}
1.135     albertel  366:         if (titleelement != null) {
                    367:             url += 'titleelement=' + titleelement + '&';
1.217     albertel  368:         } else {
                    369: 	    url += 'titleelement=&';
                    370: 	}
1.42      matthew   371:         url += 'element=' + elementname + '';
                    372:         var title = 'Browser';
1.435     albertel  373:         var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42      matthew   374:         options += ',width=700,height=600';
                    375:         editbrowser = open(url,title,options,'1');
                    376:         editbrowser.focus();
                    377:     }
                    378:     var editsearcher;
1.135     albertel  379:     function opensearcher(formname,elementname,titleelement) {
1.42      matthew   380:         var url = '/adm/searchcat?';
                    381:         if (editsearcher == null) {
                    382:             url += 'launch=1&';
                    383:         }
                    384:         url += 'catalogmode=interactive&';
1.199     albertel  385:         url += 'mode=$mode&';
1.42      matthew   386:         url += 'form=' + formname + '&';
1.135     albertel  387:         if (titleelement != null) {
                    388:             url += 'titleelement=' + titleelement + '&';
1.217     albertel  389:         } else {
                    390: 	    url += 'titleelement=&';
                    391: 	}
1.42      matthew   392:         url += 'element=' + elementname + '';
                    393:         var title = 'Search';
1.435     albertel  394:         var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42      matthew   395:         options += ',width=700,height=600';
                    396:         editsearcher = open(url,title,options,'1');
                    397:         editsearcher.focus();
                    398:     }
1.219     albertel  399: // END LON-CAPA Internal -->
1.42      matthew   400: END
1.170     www       401: }
                    402: 
                    403: sub lastresurl {
1.258     albertel  404:     if ($env{'environment.lastresurl'}) {
                    405: 	return $env{'environment.lastresurl'}
1.170     www       406:     } else {
                    407: 	return '/res';
                    408:     }
                    409: }
                    410: 
                    411: sub storeresurl {
                    412:     my $resurl=&Apache::lonnet::clutter(shift);
                    413:     unless ($resurl=~/^\/res/) { return 0; }
                    414:     $resurl=~s/\/$//;
                    415:     &Apache::lonnet::put('environment',{'lastresurl' => $resurl});
1.646     raeburn   416:     &Apache::lonnet::appenv({'environment.lastresurl' => $resurl});
1.170     www       417:     return 1;
1.42      matthew   418: }
                    419: 
1.74      www       420: sub studentbrowser_javascript {
1.111     www       421:    unless (
1.258     albertel  422:             (($env{'request.course.id'}) && 
1.302     albertel  423:              (&Apache::lonnet::allowed('srm',$env{'request.course.id'})
                    424: 	      || &Apache::lonnet::allowed('srm',$env{'request.course.id'}.
                    425: 					  '/'.$env{'request.course.sec'})
                    426: 	      ))
1.258     albertel  427:          || ($env{'request.role'}=~/^(au|dc|su)/)
1.111     www       428:           ) { return ''; }  
1.74      www       429:    return (<<'ENDSTDBRW');
1.776     bisitz    430: <script type="text/javascript" language="Javascript">
1.824     bisitz    431: // <![CDATA[
1.74      www       432:     var stdeditbrowser;
1.999     www       433:     function openstdbrowser(formname,uname,udom,clicker,roleflag,ignorefilter,courseadvonly) {
1.74      www       434:         var url = '/adm/pickstudent?';
                    435:         var filter;
1.558     albertel  436: 	if (!ignorefilter) {
                    437: 	    eval('filter=document.'+formname+'.'+uname+'.value;');
                    438: 	}
1.74      www       439:         if (filter != null) {
                    440:            if (filter != '') {
                    441:                url += 'filter='+filter+'&';
                    442: 	   }
                    443:         }
                    444:         url += 'form=' + formname + '&unameelement='+uname+
1.999     www       445:                                     '&udomelement='+udom+
                    446:                                     '&clicker='+clicker;
1.111     www       447: 	if (roleflag) { url+="&roles=1"; }
1.793     raeburn   448:         if (courseadvonly) { url+="&courseadvonly=1"; }
1.102     www       449:         var title = 'Student_Browser';
1.74      www       450:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    451:         options += ',width=700,height=600';
                    452:         stdeditbrowser = open(url,title,options,'1');
                    453:         stdeditbrowser.focus();
                    454:     }
1.824     bisitz    455: // ]]>
1.74      www       456: </script>
                    457: ENDSTDBRW
                    458: }
1.42      matthew   459: 
1.1003    www       460: sub resourcebrowser_javascript {
                    461:    unless ($env{'request.course.id'}) { return ''; }
1.1004    www       462:    return (<<'ENDRESBRW');
1.1003    www       463: <script type="text/javascript" language="Javascript">
                    464: // <![CDATA[
                    465:     var reseditbrowser;
1.1004    www       466:     function openresbrowser(formname,reslink) {
1.1005    www       467:         var url = '/adm/pickresource?form='+formname+'&reslink='+reslink;
1.1003    www       468:         var title = 'Resource_Browser';
                    469:         var options = 'scrollbars=1,resizable=1,menubar=0';
1.1005    www       470:         options += ',width=700,height=500';
1.1004    www       471:         reseditbrowser = open(url,title,options,'1');
                    472:         reseditbrowser.focus();
1.1003    www       473:     }
                    474: // ]]>
                    475: </script>
1.1004    www       476: ENDRESBRW
1.1003    www       477: }
                    478: 
1.74      www       479: sub selectstudent_link {
1.999     www       480:    my ($form,$unameele,$udomele,$courseadvonly,$clickerid)=@_;
                    481:    my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
                    482:                       &Apache::lonhtmlcommon::entity_encode($unameele)."','".
                    483:                       &Apache::lonhtmlcommon::entity_encode($udomele)."'";
1.258     albertel  484:    if ($env{'request.course.id'}) {  
1.302     albertel  485:        if (!&Apache::lonnet::allowed('srm',$env{'request.course.id'})
                    486: 	   && !&Apache::lonnet::allowed('srm',$env{'request.course.id'}.
                    487: 					'/'.$env{'request.course.sec'})) {
1.111     www       488: 	   return '';
                    489:        }
1.999     www       490:        $callargs.=",'".&Apache::lonhtmlcommon::entity_encode($clickerid)."'";
1.793     raeburn   491:        if ($courseadvonly)  {
                    492:            $callargs .= ",'',1,1";
                    493:        }
                    494:        return '<span class="LC_nobreak">'.
                    495:               '<a href="javascript:openstdbrowser('.$callargs.');">'.
                    496:               &mt('Select User').'</a></span>';
1.74      www       497:    }
1.258     albertel  498:    if ($env{'request.role'}=~/^(au|dc|su)/) {
1.1012    www       499:        $callargs .= ",'',1"; 
1.793     raeburn   500:        return '<span class="LC_nobreak">'.
                    501:               '<a href="javascript:openstdbrowser('.$callargs.');">'.
                    502:               &mt('Select User').'</a></span>';
1.111     www       503:    }
                    504:    return '';
1.91      www       505: }
                    506: 
1.1004    www       507: sub selectresource_link {
                    508:    my ($form,$reslink,$arg)=@_;
                    509:    
                    510:    my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
                    511:                       &Apache::lonhtmlcommon::entity_encode($reslink)."'";
                    512:    unless ($env{'request.course.id'}) { return $arg; }
                    513:    return '<span class="LC_nobreak">'.
                    514:               '<a href="javascript:openresbrowser('.$callargs.');">'.
                    515:               $arg.'</a></span>';
                    516: }
                    517: 
                    518: 
                    519: 
1.653     raeburn   520: sub authorbrowser_javascript {
                    521:     return <<"ENDAUTHORBRW";
1.776     bisitz    522: <script type="text/javascript" language="JavaScript">
1.824     bisitz    523: // <![CDATA[
1.653     raeburn   524: var stdeditbrowser;
                    525: 
                    526: function openauthorbrowser(formname,udom) {
                    527:     var url = '/adm/pickauthor?';
                    528:     url += 'form='+formname+'&roledom='+udom;
                    529:     var title = 'Author_Browser';
                    530:     var options = 'scrollbars=1,resizable=1,menubar=0';
                    531:     options += ',width=700,height=600';
                    532:     stdeditbrowser = open(url,title,options,'1');
                    533:     stdeditbrowser.focus();
                    534: }
                    535: 
1.824     bisitz    536: // ]]>
1.653     raeburn   537: </script>
                    538: ENDAUTHORBRW
                    539: }
                    540: 
1.91      www       541: sub coursebrowser_javascript {
1.1116    raeburn   542:     my ($domainfilter,$sec_element,$formname,$role_element,$crstype,
1.1221    raeburn   543:         $credits_element,$instcode) = @_;
1.932     raeburn   544:     my $wintitle = 'Course_Browser';
1.931     raeburn   545:     if ($crstype eq 'Community') {
1.932     raeburn   546:         $wintitle = 'Community_Browser';
1.909     raeburn   547:     }
1.876     raeburn   548:     my $id_functions = &javascript_index_functions();
                    549:     my $output = '
1.776     bisitz    550: <script type="text/javascript" language="JavaScript">
1.824     bisitz    551: // <![CDATA[
1.468     raeburn   552:     var stdeditbrowser;'."\n";
1.876     raeburn   553: 
                    554:     $output .= <<"ENDSTDBRW";
1.909     raeburn   555:     function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,type,type_elem) {
1.91      www       556:         var url = '/adm/pickcourse?';
1.895     raeburn   557:         var formid = getFormIdByName(formname);
1.876     raeburn   558:         var domainfilter = getDomainFromSelectbox(formname,udom);
1.128     albertel  559:         if (domainfilter != null) {
                    560:            if (domainfilter != '') {
                    561:                url += 'domainfilter='+domainfilter+'&';
                    562: 	   }
                    563:         }
1.91      www       564:         url += 'form=' + formname + '&cnumelement='+uname+
1.187     albertel  565: 	                            '&cdomelement='+udom+
                    566:                                     '&cnameelement='+desc;
1.468     raeburn   567:         if (extra_element !=null && extra_element != '') {
1.594     raeburn   568:             if (formname == 'rolechoice' || formname == 'studentform') {
1.468     raeburn   569:                 url += '&roleelement='+extra_element;
                    570:                 if (domainfilter == null || domainfilter == '') {
                    571:                     url += '&domainfilter='+extra_element;
                    572:                 }
1.234     raeburn   573:             }
1.468     raeburn   574:             else {
                    575:                 if (formname == 'portform') {
                    576:                     url += '&setroles='+extra_element;
1.800     raeburn   577:                 } else {
                    578:                     if (formname == 'rules') {
                    579:                         url += '&fixeddom='+extra_element; 
                    580:                     }
1.468     raeburn   581:                 }
                    582:             }     
1.230     raeburn   583:         }
1.909     raeburn   584:         if (type != null && type != '') {
                    585:             url += '&type='+type;
                    586:         }
                    587:         if (type_elem != null && type_elem != '') {
                    588:             url += '&typeelement='+type_elem;
                    589:         }
1.872     raeburn   590:         if (formname == 'ccrs') {
                    591:             var ownername = document.forms[formid].ccuname.value;
                    592:             var ownerdom =  document.forms[formid].ccdomain.options[document.forms[formid].ccdomain.selectedIndex].value;
1.1221    raeburn   593:             url += '&cloner='+ownername+':'+ownerdom+'&crscode='+document.forms[formid].crscode.value;
                    594:         }
                    595:         if (formname == 'requestcrs') {
                    596:             url += '&crsdom=$domainfilter&crscode=$instcode';
1.872     raeburn   597:         }
1.293     raeburn   598:         if (multflag !=null && multflag != '') {
                    599:             url += '&multiple='+multflag;
                    600:         }
1.909     raeburn   601:         var title = '$wintitle';
1.91      www       602:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    603:         options += ',width=700,height=600';
                    604:         stdeditbrowser = open(url,title,options,'1');
                    605:         stdeditbrowser.focus();
                    606:     }
1.876     raeburn   607: $id_functions
                    608: ENDSTDBRW
1.1116    raeburn   609:     if (($sec_element ne '') || ($role_element ne '') || ($credits_element ne '')) {
                    610:         $output .= &setsec_javascript($sec_element,$formname,$role_element,
                    611:                                       $credits_element);
1.876     raeburn   612:     }
                    613:     $output .= '
                    614: // ]]>
                    615: </script>';
                    616:     return $output;
                    617: }
                    618: 
                    619: sub javascript_index_functions {
                    620:     return <<"ENDJS";
                    621: 
                    622: function getFormIdByName(formname) {
                    623:     for (var i=0;i<document.forms.length;i++) {
                    624:         if (document.forms[i].name == formname) {
                    625:             return i;
                    626:         }
                    627:     }
                    628:     return -1;
                    629: }
                    630: 
                    631: function getIndexByName(formid,item) {
                    632:     for (var i=0;i<document.forms[formid].elements.length;i++) {
                    633:         if (document.forms[formid].elements[i].name == item) {
                    634:             return i;
                    635:         }
                    636:     }
                    637:     return -1;
                    638: }
1.468     raeburn   639: 
1.876     raeburn   640: function getDomainFromSelectbox(formname,udom) {
                    641:     var userdom;
                    642:     var formid = getFormIdByName(formname);
                    643:     if (formid > -1) {
                    644:         var domid = getIndexByName(formid,udom);
                    645:         if (domid > -1) {
                    646:             if (document.forms[formid].elements[domid].type == 'select-one') {
                    647:                 userdom=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
                    648:             }
                    649:             if (document.forms[formid].elements[domid].type == 'hidden') {
                    650:                 userdom=document.forms[formid].elements[domid].value;
1.468     raeburn   651:             }
                    652:         }
                    653:     }
1.876     raeburn   654:     return userdom;
                    655: }
                    656: 
                    657: ENDJS
1.468     raeburn   658: 
1.876     raeburn   659: }
                    660: 
1.1017    raeburn   661: sub javascript_array_indexof {
1.1018    raeburn   662:     return <<ENDJS;
1.1017    raeburn   663: <script type="text/javascript" language="JavaScript">
                    664: // <![CDATA[
                    665: 
                    666: if (!Array.prototype.indexOf) {
                    667:     Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
                    668:         "use strict";
                    669:         if (this === void 0 || this === null) {
                    670:             throw new TypeError();
                    671:         }
                    672:         var t = Object(this);
                    673:         var len = t.length >>> 0;
                    674:         if (len === 0) {
                    675:             return -1;
                    676:         }
                    677:         var n = 0;
                    678:         if (arguments.length > 0) {
                    679:             n = Number(arguments[1]);
1.1088    foxr      680:             if (n !== n) { // shortcut for verifying if it is NaN
1.1017    raeburn   681:                 n = 0;
                    682:             } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
                    683:                 n = (n > 0 || -1) * Math.floor(Math.abs(n));
                    684:             }
                    685:         }
                    686:         if (n >= len) {
                    687:             return -1;
                    688:         }
                    689:         var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
                    690:         for (; k < len; k++) {
                    691:             if (k in t && t[k] === searchElement) {
                    692:                 return k;
                    693:             }
                    694:         }
                    695:         return -1;
                    696:     }
                    697: }
                    698: 
                    699: // ]]>
                    700: </script>
                    701: 
                    702: ENDJS
                    703: 
                    704: }
                    705: 
1.876     raeburn   706: sub userbrowser_javascript {
                    707:     my $id_functions = &javascript_index_functions();
                    708:     return <<"ENDUSERBRW";
                    709: 
1.888     raeburn   710: function openuserbrowser(formname,uname,udom,ulast,ufirst,uemail,hideudom,crsdom,caller) {
1.876     raeburn   711:     var url = '/adm/pickuser?';
                    712:     var userdom = getDomainFromSelectbox(formname,udom);
                    713:     if (userdom != null) {
                    714:        if (userdom != '') {
                    715:            url += 'srchdom='+userdom+'&';
                    716:        }
                    717:     }
                    718:     url += 'form=' + formname + '&unameelement='+uname+
                    719:                                 '&udomelement='+udom+
                    720:                                 '&ulastelement='+ulast+
                    721:                                 '&ufirstelement='+ufirst+
                    722:                                 '&uemailelement='+uemail+
1.881     raeburn   723:                                 '&hideudomelement='+hideudom+
                    724:                                 '&coursedom='+crsdom;
1.888     raeburn   725:     if ((caller != null) && (caller != undefined)) {
                    726:         url += '&caller='+caller;
                    727:     }
1.876     raeburn   728:     var title = 'User_Browser';
                    729:     var options = 'scrollbars=1,resizable=1,menubar=0';
                    730:     options += ',width=700,height=600';
                    731:     var stdeditbrowser = open(url,title,options,'1');
                    732:     stdeditbrowser.focus();
                    733: }
                    734: 
1.888     raeburn   735: function fix_domain (formname,udom,origdom,uname) {
1.876     raeburn   736:     var formid = getFormIdByName(formname);
                    737:     if (formid > -1) {
1.888     raeburn   738:         var unameid = getIndexByName(formid,uname);
1.876     raeburn   739:         var domid = getIndexByName(formid,udom);
                    740:         var hidedomid = getIndexByName(formid,origdom);
                    741:         if (hidedomid > -1) {
                    742:             var fixeddom = document.forms[formid].elements[hidedomid].value;
1.888     raeburn   743:             var unameval = document.forms[formid].elements[unameid].value;
                    744:             if ((fixeddom != '') && (fixeddom != undefined) && (fixeddom != null) && (unameval != '') && (unameval != undefined) && (unameval != null)) {
                    745:                 if (domid > -1) {
                    746:                     var slct = document.forms[formid].elements[domid];
                    747:                     if (slct.type == 'select-one') {
                    748:                         var i;
                    749:                         for (i=0;i<slct.length;i++) {
                    750:                             if (slct.options[i].value==fixeddom) { slct.selectedIndex=i; }
                    751:                         }
                    752:                     }
                    753:                     if (slct.type == 'hidden') {
                    754:                         slct.value = fixeddom;
1.876     raeburn   755:                     }
                    756:                 }
1.468     raeburn   757:             }
                    758:         }
                    759:     }
1.876     raeburn   760:     return;
                    761: }
                    762: 
                    763: $id_functions
                    764: ENDUSERBRW
1.468     raeburn   765: }
                    766: 
                    767: sub setsec_javascript {
1.1116    raeburn   768:     my ($sec_element,$formname,$role_element,$credits_element) = @_;
1.905     raeburn   769:     my (@courserolenames,@communityrolenames,$rolestr,$courserolestr,
                    770:         $communityrolestr);
                    771:     if ($role_element ne '') {
                    772:         my @allroles = ('st','ta','ep','in','ad');
                    773:         foreach my $crstype ('Course','Community') {
                    774:             if ($crstype eq 'Community') {
                    775:                 foreach my $role (@allroles) {
                    776:                     push(@communityrolenames,&Apache::lonnet::plaintext($role,$crstype));
                    777:                 }
                    778:                 push(@communityrolenames,&Apache::lonnet::plaintext('co'));
                    779:             } else {
                    780:                 foreach my $role (@allroles) {
                    781:                     push(@courserolenames,&Apache::lonnet::plaintext($role,$crstype));
                    782:                 }
                    783:                 push(@courserolenames,&Apache::lonnet::plaintext('cc'));
                    784:             }
                    785:         }
                    786:         $rolestr = '"'.join('","',@allroles).'"';
                    787:         $courserolestr = '"'.join('","',@courserolenames).'"';
                    788:         $communityrolestr = '"'.join('","',@communityrolenames).'"';
                    789:     }
1.468     raeburn   790:     my $setsections = qq|
                    791: function setSect(sectionlist) {
1.629     raeburn   792:     var sectionsArray = new Array();
                    793:     if ((sectionlist != '') && (typeof sectionlist != "undefined")) {
                    794:         sectionsArray = sectionlist.split(",");
                    795:     }
1.468     raeburn   796:     var numSections = sectionsArray.length;
                    797:     document.$formname.$sec_element.length = 0;
                    798:     if (numSections == 0) {
                    799:         document.$formname.$sec_element.multiple=false;
                    800:         document.$formname.$sec_element.size=1;
                    801:         document.$formname.$sec_element.options[0] = new Option('No existing sections','',false,false)
                    802:     } else {
                    803:         if (numSections == 1) {
                    804:             document.$formname.$sec_element.multiple=false;
                    805:             document.$formname.$sec_element.size=1;
                    806:             document.$formname.$sec_element.options[0] = new Option('Select','',true,true);
                    807:             document.$formname.$sec_element.options[1] = new Option('No section','',false,false)
                    808:             document.$formname.$sec_element.options[2] = new Option(sectionsArray[0],sectionsArray[0],false,false);
                    809:         } else {
                    810:             for (var i=0; i<numSections; i++) {
                    811:                 document.$formname.$sec_element.options[i] = new Option(sectionsArray[i],sectionsArray[i],false,false)
                    812:             }
                    813:             document.$formname.$sec_element.multiple=true
                    814:             if (numSections < 3) {
                    815:                 document.$formname.$sec_element.size=numSections;
                    816:             } else {
                    817:                 document.$formname.$sec_element.size=3;
                    818:             }
                    819:             document.$formname.$sec_element.options[0].selected = false
                    820:         }
                    821:     }
1.91      www       822: }
1.905     raeburn   823: 
                    824: function setRole(crstype) {
1.468     raeburn   825: |;
1.905     raeburn   826:     if ($role_element eq '') {
                    827:         $setsections .= '    return;
                    828: }
                    829: ';
                    830:     } else {
                    831:         $setsections .= qq|
                    832:     var elementLength = document.$formname.$role_element.length;
                    833:     var allroles = Array($rolestr);
                    834:     var courserolenames = Array($courserolestr);
                    835:     var communityrolenames = Array($communityrolestr);
                    836:     if (elementLength != undefined) {
                    837:         if (document.$formname.$role_element.options[5].value == 'cc') {
                    838:             if (crstype == 'Course') {
                    839:                 return;
                    840:             } else {
                    841:                 allroles[5] = 'co';
                    842:                 for (var i=0; i<6; i++) {
                    843:                     document.$formname.$role_element.options[i].value = allroles[i];
                    844:                     document.$formname.$role_element.options[i].text = communityrolenames[i];
                    845:                 }
                    846:             }
                    847:         } else {
                    848:             if (crstype == 'Community') {
                    849:                 return;
                    850:             } else {
                    851:                 allroles[5] = 'cc';
                    852:                 for (var i=0; i<6; i++) {
                    853:                     document.$formname.$role_element.options[i].value = allroles[i];
                    854:                     document.$formname.$role_element.options[i].text = courserolenames[i];
                    855:                 }
                    856:             }
                    857:         }
                    858:     }
                    859:     return;
                    860: }
                    861: |;
                    862:     }
1.1116    raeburn   863:     if ($credits_element) {
                    864:         $setsections .= qq|
                    865: function setCredits(defaultcredits) {
                    866:     document.$formname.$credits_element.value = defaultcredits;
                    867:     return;
                    868: }
                    869: |;
                    870:     }
1.468     raeburn   871:     return $setsections;
                    872: }
                    873: 
1.91      www       874: sub selectcourse_link {
1.909     raeburn   875:    my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype,
                    876:        $typeelement) = @_;
                    877:    my $type = $selecttype;
1.871     raeburn   878:    my $linktext = &mt('Select Course');
                    879:    if ($selecttype eq 'Community') {
1.909     raeburn   880:        $linktext = &mt('Select Community');
1.906     raeburn   881:    } elsif ($selecttype eq 'Course/Community') {
                    882:        $linktext = &mt('Select Course/Community');
1.909     raeburn   883:        $type = '';
1.1019    raeburn   884:    } elsif ($selecttype eq 'Select') {
                    885:        $linktext = &mt('Select');
                    886:        $type = '';
1.871     raeburn   887:    }
1.787     bisitz    888:    return '<span class="LC_nobreak">'
                    889:          ."<a href='"
                    890:          .'javascript:opencrsbrowser("'.$form.'","'.$unameele
                    891:          .'","'.$udomele.'","'.$desc.'","'.$extra_element
1.909     raeburn   892:          .'","'.$multflag.'","'.$type.'","'.$typeelement.'");'
1.871     raeburn   893:          ."'>".$linktext.'</a>'
1.787     bisitz    894:          .'</span>';
1.74      www       895: }
1.42      matthew   896: 
1.653     raeburn   897: sub selectauthor_link {
                    898:    my ($form,$udom)=@_;
                    899:    return '<a href="javascript:openauthorbrowser('."'$form','$udom'".');">'.
                    900:           &mt('Select Author').'</a>';
                    901: }
                    902: 
1.876     raeburn   903: sub selectuser_link {
1.881     raeburn   904:     my ($form,$unameelem,$domelem,$lastelem,$firstelem,$emailelem,$hdomelem,
1.888     raeburn   905:         $coursedom,$linktext,$caller) = @_;
1.876     raeburn   906:     return '<a href="javascript:openuserbrowser('."'$form','$unameelem','$domelem',".
1.888     raeburn   907:            "'$lastelem','$firstelem','$emailelem','$hdomelem','$coursedom','$caller'".
1.881     raeburn   908:            ');">'.$linktext.'</a>';
1.876     raeburn   909: }
                    910: 
1.273     raeburn   911: sub check_uncheck_jscript {
                    912:     my $jscript = <<"ENDSCRT";
                    913: function checkAll(field) {
                    914:     if (field.length > 0) {
                    915:         for (i = 0; i < field.length; i++) {
1.1093    raeburn   916:             if (!field[i].disabled) { 
                    917:                 field[i].checked = true;
                    918:             }
1.273     raeburn   919:         }
                    920:     } else {
1.1093    raeburn   921:         if (!field.disabled) { 
                    922:             field.checked = true;
                    923:         }
1.273     raeburn   924:     }
                    925: }
                    926:  
                    927: function uncheckAll(field) {
                    928:     if (field.length > 0) {
                    929:         for (i = 0; i < field.length; i++) {
                    930:             field[i].checked = false ;
1.543     albertel  931:         }
                    932:     } else {
1.273     raeburn   933:         field.checked = false ;
                    934:     }
                    935: }
                    936: ENDSCRT
                    937:     return $jscript;
                    938: }
                    939: 
1.656     www       940: sub select_timezone {
1.659     raeburn   941:    my ($name,$selected,$onchange,$includeempty)=@_;
                    942:    my $output='<select name="'.$name.'" '.$onchange.'>'."\n";
                    943:    if ($includeempty) {
                    944:        $output .= '<option value=""';
                    945:        if (($selected eq '') || ($selected eq 'local')) {
                    946:            $output .= ' selected="selected" ';
                    947:        }
                    948:        $output .= '> </option>';
                    949:    }
1.657     raeburn   950:    my @timezones = DateTime::TimeZone->all_names;
                    951:    foreach my $tzone (@timezones) {
                    952:        $output.= '<option value="'.$tzone.'"';
                    953:        if ($tzone eq $selected) {
                    954:            $output.=' selected="selected"';
                    955:        }
                    956:        $output.=">$tzone</option>\n";
1.656     www       957:    }
                    958:    $output.="</select>";
                    959:    return $output;
                    960: }
1.273     raeburn   961: 
1.687     raeburn   962: sub select_datelocale {
                    963:     my ($name,$selected,$onchange,$includeempty)=@_;
                    964:     my $output='<select name="'.$name.'" '.$onchange.'>'."\n";
                    965:     if ($includeempty) {
                    966:         $output .= '<option value=""';
                    967:         if ($selected eq '') {
                    968:             $output .= ' selected="selected" ';
                    969:         }
                    970:         $output .= '> </option>';
                    971:     }
                    972:     my (@possibles,%locale_names);
                    973:     my @locales = DateTime::Locale::Catalog::Locales;
                    974:     foreach my $locale (@locales) {
                    975:         if (ref($locale) eq 'HASH') {
                    976:             my $id = $locale->{'id'};
                    977:             if ($id ne '') {
                    978:                 my $en_terr = $locale->{'en_territory'};
                    979:                 my $native_terr = $locale->{'native_territory'};
1.695     raeburn   980:                 my @languages = &Apache::lonlocal::preferred_languages();
1.687     raeburn   981:                 if (grep(/^en$/,@languages) || !@languages) {
                    982:                     if ($en_terr ne '') {
                    983:                         $locale_names{$id} = '('.$en_terr.')';
                    984:                     } elsif ($native_terr ne '') {
                    985:                         $locale_names{$id} = $native_terr;
                    986:                     }
                    987:                 } else {
                    988:                     if ($native_terr ne '') {
                    989:                         $locale_names{$id} = $native_terr.' ';
                    990:                     } elsif ($en_terr ne '') {
                    991:                         $locale_names{$id} = '('.$en_terr.')';
                    992:                     }
                    993:                 }
1.1220    raeburn   994:                 $locale_names{$id} = Encode::encode('UTF-8',$locale_names{$id});
1.687     raeburn   995:                 push (@possibles,$id);
                    996:             }
                    997:         }
                    998:     }
                    999:     foreach my $item (sort(@possibles)) {
                   1000:         $output.= '<option value="'.$item.'"';
                   1001:         if ($item eq $selected) {
                   1002:             $output.=' selected="selected"';
                   1003:         }
                   1004:         $output.=">$item";
                   1005:         if ($locale_names{$item} ne '') {
1.1220    raeburn  1006:             $output.='  '.$locale_names{$item};
1.687     raeburn  1007:         }
                   1008:         $output.="</option>\n";
                   1009:     }
                   1010:     $output.="</select>";
                   1011:     return $output;
                   1012: }
                   1013: 
1.792     raeburn  1014: sub select_language {
                   1015:     my ($name,$selected,$includeempty) = @_;
                   1016:     my %langchoices;
                   1017:     if ($includeempty) {
1.1117    raeburn  1018:         %langchoices = ('' => 'No language preference');
1.792     raeburn  1019:     }
                   1020:     foreach my $id (&languageids()) {
                   1021:         my $code = &supportedlanguagecode($id);
                   1022:         if ($code) {
                   1023:             $langchoices{$code} = &plainlanguagedescription($id);
                   1024:         }
                   1025:     }
1.1117    raeburn  1026:     %langchoices = &Apache::lonlocal::texthash(%langchoices);
1.970     raeburn  1027:     return &select_form($selected,$name,\%langchoices);
1.792     raeburn  1028: }
                   1029: 
1.42      matthew  1030: =pod
1.36      matthew  1031: 
1.1088    foxr     1032: 
                   1033: =item * &list_languages()
                   1034: 
                   1035: Returns an array reference that is suitable for use in language prompters.
                   1036: Each array element is itself a two element array.  The first element
                   1037: is the language code.  The second element a descsriptiuon of the 
                   1038: language itself.  This is suitable for use in e.g.
                   1039: &Apache::edit::select_arg (once dereferenced that is).
                   1040: 
                   1041: =cut 
                   1042: 
                   1043: sub list_languages {
                   1044:     my @lang_choices;
                   1045: 
                   1046:     foreach my $id (&languageids()) {
                   1047: 	my $code = &supportedlanguagecode($id);
                   1048: 	if ($code) {
                   1049: 	    my $selector    = $supported_codes{$id};
                   1050: 	    my $description = &plainlanguagedescription($id);
                   1051: 	    push (@lang_choices, [$selector, $description]);
                   1052: 	}
                   1053:     }
                   1054:     return \@lang_choices;
                   1055: }
                   1056: 
                   1057: =pod
                   1058: 
1.648     raeburn  1059: =item * &linked_select_forms(...)
1.36      matthew  1060: 
                   1061: linked_select_forms returns a string containing a <script></script> block
                   1062: and html for two <select> menus.  The select menus will be linked in that
                   1063: changing the value of the first menu will result in new values being placed
                   1064: in the second menu.  The values in the select menu will appear in alphabetical
1.609     raeburn  1065: order unless a defined order is provided.
1.36      matthew  1066: 
                   1067: linked_select_forms takes the following ordered inputs:
                   1068: 
                   1069: =over 4
                   1070: 
1.112     bowersj2 1071: =item * $formname, the name of the <form> tag
1.36      matthew  1072: 
1.112     bowersj2 1073: =item * $middletext, the text which appears between the <select> tags
1.36      matthew  1074: 
1.112     bowersj2 1075: =item * $firstdefault, the default value for the first menu
1.36      matthew  1076: 
1.112     bowersj2 1077: =item * $firstselectname, the name of the first <select> tag
1.36      matthew  1078: 
1.112     bowersj2 1079: =item * $secondselectname, the name of the second <select> tag
1.36      matthew  1080: 
1.112     bowersj2 1081: =item * $hashref, a reference to a hash containing the data for the menus.
1.36      matthew  1082: 
1.609     raeburn  1083: =item * $menuorder, the order of values in the first menu
                   1084: 
1.1115    raeburn  1085: =item * $onchangefirst, additional javascript call to execute for an onchange
                   1086:         event for the first <select> tag
                   1087: 
                   1088: =item * $onchangesecond, additional javascript call to execute for an onchange
                   1089:         event for the second <select> tag
                   1090: 
1.41      ng       1091: =back 
                   1092: 
1.36      matthew  1093: Below is an example of such a hash.  Only the 'text', 'default', and 
                   1094: 'select2' keys must appear as stated.  keys(%menu) are the possible 
                   1095: values for the first select menu.  The text that coincides with the 
1.41      ng       1096: first menu value is given in $menu{$choice1}->{'text'}.  The values 
1.36      matthew  1097: and text for the second menu are given in the hash pointed to by 
                   1098: $menu{$choice1}->{'select2'}.  
                   1099: 
1.112     bowersj2 1100:  my %menu = ( A1 => { text =>"Choice A1" ,
                   1101:                        default => "B3",
                   1102:                        select2 => { 
                   1103:                            B1 => "Choice B1",
                   1104:                            B2 => "Choice B2",
                   1105:                            B3 => "Choice B3",
                   1106:                            B4 => "Choice B4"
1.609     raeburn  1107:                            },
                   1108:                        order => ['B4','B3','B1','B2'],
1.112     bowersj2 1109:                    },
                   1110:                A2 => { text =>"Choice A2" ,
                   1111:                        default => "C2",
                   1112:                        select2 => { 
                   1113:                            C1 => "Choice C1",
                   1114:                            C2 => "Choice C2",
                   1115:                            C3 => "Choice C3"
1.609     raeburn  1116:                            },
                   1117:                        order => ['C2','C1','C3'],
1.112     bowersj2 1118:                    },
                   1119:                A3 => { text =>"Choice A3" ,
                   1120:                        default => "D6",
                   1121:                        select2 => { 
                   1122:                            D1 => "Choice D1",
                   1123:                            D2 => "Choice D2",
                   1124:                            D3 => "Choice D3",
                   1125:                            D4 => "Choice D4",
                   1126:                            D5 => "Choice D5",
                   1127:                            D6 => "Choice D6",
                   1128:                            D7 => "Choice D7"
1.609     raeburn  1129:                            },
                   1130:                        order => ['D4','D3','D2','D1','D7','D6','D5'],
1.112     bowersj2 1131:                    }
                   1132:                );
1.36      matthew  1133: 
                   1134: =cut
                   1135: 
                   1136: sub linked_select_forms {
                   1137:     my ($formname,
                   1138:         $middletext,
                   1139:         $firstdefault,
                   1140:         $firstselectname,
                   1141:         $secondselectname, 
1.609     raeburn  1142:         $hashref,
                   1143:         $menuorder,
1.1115    raeburn  1144:         $onchangefirst,
                   1145:         $onchangesecond
1.36      matthew  1146:         ) = @_;
                   1147:     my $second = "document.$formname.$secondselectname";
                   1148:     my $first = "document.$formname.$firstselectname";
                   1149:     # output the javascript to do the changing
                   1150:     my $result = '';
1.776     bisitz   1151:     $result.='<script type="text/javascript" language="JavaScript">'."\n";
1.824     bisitz   1152:     $result.="// <![CDATA[\n";
1.36      matthew  1153:     $result.="var select2data = new Object();\n";
                   1154:     $" = '","';
                   1155:     my $debug = '';
                   1156:     foreach my $s1 (sort(keys(%$hashref))) {
                   1157:         $result.="select2data.d_$s1 = new Object();\n";        
                   1158:         $result.="select2data.d_$s1.def = new String('".
                   1159:             $hashref->{$s1}->{'default'}."');\n";
1.609     raeburn  1160:         $result.="select2data.d_$s1.values = new Array(";
1.36      matthew  1161:         my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
1.609     raeburn  1162:         if (ref($hashref->{$s1}->{'order'}) eq 'ARRAY') {
                   1163:             @s2values = @{$hashref->{$s1}->{'order'}};
                   1164:         }
1.36      matthew  1165:         $result.="\"@s2values\");\n";
                   1166:         $result.="select2data.d_$s1.texts = new Array(";        
                   1167:         my @s2texts;
                   1168:         foreach my $value (@s2values) {
                   1169:             push @s2texts, $hashref->{$s1}->{'select2'}->{$value};
                   1170:         }
                   1171:         $result.="\"@s2texts\");\n";
                   1172:     }
                   1173:     $"=' ';
                   1174:     $result.= <<"END";
                   1175: 
                   1176: function select1_changed() {
                   1177:     // Determine new choice
                   1178:     var newvalue = "d_" + $first.value;
                   1179:     // update select2
                   1180:     var values     = select2data[newvalue].values;
                   1181:     var texts      = select2data[newvalue].texts;
                   1182:     var select2def = select2data[newvalue].def;
                   1183:     var i;
                   1184:     // out with the old
                   1185:     for (i = 0; i < $second.options.length; i++) {
                   1186:         $second.options[i] = null;
                   1187:     }
                   1188:     // in with the nuclear
                   1189:     for (i=0;i<values.length; i++) {
                   1190:         $second.options[i] = new Option(values[i]);
1.143     matthew  1191:         $second.options[i].value = values[i];
1.36      matthew  1192:         $second.options[i].text = texts[i];
                   1193:         if (values[i] == select2def) {
                   1194:             $second.options[i].selected = true;
                   1195:         }
                   1196:     }
                   1197: }
1.824     bisitz   1198: // ]]>
1.36      matthew  1199: </script>
                   1200: END
                   1201:     # output the initial values for the selection lists
1.1115    raeburn  1202:     $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1_changed();$onchangefirst\">\n";
1.609     raeburn  1203:     my @order = sort(keys(%{$hashref}));
                   1204:     if (ref($menuorder) eq 'ARRAY') {
                   1205:         @order = @{$menuorder};
                   1206:     }
                   1207:     foreach my $value (@order) {
1.36      matthew  1208:         $result.="    <option value=\"$value\" ";
1.253     albertel 1209:         $result.=" selected=\"selected\" " if ($value eq $firstdefault);
1.119     www      1210:         $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
1.36      matthew  1211:     }
                   1212:     $result .= "</select>\n";
                   1213:     my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
                   1214:     $result .= $middletext;
1.1115    raeburn  1215:     $result .= "<select size=\"1\" name=\"$secondselectname\"";
                   1216:     if ($onchangesecond) {
                   1217:         $result .= ' onchange="'.$onchangesecond.'"';
                   1218:     }
                   1219:     $result .= ">\n";
1.36      matthew  1220:     my $seconddefault = $hashref->{$firstdefault}->{'default'};
1.609     raeburn  1221:     
                   1222:     my @secondorder = sort(keys(%select2));
                   1223:     if (ref($hashref->{$firstdefault}->{'order'}) eq 'ARRAY') {
                   1224:         @secondorder = @{$hashref->{$firstdefault}->{'order'}};
                   1225:     }
                   1226:     foreach my $value (@secondorder) {
1.36      matthew  1227:         $result.="    <option value=\"$value\" ";        
1.253     albertel 1228:         $result.=" selected=\"selected\" " if ($value eq $seconddefault);
1.119     www      1229:         $result.=">".&mt($select2{$value})."</option>\n";
1.36      matthew  1230:     }
                   1231:     $result .= "</select>\n";
                   1232:     #    return $debug;
                   1233:     return $result;
                   1234: }   #  end of sub linked_select_forms {
                   1235: 
1.45      matthew  1236: =pod
1.44      bowersj2 1237: 
1.973     raeburn  1238: =item * &help_open_topic($topic,$text,$stayOnPage,$width,$height,$imgid)
1.44      bowersj2 1239: 
1.112     bowersj2 1240: Returns a string corresponding to an HTML link to the given help
                   1241: $topic, where $topic corresponds to the name of a .tex file in
                   1242: /home/httpd/html/adm/help/tex, with underscores replaced by
                   1243: spaces. 
                   1244: 
                   1245: $text will optionally be linked to the same topic, allowing you to
                   1246: link text in addition to the graphic. If you do not want to link
                   1247: text, but wish to specify one of the later parameters, pass an
                   1248: empty string. 
                   1249: 
                   1250: $stayOnPage is a value that will be interpreted as a boolean. If true,
                   1251: the link will not open a new window. If false, the link will open
                   1252: a new window using Javascript. (Default is false.) 
                   1253: 
                   1254: $width and $height are optional numerical parameters that will
                   1255: override the width and height of the popped up window, which may
1.973     raeburn  1256: be useful for certain help topics with big pictures included.
                   1257: 
                   1258: $imgid is the id of the img tag used for the help icon. This may be
                   1259: used in a javascript call to switch the image src.  See 
                   1260: lonhtmlcommon::htmlareaselectactive() for an example.
1.44      bowersj2 1261: 
                   1262: =cut
                   1263: 
                   1264: sub help_open_topic {
1.973     raeburn  1265:     my ($topic, $text, $stayOnPage, $width, $height, $imgid) = @_;
1.48      bowersj2 1266:     $text = "" if (not defined $text);
1.44      bowersj2 1267:     $stayOnPage = 0 if (not defined $stayOnPage);
1.1033    www      1268:     $width = 500 if (not defined $width);
1.44      bowersj2 1269:     $height = 400 if (not defined $height);
                   1270:     my $filename = $topic;
                   1271:     $filename =~ s/ /_/g;
                   1272: 
1.48      bowersj2 1273:     my $template = "";
                   1274:     my $link;
1.572     banghart 1275:     
1.159     www      1276:     $topic=~s/\W/\_/g;
1.44      bowersj2 1277: 
1.572     banghart 1278:     if (!$stayOnPage) {
1.1033    www      1279: 	$link = "javascript:openMyModal('/adm/help/${filename}.hlp',$width,$height,'yes');";
1.1037    www      1280:     } elsif ($stayOnPage eq 'popup') {
                   1281:         $link = "javascript:void(open('/adm/help/${filename}.hlp', 'Help_for_$topic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
1.572     banghart 1282:     } else {
1.48      bowersj2 1283: 	$link = "/adm/help/${filename}.hlp";
                   1284:     }
                   1285: 
                   1286:     # Add the text
1.755     neumanie 1287:     if ($text ne "") {	
1.763     bisitz   1288: 	$template.='<span class="LC_help_open_topic">'
                   1289:                   .'<a target="_top" href="'.$link.'">'
                   1290:                   .$text.'</a>';
1.48      bowersj2 1291:     }
                   1292: 
1.763     bisitz   1293:     # (Always) Add the graphic
1.179     matthew  1294:     my $title = &mt('Online Help');
1.667     raeburn  1295:     my $helpicon=&lonhttpdurl("/adm/help/help.png");
1.973     raeburn  1296:     if ($imgid ne '') {
                   1297:         $imgid = ' id="'.$imgid.'"';
                   1298:     }
1.763     bisitz   1299:     $template.=' <a target="_top" href="'.$link.'" title="'.$title.'">'
                   1300:               .'<img src="'.$helpicon.'" border="0"'
                   1301:               .' alt="'.&mt('Help: [_1]',$topic).'"'
1.973     raeburn  1302:               .' title="'.$title.'" style="vertical-align:middle;"'.$imgid 
1.763     bisitz   1303:               .' /></a>';
                   1304:     if ($text ne "") {	
                   1305:         $template.='</span>';
                   1306:     }
1.44      bowersj2 1307:     return $template;
                   1308: 
1.106     bowersj2 1309: }
                   1310: 
                   1311: # This is a quicky function for Latex cheatsheet editing, since it 
                   1312: # appears in at least four places
                   1313: sub helpLatexCheatsheet {
1.1037    www      1314:     my ($topic,$text,$not_author,$stayOnPage) = @_;
1.732     raeburn  1315:     my $out;
1.106     bowersj2 1316:     my $addOther = '';
1.732     raeburn  1317:     if ($topic) {
1.1037    www      1318: 	$addOther = '<span>'.&help_open_topic($topic,&mt($text),$stayOnPage, undef, 600).'</span> ';
1.763     bisitz   1319:     }
                   1320:     $out = '<span>' # Start cheatsheet
                   1321: 	  .$addOther
                   1322:           .'<span>'
1.1037    www      1323: 	  .&help_open_topic('Greek_Symbols',&mt('Greek Symbols'),$stayOnPage,undef,600)
1.763     bisitz   1324: 	  .'</span> <span>'
1.1037    www      1325: 	  .&help_open_topic('Other_Symbols',&mt('Other Symbols'),$stayOnPage,undef,600)
1.763     bisitz   1326: 	  .'</span>';
1.732     raeburn  1327:     unless ($not_author) {
1.1186    kruse    1328:         $out .= '<span>'
                   1329:                .&help_open_topic('Authoring_Output_Tags',&mt('Output Tags'),$stayOnPage,undef,600)
                   1330:                .'</span> <span>'
                   1331:                .&help_open_topic('Authoring_Multilingual_Problems',&mt('How to create problems in different languages'),$stayOnPage,undef,600)
1.763     bisitz   1332: 	       .'</span>';
1.732     raeburn  1333:     }
1.763     bisitz   1334:     $out .= '</span>'; # End cheatsheet
1.732     raeburn  1335:     return $out;
1.172     www      1336: }
                   1337: 
1.430     albertel 1338: sub general_help {
                   1339:     my $helptopic='Student_Intro';
                   1340:     if ($env{'request.role'}=~/^(ca|au)/) {
                   1341: 	$helptopic='Authoring_Intro';
1.907     raeburn  1342:     } elsif ($env{'request.role'}=~/^(cc|co)/) {
1.430     albertel 1343: 	$helptopic='Course_Coordination_Intro';
1.672     raeburn  1344:     } elsif ($env{'request.role'}=~/^dc/) {
                   1345:         $helptopic='Domain_Coordination_Intro';
1.430     albertel 1346:     }
                   1347:     return $helptopic;
                   1348: }
                   1349: 
                   1350: sub update_help_link {
                   1351:     my ($topic,$component_help,$faq,$bug,$stayOnPage) = @_;
                   1352:     my $origurl = $ENV{'REQUEST_URI'};
                   1353:     $origurl=~s|^/~|/priv/|;
                   1354:     my $timestamp = time;
                   1355:     foreach my $datum (\$topic,\$component_help,\$faq,\$bug,\$origurl) {
                   1356:         $$datum = &escape($$datum);
                   1357:     }
                   1358: 
                   1359:     my $banner_link = "/adm/helpmenu?page=banner&amp;topic=$topic&amp;component_help=$component_help&amp;faq=$faq&amp;bug=$bug&amp;origurl=$origurl&amp;stamp=$timestamp&amp;stayonpage=$stayOnPage";
                   1360:     my $output .= <<"ENDOUTPUT";
                   1361: <script type="text/javascript">
1.824     bisitz   1362: // <![CDATA[
1.430     albertel 1363: banner_link = '$banner_link';
1.824     bisitz   1364: // ]]>
1.430     albertel 1365: </script>
                   1366: ENDOUTPUT
                   1367:     return $output;
                   1368: }
                   1369: 
                   1370: # now just updates the help link and generates a blue icon
1.193     raeburn  1371: sub help_open_menu {
1.430     albertel 1372:     my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text) 
1.552     banghart 1373: 	= @_;    
1.949     droeschl 1374:     $stayOnPage = 1;
1.430     albertel 1375:     my $output;
                   1376:     if ($component_help) {
                   1377: 	if (!$text) {
                   1378: 	    $output=&help_open_topic($component_help,undef,$stayOnPage,
                   1379: 				       $width,$height);
                   1380: 	} else {
                   1381: 	    my $help_text;
                   1382: 	    $help_text=&unescape($topic);
                   1383: 	    $output='<table><tr><td>'.
                   1384: 		&help_open_topic($component_help,$help_text,$stayOnPage,
                   1385: 				 $width,$height).'</td></tr></table>';
                   1386: 	}
                   1387:     }
                   1388:     my $banner_link = &update_help_link($topic,$component_help,$faq,$bug,$stayOnPage);
                   1389:     return $output.$banner_link;
                   1390: }
                   1391: 
                   1392: sub top_nav_help {
                   1393:     my ($text) = @_;
1.436     albertel 1394:     $text = &mt($text);
1.949     droeschl 1395:     my $stay_on_page = 1;
                   1396: 
1.1168    raeburn  1397:     my ($link,$banner_link);
                   1398:     unless ($env{'request.noversionuri'} =~ m{^/adm/helpmenu}) {
                   1399:         $link = ($stay_on_page) ? "javascript:helpMenu('display')"
                   1400: 	                         : "javascript:helpMenu('open')";
                   1401:         $banner_link = &update_help_link(undef,undef,undef,undef,$stay_on_page);
                   1402:     }
1.201     raeburn  1403:     my $title = &mt('Get help');
1.1168    raeburn  1404:     if ($link) {
                   1405:         return <<"END";
1.436     albertel 1406: $banner_link
1.1159    raeburn  1407: <a href="$link" title="$title">$text</a>
1.436     albertel 1408: END
1.1168    raeburn  1409:     } else {
                   1410:         return '&nbsp;'.$text.'&nbsp;';
                   1411:     }
1.436     albertel 1412: }
                   1413: 
                   1414: sub help_menu_js {
1.1154    raeburn  1415:     my ($httphost) = @_;
1.949     droeschl 1416:     my $stayOnPage = 1;
1.436     albertel 1417:     my $width = 620;
                   1418:     my $height = 600;
1.430     albertel 1419:     my $helptopic=&general_help();
1.1154    raeburn  1420:     my $details_link = $httphost.'/adm/help/'.$helptopic.'.hlp';
1.261     albertel 1421:     my $nothing=&Apache::lonhtmlcommon::javascript_nothing();
1.331     albertel 1422:     my $start_page =
                   1423:         &Apache::loncommon::start_page('Help Menu', undef,
                   1424: 				       {'frameset'    => 1,
                   1425: 					'js_ready'    => 1,
1.1154    raeburn  1426:                                         'use_absolute' => $httphost,
1.331     albertel 1427: 					'add_entries' => {
1.1168    raeburn  1428: 					    'border' => '0', 
1.579     raeburn  1429: 					    'rows'   => "110,*",},});
1.331     albertel 1430:     my $end_page =
                   1431:         &Apache::loncommon::end_page({'frameset' => 1,
                   1432: 				      'js_ready' => 1,});
                   1433: 
1.436     albertel 1434:     my $template .= <<"ENDTEMPLATE";
                   1435: <script type="text/javascript">
1.877     bisitz   1436: // <![CDATA[
1.253     albertel 1437: // <!-- BEGIN LON-CAPA Internal
1.430     albertel 1438: var banner_link = '';
1.243     raeburn  1439: function helpMenu(target) {
                   1440:     var caller = this;
                   1441:     if (target == 'open') {
                   1442:         var newWindow = null;
                   1443:         try {
1.262     albertel 1444:             newWindow =  window.open($nothing,"helpmenu","HEIGHT=$height,WIDTH=$width,resizable=yes,scrollbars=yes" )
1.243     raeburn  1445:         }
                   1446:         catch(error) {
                   1447:             writeHelp(caller);
                   1448:             return;
                   1449:         }
                   1450:         if (newWindow) {
                   1451:             caller = newWindow;
                   1452:         }
1.193     raeburn  1453:     }
1.243     raeburn  1454:     writeHelp(caller);
                   1455:     return;
                   1456: }
                   1457: function writeHelp(caller) {
1.1168    raeburn  1458:     caller.document.writeln('$start_page\\n<frame name="bannerframe" src="'+banner_link+'" marginwidth="0" marginheight="0" frameborder="0">\\n');
                   1459:     caller.document.writeln('<frame name="bodyframe" src="$details_link" marginwidth="0" marginheight="0" frameborder="0">\\n$end_page');
                   1460:     caller.document.close();
                   1461:     caller.focus();
1.193     raeburn  1462: }
1.877     bisitz   1463: // END LON-CAPA Internal -->
1.253     albertel 1464: // ]]>
1.436     albertel 1465: </script>
1.193     raeburn  1466: ENDTEMPLATE
                   1467:     return $template;
                   1468: }
                   1469: 
1.172     www      1470: sub help_open_bug {
                   1471:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel 1472:     unless ($env{'user.adv'}) { return ''; }
1.172     www      1473:     unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; }
                   1474:     $text = "" if (not defined $text);
                   1475: 	$stayOnPage=1;
1.184     albertel 1476:     $width = 600 if (not defined $width);
                   1477:     $height = 600 if (not defined $height);
1.172     www      1478: 
                   1479:     $topic=~s/\W+/\+/g;
                   1480:     my $link='';
                   1481:     my $template='';
1.379     albertel 1482:     my $url=$Apache::lonnet::perlvar{'BugzillaHost'}.'enter_bug.cgi?product=LON-CAPA&amp;bug_file_loc='.
                   1483: 	&escape($ENV{'REQUEST_URI'}).'&amp;component='.$topic;
1.172     www      1484:     if (!$stayOnPage)
                   1485:     {
                   1486: 	$link = "javascript:void(open('$url', 'Bugzilla', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1487:     }
                   1488:     else
                   1489:     {
                   1490: 	$link = $url;
                   1491:     }
                   1492:     # Add the text
                   1493:     if ($text ne "")
                   1494:     {
                   1495: 	$template .= 
                   1496:   "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
1.705     tempelho 1497:   "<td bgcolor='#FF5555'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF;font-size:10pt;\">$text</span></a>";
1.172     www      1498:     }
                   1499: 
                   1500:     # Add the graphic
1.179     matthew  1501:     my $title = &mt('Report a Bug');
1.215     albertel 1502:     my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");
1.172     www      1503:     $template .= <<"ENDTEMPLATE";
1.436     albertel 1504:  <a target="_top" href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
1.172     www      1505: ENDTEMPLATE
                   1506:     if ($text ne '') { $template.='</td></tr></table>' };
                   1507:     return $template;
                   1508: 
                   1509: }
                   1510: 
                   1511: sub help_open_faq {
                   1512:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel 1513:     unless ($env{'user.adv'}) { return ''; }
1.172     www      1514:     unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; }
                   1515:     $text = "" if (not defined $text);
                   1516: 	$stayOnPage=1;
                   1517:     $width = 350 if (not defined $width);
                   1518:     $height = 400 if (not defined $height);
                   1519: 
                   1520:     $topic=~s/\W+/\+/g;
                   1521:     my $link='';
                   1522:     my $template='';
                   1523:     my $url=$Apache::lonnet::perlvar{'FAQHost'}.'/fom/cache/'.$topic.'.html';
                   1524:     if (!$stayOnPage)
                   1525:     {
                   1526: 	$link = "javascript:void(open('$url', 'FAQ-O-Matic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1527:     }
                   1528:     else
                   1529:     {
                   1530: 	$link = $url;
                   1531:     }
                   1532: 
                   1533:     # Add the text
                   1534:     if ($text ne "")
                   1535:     {
                   1536: 	$template .= 
1.173     www      1537:   "<table bgcolor='#337733' cellspacing='1' cellpadding='1' border='0'><tr>".
1.705     tempelho 1538:   "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF; font-size:10pt;\">$text</span></a>";
1.172     www      1539:     }
                   1540: 
                   1541:     # Add the graphic
1.179     matthew  1542:     my $title = &mt('View the FAQ');
1.215     albertel 1543:     my $faqicon=&lonhttpdurl("/adm/lonMisc/smallFAQ.gif");
1.172     www      1544:     $template .= <<"ENDTEMPLATE";
1.436     albertel 1545:  <a target="_top" href="$link" title="$title"><img src="$faqicon" border="0" alt="(FAQ: $topic)" /></a>
1.172     www      1546: ENDTEMPLATE
                   1547:     if ($text ne '') { $template.='</td></tr></table>' };
                   1548:     return $template;
                   1549: 
1.44      bowersj2 1550: }
1.37      matthew  1551: 
1.180     matthew  1552: ###############################################################
                   1553: ###############################################################
                   1554: 
1.45      matthew  1555: =pod
                   1556: 
1.648     raeburn  1557: =item * &change_content_javascript():
1.256     matthew  1558: 
                   1559: This and the next function allow you to create small sections of an
                   1560: otherwise static HTML page that you can update on the fly with
                   1561: Javascript, even in Netscape 4.
                   1562: 
                   1563: The Javascript fragment returned by this function (no E<lt>scriptE<gt> tag)
                   1564: must be written to the HTML page once. It will prove the Javascript
                   1565: function "change(name, content)". Calling the change function with the
                   1566: name of the section 
                   1567: you want to update, matching the name passed to C<changable_area>, and
                   1568: the new content you want to put in there, will put the content into
                   1569: that area.
                   1570: 
                   1571: B<Note>: Netscape 4 only reserves enough space for the changable area
                   1572: to contain room for the original contents. You need to "make space"
                   1573: for whatever changes you wish to make, and be B<sure> to check your
                   1574: code in Netscape 4. This feature in Netscape 4 is B<not> powerful;
                   1575: it's adequate for updating a one-line status display, but little more.
                   1576: This script will set the space to 100% width, so you only need to
                   1577: worry about height in Netscape 4.
                   1578: 
                   1579: Modern browsers are much less limiting, and if you can commit to the
                   1580: user not using Netscape 4, this feature may be used freely with
                   1581: pretty much any HTML.
                   1582: 
                   1583: =cut
                   1584: 
                   1585: sub change_content_javascript {
                   1586:     # If we're on Netscape 4, we need to use Layer-based code
1.258     albertel 1587:     if ($env{'browser.type'} eq 'netscape' &&
                   1588: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1589: 	return (<<NETSCAPE4);
                   1590: 	function change(name, content) {
                   1591: 	    doc = document.layers[name+"___escape"].layers[0].document;
                   1592: 	    doc.open();
                   1593: 	    doc.write(content);
                   1594: 	    doc.close();
                   1595: 	}
                   1596: NETSCAPE4
                   1597:     } else {
                   1598: 	# Otherwise, we need to use semi-standards-compliant code
                   1599: 	# (technically, "innerHTML" isn't standard but the equivalent
                   1600: 	# is really scary, and every useful browser supports it
                   1601: 	return (<<DOMBASED);
                   1602: 	function change(name, content) {
                   1603: 	    element = document.getElementById(name);
                   1604: 	    element.innerHTML = content;
                   1605: 	}
                   1606: DOMBASED
                   1607:     }
                   1608: }
                   1609: 
                   1610: =pod
                   1611: 
1.648     raeburn  1612: =item * &changable_area($name,$origContent):
1.256     matthew  1613: 
                   1614: This provides a "changable area" that can be modified on the fly via
                   1615: the Javascript code provided in C<change_content_javascript>. $name is
                   1616: the name you will use to reference the area later; do not repeat the
                   1617: same name on a given HTML page more then once. $origContent is what
                   1618: the area will originally contain, which can be left blank.
                   1619: 
                   1620: =cut
                   1621: 
                   1622: sub changable_area {
                   1623:     my ($name, $origContent) = @_;
                   1624: 
1.258     albertel 1625:     if ($env{'browser.type'} eq 'netscape' &&
                   1626: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1627: 	# If this is netscape 4, we need to use the Layer tag
                   1628: 	return "<ilayer width='100%' id='${name}___escape' overflow='none'><layer width='100%' id='$name' overflow='none'>$origContent</layer></ilayer>";
                   1629:     } else {
                   1630: 	return "<span id='$name'>$origContent</span>";
                   1631:     }
                   1632: }
                   1633: 
                   1634: =pod
                   1635: 
1.648     raeburn  1636: =item * &viewport_geometry_js 
1.590     raeburn  1637: 
                   1638: Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser.
                   1639: 
                   1640: =cut
                   1641: 
                   1642: 
                   1643: sub viewport_geometry_js { 
                   1644:     return <<"GEOMETRY";
                   1645: var Geometry = {};
                   1646: function init_geometry() {
                   1647:     if (Geometry.init) { return };
                   1648:     Geometry.init=1;
                   1649:     if (window.innerHeight) {
                   1650:         Geometry.getViewportHeight   = function() { return window.innerHeight; };
                   1651:         Geometry.getViewportWidth   = function() { return window.innerWidth; };
                   1652:         Geometry.getHorizontalScroll = function() { return window.pageXOffset; };
                   1653:         Geometry.getVerticalScroll   = function() { return window.pageYOffset; };
                   1654:     }
                   1655:     else if (document.documentElement && document.documentElement.clientHeight) {
                   1656:         Geometry.getViewportHeight =
                   1657:             function() { return document.documentElement.clientHeight; };
                   1658:         Geometry.getViewportWidth =
                   1659:             function() { return document.documentElement.clientWidth; };
                   1660: 
                   1661:         Geometry.getHorizontalScroll =
                   1662:             function() { return document.documentElement.scrollLeft; };
                   1663:         Geometry.getVerticalScroll =
                   1664:             function() { return document.documentElement.scrollTop; };
                   1665:     }
                   1666:     else if (document.body.clientHeight) {
                   1667:         Geometry.getViewportHeight =
                   1668:             function() { return document.body.clientHeight; };
                   1669:         Geometry.getViewportWidth =
                   1670:             function() { return document.body.clientWidth; };
                   1671:         Geometry.getHorizontalScroll =
                   1672:             function() { return document.body.scrollLeft; };
                   1673:         Geometry.getVerticalScroll =
                   1674:             function() { return document.body.scrollTop; };
                   1675:     }
                   1676: }
                   1677: 
                   1678: GEOMETRY
                   1679: }
                   1680: 
                   1681: =pod
                   1682: 
1.648     raeburn  1683: =item * &viewport_size_js()
1.590     raeburn  1684: 
                   1685: Provides a javascript function to set values of two form elements - width and height (elements are passed in as arguments to the javascript function) to the dimensions of the user's browser window. 
                   1686: 
                   1687: =cut
                   1688: 
                   1689: sub viewport_size_js {
                   1690:     my $geometry = &viewport_geometry_js();
                   1691:     return <<"DIMS";
                   1692: 
                   1693: $geometry
                   1694: 
                   1695: function getViewportDims(width,height) {
                   1696:     init_geometry();
                   1697:     width.value = Geometry.getViewportWidth();
                   1698:     height.value = Geometry.getViewportHeight();
                   1699:     return;
                   1700: }
                   1701: 
                   1702: DIMS
                   1703: }
                   1704: 
                   1705: =pod
                   1706: 
1.648     raeburn  1707: =item * &resize_textarea_js()
1.565     albertel 1708: 
                   1709: emits the needed javascript to resize a textarea to be as big as possible
                   1710: 
                   1711: creates a function resize_textrea that takes two IDs first should be
                   1712: the id of the element to resize, second should be the id of a div that
                   1713: surrounds everything that comes after the textarea, this routine needs
                   1714: to be attached to the <body> for the onload and onresize events.
                   1715: 
1.648     raeburn  1716: =back
1.565     albertel 1717: 
                   1718: =cut
                   1719: 
                   1720: sub resize_textarea_js {
1.590     raeburn  1721:     my $geometry = &viewport_geometry_js();
1.565     albertel 1722:     return <<"RESIZE";
                   1723:     <script type="text/javascript">
1.824     bisitz   1724: // <![CDATA[
1.590     raeburn  1725: $geometry
1.565     albertel 1726: 
1.588     albertel 1727: function getX(element) {
                   1728:     var x = 0;
                   1729:     while (element) {
                   1730: 	x += element.offsetLeft;
                   1731: 	element = element.offsetParent;
                   1732:     }
                   1733:     return x;
                   1734: }
                   1735: function getY(element) {
                   1736:     var y = 0;
                   1737:     while (element) {
                   1738: 	y += element.offsetTop;
                   1739: 	element = element.offsetParent;
                   1740:     }
                   1741:     return y;
                   1742: }
                   1743: 
                   1744: 
1.565     albertel 1745: function resize_textarea(textarea_id,bottom_id) {
                   1746:     init_geometry();
                   1747:     var textarea        = document.getElementById(textarea_id);
                   1748:     //alert(textarea);
                   1749: 
1.588     albertel 1750:     var textarea_top    = getY(textarea);
1.565     albertel 1751:     var textarea_height = textarea.offsetHeight;
                   1752:     var bottom          = document.getElementById(bottom_id);
1.588     albertel 1753:     var bottom_top      = getY(bottom);
1.565     albertel 1754:     var bottom_height   = bottom.offsetHeight;
                   1755:     var window_height   = Geometry.getViewportHeight();
1.588     albertel 1756:     var fudge           = 23;
1.565     albertel 1757:     var new_height      = window_height-fudge-textarea_top-bottom_height;
                   1758:     if (new_height < 300) {
                   1759: 	new_height = 300;
                   1760:     }
                   1761:     textarea.style.height=new_height+'px';
                   1762: }
1.824     bisitz   1763: // ]]>
1.565     albertel 1764: </script>
                   1765: RESIZE
                   1766: 
                   1767: }
                   1768: 
1.1205    golterma 1769: sub colorfuleditor_js {
                   1770:     return <<"COLORFULEDIT"
                   1771: <script type="text/javascript">
                   1772: // <![CDATA[>
                   1773:     function fold_box(curDepth, lastresource){
                   1774: 
                   1775:     // we need a list because there can be several blocks you need to fold in one tag
                   1776:         var block = document.getElementsByName('foldblock_'+curDepth);
                   1777:     // but there is only one folding button per tag
                   1778:         var foldbutton = document.getElementById('folding_btn_'+curDepth);
                   1779: 
                   1780:         if(block.item(0).style.display == 'none'){
                   1781: 
                   1782:             foldbutton.value = '@{[&mt("Hide")]}';
                   1783:             for (i = 0; i < block.length; i++){
                   1784:                 block.item(i).style.display = '';
                   1785:             }
                   1786:         }else{
                   1787: 
                   1788:             foldbutton.value = '@{[&mt("Show")]}';
                   1789:             for (i = 0; i < block.length; i++){
                   1790:                 // block.item(i).style.visibility = 'collapse';
                   1791:                 block.item(i).style.display = 'none';
                   1792:             }
                   1793:         };
                   1794:         saveState(lastresource);
                   1795:     }
                   1796: 
                   1797:     function saveState (lastresource) {
                   1798: 
                   1799:         var tag_list = getTagList();
                   1800:         if(tag_list != null){
                   1801:             var timestamp = new Date().getTime();
                   1802:             var key = lastresource;
                   1803: 
                   1804:             // the value pattern is: 'time;key1,value1;key2,value2; ... '
                   1805:             // starting with timestamp
                   1806:             var value = timestamp+';';
                   1807: 
                   1808:             // building the list of key-value pairs
                   1809:             for(var i = 0; i < tag_list.length; i++){
                   1810:                 value += tag_list[i]+',';
                   1811:                 value += document.getElementsByName(tag_list[i])[0].style.display+';';
                   1812:             }
                   1813: 
                   1814:             // only iterate whole storage if nothing to override
                   1815:             if(localStorage.getItem(key) == null){        
                   1816: 
                   1817:                 // prevent storage from growing large
                   1818:                 if(localStorage.length > 50){
                   1819:                     var regex_getTimestamp = /^(?:\d)+;/;
                   1820:                     var oldest_timestamp = regex_getTimestamp.exec(localStorage.key(0));
                   1821:                     var oldest_key;
                   1822:                     
                   1823:                     for(var i = 1; i < localStorage.length; i++){
                   1824:                         if (regex_getTimestamp.exec(localStorage.key(i)) < oldest_timestamp) {
                   1825:                             oldest_key = localStorage.key(i);
                   1826:                             oldest_timestamp = regex_getTimestamp.exec(oldest_key);
                   1827:                         }
                   1828:                     }
                   1829:                     localStorage.removeItem(oldest_key);
                   1830:                 }
                   1831:             }
                   1832:             localStorage.setItem(key,value);
                   1833:         }
                   1834:     }
                   1835: 
                   1836:     // restore folding status of blocks (on page load)
                   1837:     function restoreState (lastresource) {
                   1838:         if(localStorage.getItem(lastresource) != null){
                   1839:             var key = lastresource;
                   1840:             var value = localStorage.getItem(key);
                   1841:             var regex_delTimestamp = /^\d+;/;
                   1842: 
                   1843:             value.replace(regex_delTimestamp, '');
                   1844: 
                   1845:             var valueArr = value.split(';');
                   1846:             var pairs;
                   1847:             var elements;
                   1848:             for (var i = 0; i < valueArr.length; i++){
                   1849:                 pairs = valueArr[i].split(',');
                   1850:                 elements = document.getElementsByName(pairs[0]);
                   1851: 
                   1852:                 for (var j = 0; j < elements.length; j++){  
                   1853:                     elements[j].style.display = pairs[1];
                   1854:                     if (pairs[1] == "none"){
                   1855:                         var regex_id = /([_\\d]+)\$/;
                   1856:                         regex_id.exec(pairs[0]);
                   1857:                         document.getElementById("folding_btn"+RegExp.\$1).value = "Show";
                   1858:                     }
                   1859:                 }
                   1860:             }
                   1861:         }
                   1862:     }
                   1863: 
                   1864:     function getTagList () {
                   1865:         
                   1866:         var stringToSearch = document.lonhomework.innerHTML;
                   1867: 
                   1868:         var ret = new Array();
                   1869:         var regex_findBlock = /(foldblock_.*?)"/g;
                   1870:         var tag_list = stringToSearch.match(regex_findBlock);
                   1871: 
                   1872:         if(tag_list != null){
                   1873:             for(var i = 0; i < tag_list.length; i++){            
                   1874:                 ret.push(tag_list[i].replace(/"/, ''));
                   1875:             }
                   1876:         }
                   1877:         return ret;
                   1878:     }
                   1879: 
                   1880:     function saveScrollPosition (resource) {
                   1881:         var tag_list = getTagList();
                   1882: 
                   1883:         // we dont always want to jump to the first block
                   1884:         // 170 is roughly above the "Problem Editing" header. we just want to save if the user scrolled down further than this
                   1885:         if(\$(window).scrollTop() > 170){
                   1886:             if(tag_list != null){
                   1887:                 var result;
                   1888:                 for(var i = 0; i < tag_list.length; i++){
                   1889:                     if(isElementInViewport(tag_list[i])){
                   1890:                         result += tag_list[i]+';';
                   1891:                     }
                   1892:                 }
                   1893:                 sessionStorage.setItem('anchor_'+resource, result);
                   1894:             }
                   1895:         } else {
                   1896:             // we dont need to save zero, just delete the item to leave everything tidy
                   1897:             sessionStorage.removeItem('anchor_'+resource);
                   1898:         }
                   1899:     }
                   1900: 
                   1901:     function restoreScrollPosition(resource){
                   1902: 
                   1903:         var elem = sessionStorage.getItem('anchor_'+resource);
                   1904:         if(elem != null){
                   1905:             var tag_list = elem.split(';');
                   1906:             var elem_list;
                   1907: 
                   1908:             for(var i = 0; i < tag_list.length; i++){
                   1909:                 elem_list = document.getElementsByName(tag_list[i]);
                   1910:                 
                   1911:                 if(elem_list.length > 0){
                   1912:                     elem = elem_list[0];
                   1913:                     break;
                   1914:                 }
                   1915:             }
                   1916:             elem.scrollIntoView();
                   1917:         }
                   1918:     }
                   1919: 
                   1920:     function isElementInViewport(el) {
                   1921: 
                   1922:         // change to last element instead of first
                   1923:         var elem = document.getElementsByName(el);
                   1924:         var rect = elem[0].getBoundingClientRect();
                   1925: 
                   1926:         return (
                   1927:             rect.top >= 0 &&
                   1928:             rect.left >= 0 &&
                   1929:             rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
                   1930:             rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
                   1931:         );
                   1932:     }
                   1933:     
                   1934:     function autosize(depth){
                   1935:         var cmInst = window['cm'+depth];
                   1936:         var fitsizeButton = document.getElementById('fitsize'+depth);
                   1937: 
                   1938:         // is fixed size, switching to dynamic
                   1939:         if (sessionStorage.getItem("autosized_"+depth) == null) {
                   1940:             cmInst.setSize("","auto");
                   1941:             fitsizeButton.value = "@{[&mt('Fixed size')]}";
                   1942:             sessionStorage.setItem("autosized_"+depth, "yes");
                   1943: 
                   1944:         // is dynamic size, switching to fixed
                   1945:         } else {
                   1946:             cmInst.setSize("","300px");
                   1947:             fitsizeButton.value = "@{[&mt('Dynamic size')]}";
                   1948:             sessionStorage.removeItem("autosized_"+depth);
                   1949:         }
                   1950:     }
                   1951: 
                   1952: 
                   1953: 
                   1954: // ]]>
                   1955: </script>
                   1956: COLORFULEDIT
                   1957: }
                   1958: 
                   1959: sub xmleditor_js {
                   1960:     return <<XMLEDIT
                   1961: <script type="text/javascript" src="/adm/jQuery/addons/jquery-scrolltofixed.js"></script>
                   1962: <script type="text/javascript">
                   1963: // <![CDATA[>
                   1964: 
                   1965:     function saveScrollPosition (resource) {
                   1966: 
                   1967:         var scrollPos = \$(window).scrollTop();
                   1968:         sessionStorage.setItem(resource,scrollPos);
                   1969:     }
                   1970: 
                   1971:     function restoreScrollPosition(resource){
                   1972: 
                   1973:         var scrollPos = sessionStorage.getItem(resource);
                   1974:         \$(window).scrollTop(scrollPos);
                   1975:     }
                   1976: 
                   1977:     // unless internet explorer
                   1978:     if (!(window.navigator.appName == "Microsoft Internet Explorer" && (document.documentMode || document.compatMode))){
                   1979: 
                   1980:         \$(document).ready(function() {
                   1981:              \$(".LC_edit_actionbar").scrollToFixed(\{zIndex: 100\});
                   1982:         });
                   1983:     }
                   1984: 
                   1985:     // inserts text at cursor position into codemirror (xml editor only)
                   1986:     function insertText(text){
                   1987:         cm.focus();
                   1988:         var curPos = cm.getCursor();
                   1989:         cm.replaceRange(text.replace(/ESCAPEDSCRIPT/g,'script'), {line: curPos.line,ch: curPos.ch});
                   1990:     }
                   1991: // ]]>
                   1992: </script>
                   1993: XMLEDIT
                   1994: }
                   1995: 
                   1996: sub insert_folding_button {
                   1997:     my $curDepth = $Apache::lonxml::curdepth;
                   1998:     my $lastresource = $env{'request.ambiguous'};
                   1999: 
                   2000:     return "<input type=\"button\" id=\"folding_btn_$curDepth\" 
                   2001:             value=\"".&mt('Hide')."\" onclick=\"fold_box('$curDepth','$lastresource')\">";
                   2002: }
                   2003: 
1.565     albertel 2004: =pod
                   2005: 
1.256     matthew  2006: =head1 Excel and CSV file utility routines
                   2007: 
                   2008: =cut
                   2009: 
                   2010: ###############################################################
                   2011: ###############################################################
                   2012: 
                   2013: =pod
                   2014: 
1.1162    raeburn  2015: =over 4
                   2016: 
1.648     raeburn  2017: =item * &csv_translate($text) 
1.37      matthew  2018: 
1.185     www      2019: Translate $text to allow it to be output as a 'comma separated values' 
1.37      matthew  2020: format.
                   2021: 
                   2022: =cut
                   2023: 
1.180     matthew  2024: ###############################################################
                   2025: ###############################################################
1.37      matthew  2026: sub csv_translate {
                   2027:     my $text = shift;
                   2028:     $text =~ s/\"/\"\"/g;
1.209     albertel 2029:     $text =~ s/\n/ /g;
1.37      matthew  2030:     return $text;
                   2031: }
1.180     matthew  2032: 
                   2033: ###############################################################
                   2034: ###############################################################
                   2035: 
                   2036: =pod
                   2037: 
1.648     raeburn  2038: =item * &define_excel_formats()
1.180     matthew  2039: 
                   2040: Define some commonly used Excel cell formats.
                   2041: 
                   2042: Currently supported formats:
                   2043: 
                   2044: =over 4
                   2045: 
                   2046: =item header
                   2047: 
                   2048: =item bold
                   2049: 
                   2050: =item h1
                   2051: 
                   2052: =item h2
                   2053: 
                   2054: =item h3
                   2055: 
1.256     matthew  2056: =item h4
                   2057: 
                   2058: =item i
                   2059: 
1.180     matthew  2060: =item date
                   2061: 
                   2062: =back
                   2063: 
                   2064: Inputs: $workbook
                   2065: 
                   2066: Returns: $format, a hash reference.
                   2067: 
1.1057    foxr     2068: 
1.180     matthew  2069: =cut
                   2070: 
                   2071: ###############################################################
                   2072: ###############################################################
                   2073: sub define_excel_formats {
                   2074:     my ($workbook) = @_;
                   2075:     my $format;
                   2076:     $format->{'header'} = $workbook->add_format(bold      => 1, 
                   2077:                                                 bottom    => 1,
                   2078:                                                 align     => 'center');
                   2079:     $format->{'bold'} = $workbook->add_format(bold=>1);
                   2080:     $format->{'h1'}   = $workbook->add_format(bold=>1, size=>18);
                   2081:     $format->{'h2'}   = $workbook->add_format(bold=>1, size=>16);
                   2082:     $format->{'h3'}   = $workbook->add_format(bold=>1, size=>14);
1.255     matthew  2083:     $format->{'h4'}   = $workbook->add_format(bold=>1, size=>12);
1.246     matthew  2084:     $format->{'i'}    = $workbook->add_format(italic=>1);
1.180     matthew  2085:     $format->{'date'} = $workbook->add_format(num_format=>
1.207     matthew  2086:                                             'mm/dd/yyyy hh:mm:ss');
1.180     matthew  2087:     return $format;
                   2088: }
                   2089: 
                   2090: ###############################################################
                   2091: ###############################################################
1.113     bowersj2 2092: 
                   2093: =pod
                   2094: 
1.648     raeburn  2095: =item * &create_workbook()
1.255     matthew  2096: 
                   2097: Create an Excel worksheet.  If it fails, output message on the
                   2098: request object and return undefs.
                   2099: 
                   2100: Inputs: Apache request object
                   2101: 
                   2102: Returns (undef) on failure, 
                   2103:     Excel worksheet object, scalar with filename, and formats 
                   2104:     from &Apache::loncommon::define_excel_formats on success
                   2105: 
                   2106: =cut
                   2107: 
                   2108: ###############################################################
                   2109: ###############################################################
                   2110: sub create_workbook {
                   2111:     my ($r) = @_;
                   2112:         #
                   2113:     # Create the excel spreadsheet
                   2114:     my $filename = '/prtspool/'.
1.258     albertel 2115:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.255     matthew  2116:         time.'_'.rand(1000000000).'.xls';
                   2117:     my $workbook  = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
                   2118:     if (! defined($workbook)) {
                   2119:         $r->log_error("Error creating excel spreadsheet $filename: $!");
1.928     bisitz   2120:         $r->print(
                   2121:             '<p class="LC_error">'
                   2122:            .&mt('Problems occurred in creating the new Excel file.')
                   2123:            .' '.&mt('This error has been logged.')
                   2124:            .' '.&mt('Please alert your LON-CAPA administrator.')
                   2125:            .'</p>'
                   2126:         );
1.255     matthew  2127:         return (undef);
                   2128:     }
                   2129:     #
1.1014    foxr     2130:     $workbook->set_tempdir(LONCAPA::tempdir());
1.255     matthew  2131:     #
                   2132:     my $format = &Apache::loncommon::define_excel_formats($workbook);
                   2133:     return ($workbook,$filename,$format);
                   2134: }
                   2135: 
                   2136: ###############################################################
                   2137: ###############################################################
                   2138: 
                   2139: =pod
                   2140: 
1.648     raeburn  2141: =item * &create_text_file()
1.113     bowersj2 2142: 
1.542     raeburn  2143: Create a file to write to and eventually make available to the user.
1.256     matthew  2144: If file creation fails, outputs an error message on the request object and 
                   2145: return undefs.
1.113     bowersj2 2146: 
1.256     matthew  2147: Inputs: Apache request object, and file suffix
1.113     bowersj2 2148: 
1.256     matthew  2149: Returns (undef) on failure, 
                   2150:     Filehandle and filename on success.
1.113     bowersj2 2151: 
                   2152: =cut
                   2153: 
1.256     matthew  2154: ###############################################################
                   2155: ###############################################################
                   2156: sub create_text_file {
                   2157:     my ($r,$suffix) = @_;
                   2158:     if (! defined($suffix)) { $suffix = 'txt'; };
                   2159:     my $fh;
                   2160:     my $filename = '/prtspool/'.
1.258     albertel 2161:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.256     matthew  2162:         time.'_'.rand(1000000000).'.'.$suffix;
                   2163:     $fh = Apache::File->new('>/home/httpd'.$filename);
                   2164:     if (! defined($fh)) {
                   2165:         $r->log_error("Couldn't open $filename for output $!");
1.928     bisitz   2166:         $r->print(
                   2167:             '<p class="LC_error">'
                   2168:            .&mt('Problems occurred in creating the output file.')
                   2169:            .' '.&mt('This error has been logged.')
                   2170:            .' '.&mt('Please alert your LON-CAPA administrator.')
                   2171:            .'</p>'
                   2172:         );
1.113     bowersj2 2173:     }
1.256     matthew  2174:     return ($fh,$filename)
1.113     bowersj2 2175: }
                   2176: 
                   2177: 
1.256     matthew  2178: =pod 
1.113     bowersj2 2179: 
                   2180: =back
                   2181: 
                   2182: =cut
1.37      matthew  2183: 
                   2184: ###############################################################
1.33      matthew  2185: ##        Home server <option> list generating code          ##
                   2186: ###############################################################
1.35      matthew  2187: 
1.169     www      2188: # ------------------------------------------
                   2189: 
                   2190: sub domain_select {
                   2191:     my ($name,$value,$multiple)=@_;
                   2192:     my %domains=map { 
1.514     albertel 2193: 	$_ => $_.' '. &Apache::lonnet::domain($_,'description') 
1.512     albertel 2194:     } &Apache::lonnet::all_domains();
1.169     www      2195:     if ($multiple) {
                   2196: 	$domains{''}=&mt('Any domain');
1.550     albertel 2197: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.287     albertel 2198: 	return &multiple_select_form($name,$value,4,\%domains);
1.169     www      2199:     } else {
1.550     albertel 2200: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.970     raeburn  2201: 	return &select_form($name,$value,\%domains);
1.169     www      2202:     }
                   2203: }
                   2204: 
1.282     albertel 2205: #-------------------------------------------
                   2206: 
                   2207: =pod
                   2208: 
1.519     raeburn  2209: =head1 Routines for form select boxes
                   2210: 
                   2211: =over 4
                   2212: 
1.648     raeburn  2213: =item * &multiple_select_form($name,$value,$size,$hash,$order)
1.282     albertel 2214: 
                   2215: Returns a string containing a <select> element int multiple mode
                   2216: 
                   2217: 
                   2218: Args:
                   2219:   $name - name of the <select> element
1.506     raeburn  2220:   $value - scalar or array ref of values that should already be selected
1.282     albertel 2221:   $size - number of rows long the select element is
1.283     albertel 2222:   $hash - the elements should be 'option' => 'shown text'
1.282     albertel 2223:           (shown text should already have been &mt())
1.506     raeburn  2224:   $order - (optional) array ref of the order to show the elements in
1.283     albertel 2225: 
1.282     albertel 2226: =cut
                   2227: 
                   2228: #-------------------------------------------
1.169     www      2229: sub multiple_select_form {
1.284     albertel 2230:     my ($name,$value,$size,$hash,$order)=@_;
1.169     www      2231:     my %selected = map { $_ => 1 } ref($value)?@{$value}:($value);
                   2232:     my $output='';
1.191     matthew  2233:     if (! defined($size)) {
                   2234:         $size = 4;
1.283     albertel 2235:         if (scalar(keys(%$hash))<4) {
                   2236:             $size = scalar(keys(%$hash));
1.191     matthew  2237:         }
                   2238:     }
1.734     bisitz   2239:     $output.="\n".'<select name="'.$name.'" size="'.$size.'" multiple="multiple">';
1.501     banghart 2240:     my @order;
1.506     raeburn  2241:     if (ref($order) eq 'ARRAY')  {
                   2242:         @order = @{$order};
                   2243:     } else {
                   2244:         @order = sort(keys(%$hash));
1.501     banghart 2245:     }
                   2246:     if (exists($$hash{'select_form_order'})) {
                   2247:         @order = @{$$hash{'select_form_order'}};
                   2248:     }
                   2249:         
1.284     albertel 2250:     foreach my $key (@order) {
1.356     albertel 2251:         $output.='<option value="'.&HTML::Entities::encode($key,'"<>&').'" ';
1.284     albertel 2252:         $output.='selected="selected" ' if ($selected{$key});
                   2253:         $output.='>'.$hash->{$key}."</option>\n";
1.169     www      2254:     }
                   2255:     $output.="</select>\n";
                   2256:     return $output;
                   2257: }
                   2258: 
1.88      www      2259: #-------------------------------------------
                   2260: 
                   2261: =pod
                   2262: 
1.970     raeburn  2263: =item * &select_form($defdom,$name,$hashref,$onchange)
1.88      www      2264: 
                   2265: Returns a string containing a <select name='$name' size='1'> form to 
1.970     raeburn  2266: allow a user to select options from a ref to a hash containing:
                   2267: option_name => displayed text. An optional $onchange can include
                   2268: a javascript onchange item, e.g., onchange="this.form.submit();"  
                   2269: 
1.88      www      2270: See lonrights.pm for an example invocation and use.
                   2271: 
                   2272: =cut
                   2273: 
                   2274: #-------------------------------------------
                   2275: sub select_form {
1.1228    raeburn  2276:     my ($def,$name,$hashref,$onchange,$readonly) = @_;
1.970     raeburn  2277:     return unless (ref($hashref) eq 'HASH');
                   2278:     if ($onchange) {
                   2279:         $onchange = ' onchange="'.$onchange.'"';
                   2280:     }
1.1228    raeburn  2281:     my $disabled;
                   2282:     if ($readonly) {
                   2283:         $disabled = ' disabled="disabled"';
                   2284:     }
                   2285:     my $selectform = "<select name=\"$name\" size=\"1\"$onchange$disabled>\n";
1.128     albertel 2286:     my @keys;
1.970     raeburn  2287:     if (exists($hashref->{'select_form_order'})) {
                   2288: 	@keys=@{$hashref->{'select_form_order'}};
1.128     albertel 2289:     } else {
1.970     raeburn  2290: 	@keys=sort(keys(%{$hashref}));
1.128     albertel 2291:     }
1.356     albertel 2292:     foreach my $key (@keys) {
                   2293:         $selectform.=
                   2294: 	    '<option value="'.&HTML::Entities::encode($key,'"<>&').'" '.
                   2295:             ($key eq $def ? 'selected="selected" ' : '').
1.970     raeburn  2296:                 ">".$hashref->{$key}."</option>\n";
1.88      www      2297:     }
                   2298:     $selectform.="</select>";
                   2299:     return $selectform;
                   2300: }
                   2301: 
1.475     www      2302: # For display filters
                   2303: 
                   2304: sub display_filter {
1.1074    raeburn  2305:     my ($context) = @_;
1.475     www      2306:     if (!$env{'form.show'}) { $env{'form.show'}=10; }
1.477     www      2307:     if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
1.1074    raeburn  2308:     my $phraseinput = 'hidden';
                   2309:     my $includeinput = 'hidden';
                   2310:     my ($checked,$includetypestext);
                   2311:     if ($env{'form.displayfilter'} eq 'containing') {
                   2312:         $phraseinput = 'text'; 
                   2313:         if ($context eq 'parmslog') {
                   2314:             $includeinput = 'checkbox';
                   2315:             if ($env{'form.includetypes'}) {
                   2316:                 $checked = ' checked="checked"';
                   2317:             }
                   2318:             $includetypestext = &mt('Include parameter types');
                   2319:         }
                   2320:     } else {
                   2321:         $includetypestext = '&nbsp;';
                   2322:     }
                   2323:     my ($additional,$secondid,$thirdid);
                   2324:     if ($context eq 'parmslog') {
                   2325:         $additional = 
                   2326:             '<label><input type="'.$includeinput.'" name="includetypes"'. 
                   2327:             $checked.' name="includetypes" value="1" id="includetypes" />'.
                   2328:             '&nbsp;<span id="includetypestext">'.$includetypestext.'</span>'.
                   2329:             '</label>';
                   2330:         $secondid = 'includetypes';
                   2331:         $thirdid = 'includetypestext';
                   2332:     }
                   2333:     my $onchange = "javascript:toggleHistoryOptions(this,'containingphrase','$context',
                   2334:                                                     '$secondid','$thirdid')";
                   2335:     return '<span class="LC_nobreak"><label>'.&mt('Records: [_1]',
1.475     www      2336: 			       &Apache::lonmeta::selectbox('show',$env{'form.show'},undef,
                   2337: 							   (&mt('all'),10,20,50,100,1000,10000))).
1.714     bisitz   2338: 	   '</label></span> <span class="LC_nobreak">'.
1.1074    raeburn  2339:            &mt('Filter: [_1]',
1.477     www      2340: 	   &select_form($env{'form.displayfilter'},
                   2341: 			'displayfilter',
1.970     raeburn  2342: 			{'currentfolder' => 'Current folder/page',
1.477     www      2343: 			 'containing' => 'Containing phrase',
1.1074    raeburn  2344: 			 'none' => 'None'},$onchange)).'&nbsp;'.
                   2345: 			 '<input type="'.$phraseinput.'" name="containingphrase" id="containingphrase" size="30" value="'.
                   2346:                          &HTML::Entities::encode($env{'form.containingphrase'}).
                   2347:                          '" />'.$additional;
                   2348: }
                   2349: 
                   2350: sub display_filter_js {
                   2351:     my $includetext = &mt('Include parameter types');
                   2352:     return <<"ENDJS";
                   2353:   
                   2354: function toggleHistoryOptions(setter,firstid,context,secondid,thirdid) {
                   2355:     var firstType = 'hidden';
                   2356:     if (setter.options[setter.selectedIndex].value == 'containing') {
                   2357:         firstType = 'text';
                   2358:     }
                   2359:     firstObject = document.getElementById(firstid);
                   2360:     if (typeof(firstObject) == 'object') {
                   2361:         if (firstObject.type != firstType) {
                   2362:             changeInputType(firstObject,firstType);
                   2363:         }
                   2364:     }
                   2365:     if (context == 'parmslog') {
                   2366:         var secondType = 'hidden';
                   2367:         if (firstType == 'text') {
                   2368:             secondType = 'checkbox';
                   2369:         }
                   2370:         secondObject = document.getElementById(secondid);  
                   2371:         if (typeof(secondObject) == 'object') {
                   2372:             if (secondObject.type != secondType) {
                   2373:                 changeInputType(secondObject,secondType);
                   2374:             }
                   2375:         }
                   2376:         var textItem = document.getElementById(thirdid);
                   2377:         var currtext = textItem.innerHTML;
                   2378:         var newtext;
                   2379:         if (firstType == 'text') {
                   2380:             newtext = '$includetext';
                   2381:         } else {
                   2382:             newtext = '&nbsp;';
                   2383:         }
                   2384:         if (currtext != newtext) {
                   2385:             textItem.innerHTML = newtext;
                   2386:         }
                   2387:     }
                   2388:     return;
                   2389: }
                   2390: 
                   2391: function changeInputType(oldObject,newType) {
                   2392:     var newObject = document.createElement('input');
                   2393:     newObject.type = newType;
                   2394:     if (oldObject.size) {
                   2395:         newObject.size = oldObject.size;
                   2396:     }
                   2397:     if (oldObject.value) {
                   2398:         newObject.value = oldObject.value;
                   2399:     }
                   2400:     if (oldObject.name) {
                   2401:         newObject.name = oldObject.name;
                   2402:     }
                   2403:     if (oldObject.id) {
                   2404:         newObject.id = oldObject.id;
                   2405:     }
                   2406:     oldObject.parentNode.replaceChild(newObject,oldObject);
                   2407:     return;
                   2408: }
                   2409: 
                   2410: ENDJS
1.475     www      2411: }
                   2412: 
1.167     www      2413: sub gradeleveldescription {
                   2414:     my $gradelevel=shift;
                   2415:     my %gradelevels=(0 => 'Not specified',
                   2416: 		     1 => 'Grade 1',
                   2417: 		     2 => 'Grade 2',
                   2418: 		     3 => 'Grade 3',
                   2419: 		     4 => 'Grade 4',
                   2420: 		     5 => 'Grade 5',
                   2421: 		     6 => 'Grade 6',
                   2422: 		     7 => 'Grade 7',
                   2423: 		     8 => 'Grade 8',
                   2424: 		     9 => 'Grade 9',
                   2425: 		     10 => 'Grade 10',
                   2426: 		     11 => 'Grade 11',
                   2427: 		     12 => 'Grade 12',
                   2428: 		     13 => 'Grade 13',
                   2429: 		     14 => '100 Level',
                   2430: 		     15 => '200 Level',
                   2431: 		     16 => '300 Level',
                   2432: 		     17 => '400 Level',
                   2433: 		     18 => 'Graduate Level');
                   2434:     return &mt($gradelevels{$gradelevel});
                   2435: }
                   2436: 
1.163     www      2437: sub select_level_form {
                   2438:     my ($deflevel,$name)=@_;
                   2439:     unless ($deflevel) { $deflevel=0; }
1.167     www      2440:     my $selectform = "<select name=\"$name\" size=\"1\">\n";
                   2441:     for (my $i=0; $i<=18; $i++) {
                   2442:         $selectform.="<option value=\"$i\" ".
1.253     albertel 2443:             ($i==$deflevel ? 'selected="selected" ' : '').
1.167     www      2444:                 ">".&gradeleveldescription($i)."</option>\n";
                   2445:     }
                   2446:     $selectform.="</select>";
                   2447:     return $selectform;
1.163     www      2448: }
1.167     www      2449: 
1.35      matthew  2450: #-------------------------------------------
                   2451: 
1.45      matthew  2452: =pod
                   2453: 
1.1121    raeburn  2454: =item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms)
1.35      matthew  2455: 
                   2456: Returns a string containing a <select name='$name' size='1'> form to 
                   2457: allow a user to select the domain to preform an operation in.  
                   2458: See loncreateuser.pm for an example invocation and use.
                   2459: 
1.90      www      2460: If the $includeempty flag is set, it also includes an empty choice ("no domain
                   2461: selected");
                   2462: 
1.743     raeburn  2463: If the $showdomdesc flag is set, the domain name is followed by the domain description.
                   2464: 
1.910     raeburn  2465: The optional $onchange argument specifies what should occur if the domain selector is changed, e.g., 'this.form.submit()' if the form is to be automatically submitted.
                   2466: 
1.1121    raeburn  2467: The optional $incdoms is a reference to an array of domains which will be the only available options.
                   2468: 
                   2469: The optional $excdoms is a reference to an array of domains which will be excluded from the available options.
1.563     raeburn  2470: 
1.35      matthew  2471: =cut
                   2472: 
                   2473: #-------------------------------------------
1.34      matthew  2474: sub select_dom_form {
1.1121    raeburn  2475:     my ($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms) = @_;
1.872     raeburn  2476:     if ($onchange) {
1.874     raeburn  2477:         $onchange = ' onchange="'.$onchange.'"';
1.743     raeburn  2478:     }
1.1121    raeburn  2479:     my (@domains,%exclude);
1.910     raeburn  2480:     if (ref($incdoms) eq 'ARRAY') {
                   2481:         @domains = sort {lc($a) cmp lc($b)} (@{$incdoms});
                   2482:     } else {
                   2483:         @domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
                   2484:     }
1.90      www      2485:     if ($includeempty) { @domains=('',@domains); }
1.1121    raeburn  2486:     if (ref($excdoms) eq 'ARRAY') {
                   2487:         map { $exclude{$_} = 1; } @{$excdoms}; 
                   2488:     }
1.743     raeburn  2489:     my $selectdomain = "<select name=\"$name\" size=\"1\"$onchange>\n";
1.356     albertel 2490:     foreach my $dom (@domains) {
1.1121    raeburn  2491:         next if ($exclude{$dom});
1.356     albertel 2492:         $selectdomain.="<option value=\"$dom\" ".
1.563     raeburn  2493:             ($dom eq $defdom ? 'selected="selected" ' : '').'>'.$dom;
                   2494:         if ($showdomdesc) {
                   2495:             if ($dom ne '') {
                   2496:                 my $domdesc = &Apache::lonnet::domain($dom,'description');
                   2497:                 if ($domdesc ne '') {
                   2498:                     $selectdomain .= ' ('.$domdesc.')';
                   2499:                 }
                   2500:             } 
                   2501:         }
                   2502:         $selectdomain .= "</option>\n";
1.34      matthew  2503:     }
                   2504:     $selectdomain.="</select>";
                   2505:     return $selectdomain;
                   2506: }
                   2507: 
1.35      matthew  2508: #-------------------------------------------
                   2509: 
1.45      matthew  2510: =pod
                   2511: 
1.648     raeburn  2512: =item * &home_server_form_item($domain,$name,$defaultflag)
1.35      matthew  2513: 
1.586     raeburn  2514: input: 4 arguments (two required, two optional) - 
                   2515:     $domain - domain of new user
                   2516:     $name - name of form element
                   2517:     $default - Value of 'default' causes a default item to be first 
                   2518:                             option, and selected by default. 
                   2519:     $hide - Value of 'hide' causes hiding of the name of the server, 
                   2520:                             if 1 server found, or default, if 0 found.
1.594     raeburn  2521: output: returns 2 items: 
1.586     raeburn  2522: (a) form element which contains either:
                   2523:    (i) <select name="$name">
                   2524:         <option value="$hostid1">$hostid $servers{$hostid}</option>
                   2525:         <option value="$hostid2">$hostid $servers{$hostid}</option>       
                   2526:        </select>
                   2527:        form item if there are multiple library servers in $domain, or
                   2528:    (ii) an <input type="hidden" name="$name" value="$hostid" /> form item 
                   2529:        if there is only one library server in $domain.
                   2530: 
                   2531: (b) number of library servers found.
                   2532: 
                   2533: See loncreateuser.pm for example of use.
1.35      matthew  2534: 
                   2535: =cut
                   2536: 
                   2537: #-------------------------------------------
1.586     raeburn  2538: sub home_server_form_item {
                   2539:     my ($domain,$name,$default,$hide) = @_;
1.513     albertel 2540:     my %servers = &Apache::lonnet::get_servers($domain,'library');
1.586     raeburn  2541:     my $result;
                   2542:     my $numlib = keys(%servers);
                   2543:     if ($numlib > 1) {
                   2544:         $result .= '<select name="'.$name.'" />'."\n";
                   2545:         if ($default) {
1.804     bisitz   2546:             $result .= '<option value="default" selected="selected">'.&mt('default').
1.586     raeburn  2547:                        '</option>'."\n";
                   2548:         }
                   2549:         foreach my $hostid (sort(keys(%servers))) {
                   2550:             $result.= '<option value="'.$hostid.'">'.
                   2551: 	              $hostid.' '.$servers{$hostid}."</option>\n";
                   2552:         }
                   2553:         $result .= '</select>'."\n";
                   2554:     } elsif ($numlib == 1) {
                   2555:         my $hostid;
                   2556:         foreach my $item (keys(%servers)) {
                   2557:             $hostid = $item;
                   2558:         }
                   2559:         $result .= '<input type="hidden" name="'.$name.'" value="'.
                   2560:                    $hostid.'" />';
                   2561:                    if (!$hide) {
                   2562:                        $result .= $hostid.' '.$servers{$hostid};
                   2563:                    }
                   2564:                    $result .= "\n";
                   2565:     } elsif ($default) {
                   2566:         $result .= '<input type="hidden" name="'.$name.
                   2567:                    '" value="default" />';
                   2568:                    if (!$hide) {
                   2569:                        $result .= &mt('default');
                   2570:                    }
                   2571:                    $result .= "\n";
1.33      matthew  2572:     }
1.586     raeburn  2573:     return ($result,$numlib);
1.33      matthew  2574: }
1.112     bowersj2 2575: 
                   2576: =pod
                   2577: 
1.534     albertel 2578: =back 
                   2579: 
1.112     bowersj2 2580: =cut
1.87      matthew  2581: 
                   2582: ###############################################################
1.112     bowersj2 2583: ##                  Decoding User Agent                      ##
1.87      matthew  2584: ###############################################################
                   2585: 
                   2586: =pod
                   2587: 
1.112     bowersj2 2588: =head1 Decoding the User Agent
                   2589: 
                   2590: =over 4
                   2591: 
                   2592: =item * &decode_user_agent()
1.87      matthew  2593: 
                   2594: Inputs: $r
                   2595: 
                   2596: Outputs:
                   2597: 
                   2598: =over 4
                   2599: 
1.112     bowersj2 2600: =item * $httpbrowser
1.87      matthew  2601: 
1.112     bowersj2 2602: =item * $clientbrowser
1.87      matthew  2603: 
1.112     bowersj2 2604: =item * $clientversion
1.87      matthew  2605: 
1.112     bowersj2 2606: =item * $clientmathml
1.87      matthew  2607: 
1.112     bowersj2 2608: =item * $clientunicode
1.87      matthew  2609: 
1.112     bowersj2 2610: =item * $clientos
1.87      matthew  2611: 
1.1137    raeburn  2612: =item * $clientmobile
                   2613: 
1.1141    raeburn  2614: =item * $clientinfo
                   2615: 
1.1194    raeburn  2616: =item * $clientosversion
                   2617: 
1.87      matthew  2618: =back
                   2619: 
1.157     matthew  2620: =back 
                   2621: 
1.87      matthew  2622: =cut
                   2623: 
                   2624: ###############################################################
                   2625: ###############################################################
                   2626: sub decode_user_agent {
1.247     albertel 2627:     my ($r)=@_;
1.87      matthew  2628:     my @browsertype=split(/\&/,$Apache::lonnet::perlvar{"lonBrowsDet"});
                   2629:     my %mathcap=split(/\&/,$$Apache::lonnet::perlvar{"lonMathML"});
                   2630:     my $httpbrowser=$ENV{"HTTP_USER_AGENT"};
1.247     albertel 2631:     if (!$httpbrowser && $r) { $httpbrowser=$r->header_in('User-Agent'); }
1.87      matthew  2632:     my $clientbrowser='unknown';
                   2633:     my $clientversion='0';
                   2634:     my $clientmathml='';
                   2635:     my $clientunicode='0';
1.1137    raeburn  2636:     my $clientmobile=0;
1.1194    raeburn  2637:     my $clientosversion='';
1.87      matthew  2638:     for (my $i=0;$i<=$#browsertype;$i++) {
1.1193    raeburn  2639:         my ($bname,$match,$notmatch,$vreg,$minv,$univ)=split(/\%/,$browsertype[$i]);
1.87      matthew  2640: 	if (($httpbrowser=~/$match/i)  && ($httpbrowser!~/$notmatch/i)) {
                   2641: 	    $clientbrowser=$bname;
                   2642:             $httpbrowser=~/$vreg/i;
                   2643: 	    $clientversion=$1;
                   2644:             $clientmathml=($clientversion>=$minv);
                   2645:             $clientunicode=($clientversion>=$univ);
                   2646: 	}
                   2647:     }
                   2648:     my $clientos='unknown';
1.1141    raeburn  2649:     my $clientinfo;
1.87      matthew  2650:     if (($httpbrowser=~/linux/i) ||
                   2651:         ($httpbrowser=~/unix/i) ||
                   2652:         ($httpbrowser=~/ux/i) ||
                   2653:         ($httpbrowser=~/solaris/i)) { $clientos='unix'; }
                   2654:     if (($httpbrowser=~/vax/i) ||
                   2655:         ($httpbrowser=~/vms/i)) { $clientos='vms'; }
                   2656:     if ($httpbrowser=~/next/i) { $clientos='next'; }
                   2657:     if (($httpbrowser=~/mac/i) ||
                   2658:         ($httpbrowser=~/powerpc/i)) { $clientos='mac'; }
1.1194    raeburn  2659:     if ($httpbrowser=~/win/i) {
                   2660:         $clientos='win';
                   2661:         if ($httpbrowser =~/Windows\s+NT\s+(\d+\.\d+)/i) {
                   2662:             $clientosversion = $1;
                   2663:         }
                   2664:     }
1.87      matthew  2665:     if ($httpbrowser=~/embed/i) { $clientos='pda'; }
1.1137    raeburn  2666:     if ($httpbrowser=~/(Android|iPod|iPad|iPhone|webOS|Blackberry|Windows Phone|Opera m(?:ob|in)|Fennec)/i) {
                   2667:         $clientmobile=lc($1);
                   2668:     }
1.1141    raeburn  2669:     if ($httpbrowser=~ m{Firefox/(\d+\.\d+)}) {
                   2670:         $clientinfo = 'firefox-'.$1;
                   2671:     } elsif ($httpbrowser=~ m{chromeframe/(\d+\.\d+)\.}) {
                   2672:         $clientinfo = 'chromeframe-'.$1;
                   2673:     }
1.87      matthew  2674:     return ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
1.1194    raeburn  2675:             $clientunicode,$clientos,$clientmobile,$clientinfo,
                   2676:             $clientosversion);
1.87      matthew  2677: }
                   2678: 
1.32      matthew  2679: ###############################################################
                   2680: ##    Authentication changing form generation subroutines    ##
                   2681: ###############################################################
                   2682: ##
                   2683: ## All of the authform_xxxxxxx subroutines take their inputs in a
                   2684: ## hash, and have reasonable default values.
                   2685: ##
                   2686: ##    formname = the name given in the <form> tag.
1.35      matthew  2687: #-------------------------------------------
                   2688: 
1.45      matthew  2689: =pod
                   2690: 
1.112     bowersj2 2691: =head1 Authentication Routines
                   2692: 
                   2693: =over 4
                   2694: 
1.648     raeburn  2695: =item * &authform_xxxxxx()
1.35      matthew  2696: 
                   2697: The authform_xxxxxx subroutines provide javascript and html forms which 
                   2698: handle some of the conveniences required for authentication forms.  
                   2699: This is not an optimal method, but it works.  
                   2700: 
                   2701: =over 4
                   2702: 
1.112     bowersj2 2703: =item * authform_header
1.35      matthew  2704: 
1.112     bowersj2 2705: =item * authform_authorwarning
1.35      matthew  2706: 
1.112     bowersj2 2707: =item * authform_nochange
1.35      matthew  2708: 
1.112     bowersj2 2709: =item * authform_kerberos
1.35      matthew  2710: 
1.112     bowersj2 2711: =item * authform_internal
1.35      matthew  2712: 
1.112     bowersj2 2713: =item * authform_filesystem
1.35      matthew  2714: 
                   2715: =back
                   2716: 
1.648     raeburn  2717: See loncreateuser.pm for invocation and use examples.
1.157     matthew  2718: 
1.35      matthew  2719: =cut
                   2720: 
                   2721: #-------------------------------------------
1.32      matthew  2722: sub authform_header{  
                   2723:     my %in = (
                   2724:         formname => 'cu',
1.80      albertel 2725:         kerb_def_dom => '',
1.32      matthew  2726:         @_,
                   2727:     );
                   2728:     $in{'formname'} = 'document.' . $in{'formname'};
                   2729:     my $result='';
1.80      albertel 2730: 
                   2731: #---------------------------------------------- Code for upper case translation
                   2732:     my $Javascript_toUpperCase;
                   2733:     unless ($in{kerb_def_dom}) {
                   2734:         $Javascript_toUpperCase =<<"END";
                   2735:         switch (choice) {
                   2736:            case 'krb': currentform.elements[choicearg].value =
                   2737:                currentform.elements[choicearg].value.toUpperCase();
                   2738:                break;
                   2739:            default:
                   2740:         }
                   2741: END
                   2742:     } else {
                   2743:         $Javascript_toUpperCase = "";
                   2744:     }
                   2745: 
1.165     raeburn  2746:     my $radioval = "'nochange'";
1.591     raeburn  2747:     if (defined($in{'curr_authtype'})) {
                   2748:         if ($in{'curr_authtype'} ne '') {
                   2749:             $radioval = "'".$in{'curr_authtype'}."arg'";
                   2750:         }
1.174     matthew  2751:     }
1.165     raeburn  2752:     my $argfield = 'null';
1.591     raeburn  2753:     if (defined($in{'mode'})) {
1.165     raeburn  2754:         if ($in{'mode'} eq 'modifycourse')  {
1.591     raeburn  2755:             if (defined($in{'curr_autharg'})) {
                   2756:                 if ($in{'curr_autharg'} ne '') {
1.165     raeburn  2757:                     $argfield = "'$in{'curr_autharg'}'";
                   2758:                 }
                   2759:             }
                   2760:         }
                   2761:     }
                   2762: 
1.32      matthew  2763:     $result.=<<"END";
                   2764: var current = new Object();
1.165     raeburn  2765: current.radiovalue = $radioval;
                   2766: current.argfield = $argfield;
1.32      matthew  2767: 
                   2768: function changed_radio(choice,currentform) {
                   2769:     var choicearg = choice + 'arg';
                   2770:     // If a radio button in changed, we need to change the argfield
                   2771:     if (current.radiovalue != choice) {
                   2772:         current.radiovalue = choice;
                   2773:         if (current.argfield != null) {
                   2774:             currentform.elements[current.argfield].value = '';
                   2775:         }
                   2776:         if (choice == 'nochange') {
                   2777:             current.argfield = null;
                   2778:         } else {
                   2779:             current.argfield = choicearg;
                   2780:             switch(choice) {
                   2781:                 case 'krb': 
                   2782:                     currentform.elements[current.argfield].value = 
                   2783:                         "$in{'kerb_def_dom'}";
                   2784:                 break;
                   2785:               default:
                   2786:                 break;
                   2787:             }
                   2788:         }
                   2789:     }
                   2790:     return;
                   2791: }
1.22      www      2792: 
1.32      matthew  2793: function changed_text(choice,currentform) {
                   2794:     var choicearg = choice + 'arg';
                   2795:     if (currentform.elements[choicearg].value !='') {
1.80      albertel 2796:         $Javascript_toUpperCase
1.32      matthew  2797:         // clear old field
                   2798:         if ((current.argfield != choicearg) && (current.argfield != null)) {
                   2799:             currentform.elements[current.argfield].value = '';
                   2800:         }
                   2801:         current.argfield = choicearg;
                   2802:     }
                   2803:     set_auth_radio_buttons(choice,currentform);
                   2804:     return;
1.20      www      2805: }
1.32      matthew  2806: 
                   2807: function set_auth_radio_buttons(newvalue,currentform) {
1.986     raeburn  2808:     var numauthchoices = currentform.login.length;
                   2809:     if (typeof numauthchoices  == "undefined") {
                   2810:         return;
                   2811:     } 
1.32      matthew  2812:     var i=0;
1.986     raeburn  2813:     while (i < numauthchoices) {
1.32      matthew  2814:         if (currentform.login[i].value == newvalue) { break; }
                   2815:         i++;
                   2816:     }
1.986     raeburn  2817:     if (i == numauthchoices) {
1.32      matthew  2818:         return;
                   2819:     }
                   2820:     current.radiovalue = newvalue;
                   2821:     currentform.login[i].checked = true;
                   2822:     return;
                   2823: }
                   2824: END
                   2825:     return $result;
                   2826: }
                   2827: 
1.1106    raeburn  2828: sub authform_authorwarning {
1.32      matthew  2829:     my $result='';
1.144     matthew  2830:     $result='<i>'.
                   2831:         &mt('As a general rule, only authors or co-authors should be '.
                   2832:             'filesystem authenticated '.
                   2833:             '(which allows access to the server filesystem).')."</i>\n";
1.32      matthew  2834:     return $result;
                   2835: }
                   2836: 
1.1106    raeburn  2837: sub authform_nochange {
1.32      matthew  2838:     my %in = (
                   2839:               formname => 'document.cu',
                   2840:               kerb_def_dom => 'MSU.EDU',
                   2841:               @_,
                   2842:           );
1.1106    raeburn  2843:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.586     raeburn  2844:     my $result;
1.1104    raeburn  2845:     if (!$authnum) {
1.1105    raeburn  2846:         $result = &mt('Under your current role you are not permitted to change login settings for this user');
1.586     raeburn  2847:     } else {
                   2848:         $result = '<label>'.&mt('[_1] Do not change login data',
                   2849:                   '<input type="radio" name="login" value="nochange" '.
                   2850:                   'checked="checked" onclick="'.
1.281     albertel 2851:             "javascript:changed_radio('nochange',$in{'formname'});".'" />').
                   2852: 	    '</label>';
1.586     raeburn  2853:     }
1.32      matthew  2854:     return $result;
                   2855: }
                   2856: 
1.591     raeburn  2857: sub authform_kerberos {
1.32      matthew  2858:     my %in = (
                   2859:               formname => 'document.cu',
                   2860:               kerb_def_dom => 'MSU.EDU',
1.80      albertel 2861:               kerb_def_auth => 'krb4',
1.32      matthew  2862:               @_,
                   2863:               );
1.586     raeburn  2864:     my ($check4,$check5,$krbcheck,$krbarg,$krbver,$result,$authtype,
                   2865:         $autharg,$jscall);
1.1106    raeburn  2866:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.80      albertel 2867:     if ($in{'kerb_def_auth'} eq 'krb5') {
1.772     bisitz   2868:        $check5 = ' checked="checked"';
1.80      albertel 2869:     } else {
1.772     bisitz   2870:        $check4 = ' checked="checked"';
1.80      albertel 2871:     }
1.165     raeburn  2872:     $krbarg = $in{'kerb_def_dom'};
1.591     raeburn  2873:     if (defined($in{'curr_authtype'})) {
                   2874:         if ($in{'curr_authtype'} eq 'krb') {
1.772     bisitz   2875:             $krbcheck = ' checked="checked"';
1.623     raeburn  2876:             if (defined($in{'mode'})) {
                   2877:                 if ($in{'mode'} eq 'modifyuser') {
                   2878:                     $krbcheck = '';
                   2879:                 }
                   2880:             }
1.591     raeburn  2881:             if (defined($in{'curr_kerb_ver'})) {
                   2882:                 if ($in{'curr_krb_ver'} eq '5') {
1.772     bisitz   2883:                     $check5 = ' checked="checked"';
1.591     raeburn  2884:                     $check4 = '';
                   2885:                 } else {
1.772     bisitz   2886:                     $check4 = ' checked="checked"';
1.591     raeburn  2887:                     $check5 = '';
                   2888:                 }
1.586     raeburn  2889:             }
1.591     raeburn  2890:             if (defined($in{'curr_autharg'})) {
1.165     raeburn  2891:                 $krbarg = $in{'curr_autharg'};
                   2892:             }
1.586     raeburn  2893:             if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
1.591     raeburn  2894:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2895:                     $result = 
                   2896:     &mt('Currently Kerberos authenticated with domain [_1] Version [_2].',
                   2897:         $in{'curr_autharg'},$krbver);
                   2898:                 } else {
                   2899:                     $result =
                   2900:     &mt('Currently Kerberos authenticated, Version [_1].',$krbver);
                   2901:                 }
                   2902:                 return $result; 
                   2903:             }
                   2904:         }
                   2905:     } else {
                   2906:         if ($authnum == 1) {
1.784     bisitz   2907:             $authtype = '<input type="hidden" name="login" value="krb" />';
1.165     raeburn  2908:         }
                   2909:     }
1.586     raeburn  2910:     if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
                   2911:         return;
1.587     raeburn  2912:     } elsif ($authtype eq '') {
1.591     raeburn  2913:         if (defined($in{'mode'})) {
1.587     raeburn  2914:             if ($in{'mode'} eq 'modifycourse') {
                   2915:                 if ($authnum == 1) {
1.1104    raeburn  2916:                     $authtype = '<input type="radio" name="login" value="krb" />';
1.587     raeburn  2917:                 }
                   2918:             }
                   2919:         }
1.586     raeburn  2920:     }
                   2921:     $jscall = "javascript:changed_radio('krb',$in{'formname'});";
                   2922:     if ($authtype eq '') {
                   2923:         $authtype = '<input type="radio" name="login" value="krb" '.
                   2924:                     'onclick="'.$jscall.'" onchange="'.$jscall.'"'.
                   2925:                     $krbcheck.' />';
                   2926:     }
                   2927:     if (($can_assign{'krb4'} && $can_assign{'krb5'}) ||
1.1106    raeburn  2928:         ($can_assign{'krb4'} && !$can_assign{'krb5'} &&
1.586     raeburn  2929:          $in{'curr_authtype'} eq 'krb5') ||
1.1106    raeburn  2930:         (!$can_assign{'krb4'} && $can_assign{'krb5'} &&
1.586     raeburn  2931:          $in{'curr_authtype'} eq 'krb4')) {
                   2932:         $result .= &mt
1.144     matthew  2933:         ('[_1] Kerberos authenticated with domain [_2] '.
1.281     albertel 2934:          '[_3] Version 4 [_4] Version 5 [_5]',
1.586     raeburn  2935:          '<label>'.$authtype,
1.281     albertel 2936:          '</label><input type="text" size="10" name="krbarg" '.
1.165     raeburn  2937:              'value="'.$krbarg.'" '.
1.144     matthew  2938:              'onchange="'.$jscall.'" />',
1.281     albertel 2939:          '<label><input type="radio" name="krbver" value="4" '.$check4.' />',
                   2940:          '</label><label><input type="radio" name="krbver" value="5" '.$check5.' />',
                   2941: 	 '</label>');
1.586     raeburn  2942:     } elsif ($can_assign{'krb4'}) {
                   2943:         $result .= &mt
                   2944:         ('[_1] Kerberos authenticated with domain [_2] '.
                   2945:          '[_3] Version 4 [_4]',
                   2946:          '<label>'.$authtype,
                   2947:          '</label><input type="text" size="10" name="krbarg" '.
                   2948:              'value="'.$krbarg.'" '.
                   2949:              'onchange="'.$jscall.'" />',
                   2950:          '<label><input type="hidden" name="krbver" value="4" />',
                   2951:          '</label>');
                   2952:     } elsif ($can_assign{'krb5'}) {
                   2953:         $result .= &mt
                   2954:         ('[_1] Kerberos authenticated with domain [_2] '.
                   2955:          '[_3] Version 5 [_4]',
                   2956:          '<label>'.$authtype,
                   2957:          '</label><input type="text" size="10" name="krbarg" '.
                   2958:              'value="'.$krbarg.'" '.
                   2959:              'onchange="'.$jscall.'" />',
                   2960:          '<label><input type="hidden" name="krbver" value="5" />',
                   2961:          '</label>');
                   2962:     }
1.32      matthew  2963:     return $result;
                   2964: }
                   2965: 
1.1106    raeburn  2966: sub authform_internal {
1.586     raeburn  2967:     my %in = (
1.32      matthew  2968:                 formname => 'document.cu',
                   2969:                 kerb_def_dom => 'MSU.EDU',
                   2970:                 @_,
                   2971:                 );
1.586     raeburn  2972:     my ($intcheck,$intarg,$result,$authtype,$autharg,$jscall);
1.1106    raeburn  2973:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.591     raeburn  2974:     if (defined($in{'curr_authtype'})) {
                   2975:         if ($in{'curr_authtype'} eq 'int') {
1.586     raeburn  2976:             if ($can_assign{'int'}) {
1.772     bisitz   2977:                 $intcheck = 'checked="checked" ';
1.623     raeburn  2978:                 if (defined($in{'mode'})) {
                   2979:                     if ($in{'mode'} eq 'modifyuser') {
                   2980:                         $intcheck = '';
                   2981:                     }
                   2982:                 }
1.591     raeburn  2983:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2984:                     $intarg = $in{'curr_autharg'};
                   2985:                 }
                   2986:             } else {
                   2987:                 $result = &mt('Currently internally authenticated.');
                   2988:                 return $result;
1.165     raeburn  2989:             }
                   2990:         }
1.586     raeburn  2991:     } else {
                   2992:         if ($authnum == 1) {
1.784     bisitz   2993:             $authtype = '<input type="hidden" name="login" value="int" />';
1.586     raeburn  2994:         }
                   2995:     }
                   2996:     if (!$can_assign{'int'}) {
                   2997:         return;
1.587     raeburn  2998:     } elsif ($authtype eq '') {
1.591     raeburn  2999:         if (defined($in{'mode'})) {
1.587     raeburn  3000:             if ($in{'mode'} eq 'modifycourse') {
                   3001:                 if ($authnum == 1) {
1.1104    raeburn  3002:                     $authtype = '<input type="radio" name="login" value="int" />';
1.587     raeburn  3003:                 }
                   3004:             }
                   3005:         }
1.165     raeburn  3006:     }
1.586     raeburn  3007:     $jscall = "javascript:changed_radio('int',$in{'formname'});";
                   3008:     if ($authtype eq '') {
                   3009:         $authtype = '<input type="radio" name="login" value="int" '.$intcheck.
                   3010:                     ' onchange="'.$jscall.'" onclick="'.$jscall.'" />';
                   3011:     }
1.605     bisitz   3012:     $autharg = '<input type="password" size="10" name="intarg" value="'.
1.586     raeburn  3013:                $intarg.'" onchange="'.$jscall.'" />';
                   3014:     $result = &mt
1.144     matthew  3015:         ('[_1] Internally authenticated (with initial password [_2])',
1.586     raeburn  3016:          '<label>'.$authtype,'</label>'.$autharg);
1.824     bisitz   3017:     $result.="<label><input type=\"checkbox\" name=\"visible\" onclick='if (this.checked) { this.form.intarg.type=\"text\" } else { this.form.intarg.type=\"password\" }' />".&mt('Visible input').'</label>';
1.32      matthew  3018:     return $result;
                   3019: }
                   3020: 
1.1104    raeburn  3021: sub authform_local {
1.32      matthew  3022:     my %in = (
                   3023:               formname => 'document.cu',
                   3024:               kerb_def_dom => 'MSU.EDU',
                   3025:               @_,
                   3026:               );
1.586     raeburn  3027:     my ($loccheck,$locarg,$result,$authtype,$autharg,$jscall);
1.1106    raeburn  3028:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.591     raeburn  3029:     if (defined($in{'curr_authtype'})) {
                   3030:         if ($in{'curr_authtype'} eq 'loc') {
1.586     raeburn  3031:             if ($can_assign{'loc'}) {
1.772     bisitz   3032:                 $loccheck = 'checked="checked" ';
1.623     raeburn  3033:                 if (defined($in{'mode'})) {
                   3034:                     if ($in{'mode'} eq 'modifyuser') {
                   3035:                         $loccheck = '';
                   3036:                     }
                   3037:                 }
1.591     raeburn  3038:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  3039:                     $locarg = $in{'curr_autharg'};
                   3040:                 }
                   3041:             } else {
                   3042:                 $result = &mt('Currently using local (institutional) authentication.');
                   3043:                 return $result;
1.165     raeburn  3044:             }
                   3045:         }
1.586     raeburn  3046:     } else {
                   3047:         if ($authnum == 1) {
1.784     bisitz   3048:             $authtype = '<input type="hidden" name="login" value="loc" />';
1.586     raeburn  3049:         }
                   3050:     }
                   3051:     if (!$can_assign{'loc'}) {
                   3052:         return;
1.587     raeburn  3053:     } elsif ($authtype eq '') {
1.591     raeburn  3054:         if (defined($in{'mode'})) {
1.587     raeburn  3055:             if ($in{'mode'} eq 'modifycourse') {
                   3056:                 if ($authnum == 1) {
1.1104    raeburn  3057:                     $authtype = '<input type="radio" name="login" value="loc" />';
1.587     raeburn  3058:                 }
                   3059:             }
                   3060:         }
1.165     raeburn  3061:     }
1.586     raeburn  3062:     $jscall = "javascript:changed_radio('loc',$in{'formname'});";
                   3063:     if ($authtype eq '') {
                   3064:         $authtype = '<input type="radio" name="login" value="loc" '.
                   3065:                     $loccheck.' onchange="'.$jscall.'" onclick="'.
                   3066:                     $jscall.'" />';
                   3067:     }
                   3068:     $autharg = '<input type="text" size="10" name="locarg" value="'.
                   3069:                $locarg.'" onchange="'.$jscall.'" />';
                   3070:     $result = &mt('[_1] Local Authentication with argument [_2]',
                   3071:                   '<label>'.$authtype,'</label>'.$autharg);
1.32      matthew  3072:     return $result;
                   3073: }
                   3074: 
1.1106    raeburn  3075: sub authform_filesystem {
1.32      matthew  3076:     my %in = (
                   3077:               formname => 'document.cu',
                   3078:               kerb_def_dom => 'MSU.EDU',
                   3079:               @_,
                   3080:               );
1.586     raeburn  3081:     my ($fsyscheck,$result,$authtype,$autharg,$jscall);
1.1106    raeburn  3082:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.591     raeburn  3083:     if (defined($in{'curr_authtype'})) {
                   3084:         if ($in{'curr_authtype'} eq 'fsys') {
1.586     raeburn  3085:             if ($can_assign{'fsys'}) {
1.772     bisitz   3086:                 $fsyscheck = 'checked="checked" ';
1.623     raeburn  3087:                 if (defined($in{'mode'})) {
                   3088:                     if ($in{'mode'} eq 'modifyuser') {
                   3089:                         $fsyscheck = '';
                   3090:                     }
                   3091:                 }
1.586     raeburn  3092:             } else {
                   3093:                 $result = &mt('Currently Filesystem Authenticated.');
                   3094:                 return $result;
                   3095:             }           
                   3096:         }
                   3097:     } else {
                   3098:         if ($authnum == 1) {
1.784     bisitz   3099:             $authtype = '<input type="hidden" name="login" value="fsys" />';
1.586     raeburn  3100:         }
                   3101:     }
                   3102:     if (!$can_assign{'fsys'}) {
                   3103:         return;
1.587     raeburn  3104:     } elsif ($authtype eq '') {
1.591     raeburn  3105:         if (defined($in{'mode'})) {
1.587     raeburn  3106:             if ($in{'mode'} eq 'modifycourse') {
                   3107:                 if ($authnum == 1) {
1.1104    raeburn  3108:                     $authtype = '<input type="radio" name="login" value="fsys" />';
1.587     raeburn  3109:                 }
                   3110:             }
                   3111:         }
1.586     raeburn  3112:     }
                   3113:     $jscall = "javascript:changed_radio('fsys',$in{'formname'});";
                   3114:     if ($authtype eq '') {
                   3115:         $authtype = '<input type="radio" name="login" value="fsys" '.
                   3116:                     $fsyscheck.' onchange="'.$jscall.'" onclick="'.
                   3117:                     $jscall.'" />';
                   3118:     }
                   3119:     $autharg = '<input type="text" size="10" name="fsysarg" value=""'.
                   3120:                ' onchange="'.$jscall.'" />';
                   3121:     $result = &mt
1.144     matthew  3122:         ('[_1] Filesystem Authenticated (with initial password [_2])',
1.281     albertel 3123:          '<label><input type="radio" name="login" value="fsys" '.
1.586     raeburn  3124:          $fsyscheck.'onchange="'.$jscall.'" onclick="'.$jscall.'" />',
1.605     bisitz   3125:          '</label><input type="password" size="10" name="fsysarg" value="" '.
1.144     matthew  3126:                   'onchange="'.$jscall.'" />');
1.32      matthew  3127:     return $result;
                   3128: }
                   3129: 
1.586     raeburn  3130: sub get_assignable_auth {
                   3131:     my ($dom) = @_;
                   3132:     if ($dom eq '') {
                   3133:         $dom = $env{'request.role.domain'};
                   3134:     }
                   3135:     my %can_assign = (
                   3136:                           krb4 => 1,
                   3137:                           krb5 => 1,
                   3138:                           int  => 1,
                   3139:                           loc  => 1,
                   3140:                      );
                   3141:     my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);
                   3142:     if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   3143:         if (ref($domconfig{'usercreation'}{'authtypes'}) eq 'HASH') {
                   3144:             my $authhash = $domconfig{'usercreation'}{'authtypes'};
                   3145:             my $context;
                   3146:             if ($env{'request.role'} =~ /^au/) {
                   3147:                 $context = 'author';
                   3148:             } elsif ($env{'request.role'} =~ /^dc/) {
                   3149:                 $context = 'domain';
                   3150:             } elsif ($env{'request.course.id'}) {
                   3151:                 $context = 'course';
                   3152:             }
                   3153:             if ($context) {
                   3154:                 if (ref($authhash->{$context}) eq 'HASH') {
                   3155:                    %can_assign = %{$authhash->{$context}}; 
                   3156:                 }
                   3157:             }
                   3158:         }
                   3159:     }
                   3160:     my $authnum = 0;
                   3161:     foreach my $key (keys(%can_assign)) {
                   3162:         if ($can_assign{$key}) {
                   3163:             $authnum ++;
                   3164:         }
                   3165:     }
                   3166:     if ($can_assign{'krb4'} && $can_assign{'krb5'}) {
                   3167:         $authnum --;
                   3168:     }
                   3169:     return ($authnum,%can_assign);
                   3170: }
                   3171: 
1.80      albertel 3172: ###############################################################
                   3173: ##    Get Kerberos Defaults for Domain                 ##
                   3174: ###############################################################
                   3175: ##
                   3176: ## Returns default kerberos version and an associated argument
                   3177: ## as listed in file domain.tab. If not listed, provides
                   3178: ## appropriate default domain and kerberos version.
                   3179: ##
                   3180: #-------------------------------------------
                   3181: 
                   3182: =pod
                   3183: 
1.648     raeburn  3184: =item * &get_kerberos_defaults()
1.80      albertel 3185: 
                   3186: get_kerberos_defaults($target_domain) returns the default kerberos
1.641     raeburn  3187: version and domain. If not found, it defaults to version 4 and the 
                   3188: domain of the server.
1.80      albertel 3189: 
1.648     raeburn  3190: =over 4
                   3191: 
1.80      albertel 3192: ($def_version, $def_krb_domain) = &get_kerberos_defaults($target_domain);
                   3193: 
1.648     raeburn  3194: =back
                   3195: 
                   3196: =back
                   3197: 
1.80      albertel 3198: =cut
                   3199: 
                   3200: #-------------------------------------------
                   3201: sub get_kerberos_defaults {
                   3202:     my $domain=shift;
1.641     raeburn  3203:     my ($krbdef,$krbdefdom);
                   3204:     my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
                   3205:     if (($domdefaults{'auth_def'} =~/^krb(4|5)$/) && ($domdefaults{'auth_arg_def'} ne '')) {
                   3206:         $krbdef = $domdefaults{'auth_def'};
                   3207:         $krbdefdom = $domdefaults{'auth_arg_def'};
                   3208:     } else {
1.80      albertel 3209:         $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/;
                   3210:         my $krbdefdom=$1;
                   3211:         $krbdefdom=~tr/a-z/A-Z/;
                   3212:         $krbdef = "krb4";
                   3213:     }
                   3214:     return ($krbdef,$krbdefdom);
                   3215: }
1.112     bowersj2 3216: 
1.32      matthew  3217: 
1.46      matthew  3218: ###############################################################
                   3219: ##                Thesaurus Functions                        ##
                   3220: ###############################################################
1.20      www      3221: 
1.46      matthew  3222: =pod
1.20      www      3223: 
1.112     bowersj2 3224: =head1 Thesaurus Functions
                   3225: 
                   3226: =over 4
                   3227: 
1.648     raeburn  3228: =item * &initialize_keywords()
1.46      matthew  3229: 
                   3230: Initializes the package variable %Keywords if it is empty.  Uses the
                   3231: package variable $thesaurus_db_file.
                   3232: 
                   3233: =cut
                   3234: 
                   3235: ###################################################
                   3236: 
                   3237: sub initialize_keywords {
                   3238:     return 1 if (scalar keys(%Keywords));
                   3239:     # If we are here, %Keywords is empty, so fill it up
                   3240:     #   Make sure the file we need exists...
                   3241:     if (! -e $thesaurus_db_file) {
                   3242:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file".
                   3243:                                  " failed because it does not exist");
                   3244:         return 0;
                   3245:     }
                   3246:     #   Set up the hash as a database
                   3247:     my %thesaurus_db;
                   3248:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 3249:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  3250:         &Apache::lonnet::logthis("Could not tie \%thesaurus_db to ".
                   3251:                                  $thesaurus_db_file);
                   3252:         return 0;
                   3253:     } 
                   3254:     #  Get the average number of appearances of a word.
                   3255:     my $avecount = $thesaurus_db{'average.count'};
                   3256:     #  Put keywords (those that appear > average) into %Keywords
                   3257:     while (my ($word,$data)=each (%thesaurus_db)) {
                   3258:         my ($count,undef) = split /:/,$data;
                   3259:         $Keywords{$word}++ if ($count > $avecount);
                   3260:     }
                   3261:     untie %thesaurus_db;
                   3262:     # Remove special values from %Keywords.
1.356     albertel 3263:     foreach my $value ('total.count','average.count') {
                   3264:         delete($Keywords{$value}) if (exists($Keywords{$value}));
1.586     raeburn  3265:   }
1.46      matthew  3266:     return 1;
                   3267: }
                   3268: 
                   3269: ###################################################
                   3270: 
                   3271: =pod
                   3272: 
1.648     raeburn  3273: =item * &keyword($word)
1.46      matthew  3274: 
                   3275: Returns true if $word is a keyword.  A keyword is a word that appears more 
                   3276: than the average number of times in the thesaurus database.  Calls 
                   3277: &initialize_keywords
                   3278: 
                   3279: =cut
                   3280: 
                   3281: ###################################################
1.20      www      3282: 
                   3283: sub keyword {
1.46      matthew  3284:     return if (!&initialize_keywords());
                   3285:     my $word=lc(shift());
                   3286:     $word=~s/\W//g;
                   3287:     return exists($Keywords{$word});
1.20      www      3288: }
1.46      matthew  3289: 
                   3290: ###############################################################
                   3291: 
                   3292: =pod 
1.20      www      3293: 
1.648     raeburn  3294: =item * &get_related_words()
1.46      matthew  3295: 
1.160     matthew  3296: Look up a word in the thesaurus.  Takes a scalar argument and returns
1.46      matthew  3297: an array of words.  If the keyword is not in the thesaurus, an empty array
                   3298: will be returned.  The order of the words returned is determined by the
                   3299: database which holds them.
                   3300: 
                   3301: Uses global $thesaurus_db_file.
                   3302: 
1.1057    foxr     3303: 
1.46      matthew  3304: =cut
                   3305: 
                   3306: ###############################################################
                   3307: sub get_related_words {
                   3308:     my $keyword = shift;
                   3309:     my %thesaurus_db;
                   3310:     if (! -e $thesaurus_db_file) {
                   3311:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file ".
                   3312:                                  "failed because the file does not exist");
                   3313:         return ();
                   3314:     }
                   3315:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 3316:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  3317:         return ();
                   3318:     } 
                   3319:     my @Words=();
1.429     www      3320:     my $count=0;
1.46      matthew  3321:     if (exists($thesaurus_db{$keyword})) {
1.356     albertel 3322: 	# The first element is the number of times
                   3323: 	# the word appears.  We do not need it now.
1.429     www      3324: 	my (undef,@RelatedWords) = (split(/:/,$thesaurus_db{$keyword}));
                   3325: 	my (undef,$mostfrequentcount)=split(/\,/,$RelatedWords[0]);
                   3326: 	my $threshold=$mostfrequentcount/10;
                   3327:         foreach my $possibleword (@RelatedWords) {
                   3328:             my ($word,$wordcount)=split(/\,/,$possibleword);
                   3329:             if ($wordcount>$threshold) {
                   3330: 		push(@Words,$word);
                   3331:                 $count++;
                   3332:                 if ($count>10) { last; }
                   3333: 	    }
1.20      www      3334:         }
                   3335:     }
1.46      matthew  3336:     untie %thesaurus_db;
                   3337:     return @Words;
1.14      harris41 3338: }
1.1090    foxr     3339: ###############################################################
                   3340: #
                   3341: #  Spell checking
                   3342: #
                   3343: 
                   3344: =pod
                   3345: 
1.1142    raeburn  3346: =back
                   3347: 
1.1090    foxr     3348: =head1 Spell checking
                   3349: 
                   3350: =over 4
                   3351: 
                   3352: =item * &check_spelling($wordlist $language)
                   3353: 
                   3354: Takes a string containing words and feeds it to an external
                   3355: spellcheck program via a pipeline. Returns a string containing
                   3356: them mis-spelled words.
                   3357: 
                   3358: Parameters:
                   3359: 
                   3360: =over 4
                   3361: 
                   3362: =item - $wordlist
                   3363: 
                   3364: String that will be fed into the spellcheck program.
                   3365: 
                   3366: =item - $language
                   3367: 
                   3368: Language string that specifies the language for which the spell
                   3369: check will be performed.
                   3370: 
                   3371: =back
                   3372: 
                   3373: =back
                   3374: 
                   3375: Note: This sub assumes that aspell is installed.
                   3376: 
                   3377: 
                   3378: =cut
                   3379: 
1.46      matthew  3380: 
1.1090    foxr     3381: sub check_spelling {
                   3382:     my ($wordlist, $language) = @_;
1.1091    foxr     3383:     my @misspellings;
                   3384:     
                   3385:     # Generate the speller and set the langauge.
                   3386:     # if explicitly selected:
1.1090    foxr     3387: 
1.1091    foxr     3388:     my $speller = Text::Aspell->new;
1.1090    foxr     3389:     if ($language) {
1.1091    foxr     3390: 	$speller->set_option('lang', $language);
1.1090    foxr     3391:     }
                   3392: 
1.1091    foxr     3393:     # Turn the word list into an array of words by splittingon whitespace
1.1090    foxr     3394: 
1.1091    foxr     3395:     my @words = split(/\s+/, $wordlist);
1.1090    foxr     3396: 
1.1091    foxr     3397:     foreach my $word (@words) {
                   3398: 	if(! $speller->check($word)) {
                   3399: 	    push(@misspellings, $word);
1.1090    foxr     3400: 	}
                   3401:     }
1.1091    foxr     3402:     return join(' ', @misspellings);
                   3403:     
1.1090    foxr     3404: }
                   3405: 
1.61      www      3406: # -------------------------------------------------------------- Plaintext name
1.81      albertel 3407: =pod
                   3408: 
1.112     bowersj2 3409: =head1 User Name Functions
                   3410: 
                   3411: =over 4
                   3412: 
1.648     raeburn  3413: =item * &plainname($uname,$udom,$first)
1.81      albertel 3414: 
1.112     bowersj2 3415: Takes a users logon name and returns it as a string in
1.226     albertel 3416: "first middle last generation" form 
                   3417: if $first is set to 'lastname' then it returns it as
                   3418: 'lastname generation, firstname middlename' if their is a lastname
1.81      albertel 3419: 
                   3420: =cut
1.61      www      3421: 
1.295     www      3422: 
1.81      albertel 3423: ###############################################################
1.61      www      3424: sub plainname {
1.226     albertel 3425:     my ($uname,$udom,$first)=@_;
1.537     albertel 3426:     return if (!defined($uname) || !defined($udom));
1.295     www      3427:     my %names=&getnames($uname,$udom);
1.226     albertel 3428:     my $name=&Apache::lonnet::format_name($names{'firstname'},
                   3429: 					  $names{'middlename'},
                   3430: 					  $names{'lastname'},
                   3431: 					  $names{'generation'},$first);
                   3432:     $name=~s/^\s+//;
1.62      www      3433:     $name=~s/\s+$//;
                   3434:     $name=~s/\s+/ /g;
1.353     albertel 3435:     if ($name !~ /\S/) { $name=$uname.':'.$udom; }
1.62      www      3436:     return $name;
1.61      www      3437: }
1.66      www      3438: 
                   3439: # -------------------------------------------------------------------- Nickname
1.81      albertel 3440: =pod
                   3441: 
1.648     raeburn  3442: =item * &nickname($uname,$udom)
1.81      albertel 3443: 
                   3444: Gets a users name and returns it as a string as
                   3445: 
                   3446: "&quot;nickname&quot;"
1.66      www      3447: 
1.81      albertel 3448: if the user has a nickname or
                   3449: 
                   3450: "first middle last generation"
                   3451: 
                   3452: if the user does not
                   3453: 
                   3454: =cut
1.66      www      3455: 
                   3456: sub nickname {
                   3457:     my ($uname,$udom)=@_;
1.537     albertel 3458:     return if (!defined($uname) || !defined($udom));
1.295     www      3459:     my %names=&getnames($uname,$udom);
1.68      albertel 3460:     my $name=$names{'nickname'};
1.66      www      3461:     if ($name) {
                   3462:        $name='&quot;'.$name.'&quot;'; 
                   3463:     } else {
                   3464:        $name=$names{'firstname'}.' '.$names{'middlename'}.' '.
                   3465: 	     $names{'lastname'}.' '.$names{'generation'};
                   3466:        $name=~s/\s+$//;
                   3467:        $name=~s/\s+/ /g;
                   3468:     }
                   3469:     return $name;
                   3470: }
                   3471: 
1.295     www      3472: sub getnames {
                   3473:     my ($uname,$udom)=@_;
1.537     albertel 3474:     return if (!defined($uname) || !defined($udom));
1.433     albertel 3475:     if ($udom eq 'public' && $uname eq 'public') {
                   3476: 	return ('lastname' => &mt('Public'));
                   3477:     }
1.295     www      3478:     my $id=$uname.':'.$udom;
                   3479:     my ($names,$cached)=&Apache::lonnet::is_cached_new('namescache',$id);
                   3480:     if ($cached) {
                   3481: 	return %{$names};
                   3482:     } else {
                   3483: 	my %loadnames=&Apache::lonnet::get('environment',
                   3484:                     ['firstname','middlename','lastname','generation','nickname'],
                   3485: 					 $udom,$uname);
                   3486: 	&Apache::lonnet::do_cache_new('namescache',$id,\%loadnames);
                   3487: 	return %loadnames;
                   3488:     }
                   3489: }
1.61      www      3490: 
1.542     raeburn  3491: # -------------------------------------------------------------------- getemails
1.648     raeburn  3492: 
1.542     raeburn  3493: =pod
                   3494: 
1.648     raeburn  3495: =item * &getemails($uname,$udom)
1.542     raeburn  3496: 
                   3497: Gets a user's email information and returns it as a hash with keys:
                   3498: notification, critnotification, permanentemail
                   3499: 
                   3500: For notification and critnotification, values are comma-separated lists 
1.648     raeburn  3501: of e-mail addresses; for permanentemail, value is a single e-mail address.
1.542     raeburn  3502:  
1.648     raeburn  3503: 
1.542     raeburn  3504: =cut
                   3505: 
1.648     raeburn  3506: 
1.466     albertel 3507: sub getemails {
                   3508:     my ($uname,$udom)=@_;
                   3509:     if ($udom eq 'public' && $uname eq 'public') {
                   3510: 	return;
                   3511:     }
1.467     www      3512:     if (!$udom) { $udom=$env{'user.domain'}; }
                   3513:     if (!$uname) { $uname=$env{'user.name'}; }
1.466     albertel 3514:     my $id=$uname.':'.$udom;
                   3515:     my ($names,$cached)=&Apache::lonnet::is_cached_new('emailscache',$id);
                   3516:     if ($cached) {
                   3517: 	return %{$names};
                   3518:     } else {
                   3519: 	my %loadnames=&Apache::lonnet::get('environment',
                   3520:                     			   ['notification','critnotification',
                   3521: 					    'permanentemail'],
                   3522: 					   $udom,$uname);
                   3523: 	&Apache::lonnet::do_cache_new('emailscache',$id,\%loadnames);
                   3524: 	return %loadnames;
                   3525:     }
                   3526: }
                   3527: 
1.551     albertel 3528: sub flush_email_cache {
                   3529:     my ($uname,$udom)=@_;
                   3530:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   3531:     if (!$uname) { $uname=$env{'user.name'};   }
                   3532:     return if ($udom eq 'public' && $uname eq 'public');
                   3533:     my $id=$uname.':'.$udom;
                   3534:     &Apache::lonnet::devalidate_cache_new('emailscache',$id);
                   3535: }
                   3536: 
1.728     raeburn  3537: # -------------------------------------------------------------------- getlangs
                   3538: 
                   3539: =pod
                   3540: 
                   3541: =item * &getlangs($uname,$udom)
                   3542: 
                   3543: Gets a user's language preference and returns it as a hash with key:
                   3544: language.
                   3545: 
                   3546: =cut
                   3547: 
                   3548: 
                   3549: sub getlangs {
                   3550:     my ($uname,$udom) = @_;
                   3551:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   3552:     if (!$uname) { $uname=$env{'user.name'};   }
                   3553:     my $id=$uname.':'.$udom;
                   3554:     my ($langs,$cached)=&Apache::lonnet::is_cached_new('userlangs',$id);
                   3555:     if ($cached) {
                   3556:         return %{$langs};
                   3557:     } else {
                   3558:         my %loadlangs=&Apache::lonnet::get('environment',['languages'],
                   3559:                                            $udom,$uname);
                   3560:         &Apache::lonnet::do_cache_new('userlangs',$id,\%loadlangs);
                   3561:         return %loadlangs;
                   3562:     }
                   3563: }
                   3564: 
                   3565: sub flush_langs_cache {
                   3566:     my ($uname,$udom)=@_;
                   3567:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   3568:     if (!$uname) { $uname=$env{'user.name'};   }
                   3569:     return if ($udom eq 'public' && $uname eq 'public');
                   3570:     my $id=$uname.':'.$udom;
                   3571:     &Apache::lonnet::devalidate_cache_new('userlangs',$id);
                   3572: }
                   3573: 
1.61      www      3574: # ------------------------------------------------------------------ Screenname
1.81      albertel 3575: 
                   3576: =pod
                   3577: 
1.648     raeburn  3578: =item * &screenname($uname,$udom)
1.81      albertel 3579: 
                   3580: Gets a users screenname and returns it as a string
                   3581: 
                   3582: =cut
1.61      www      3583: 
                   3584: sub screenname {
                   3585:     my ($uname,$udom)=@_;
1.258     albertel 3586:     if ($uname eq $env{'user.name'} &&
                   3587: 	$udom eq $env{'user.domain'}) {return $env{'environment.screenname'};}
1.212     albertel 3588:     my %names=&Apache::lonnet::get('environment',['screenname'],$udom,$uname);
1.68      albertel 3589:     return $names{'screenname'};
1.62      www      3590: }
                   3591: 
1.212     albertel 3592: 
1.802     bisitz   3593: # ------------------------------------------------------------- Confirm Wrapper
                   3594: =pod
                   3595: 
1.1142    raeburn  3596: =item * &confirmwrapper($message)
1.802     bisitz   3597: 
                   3598: Wrap messages about completion of operation in box
                   3599: 
                   3600: =cut
                   3601: 
                   3602: sub confirmwrapper {
                   3603:     my ($message)=@_;
                   3604:     if ($message) {
                   3605:         return "\n".'<div class="LC_confirm_box">'."\n"
                   3606:                .$message."\n"
                   3607:                .'</div>'."\n";
                   3608:     } else {
                   3609:         return $message;
                   3610:     }
                   3611: }
                   3612: 
1.62      www      3613: # ------------------------------------------------------------- Message Wrapper
                   3614: 
                   3615: sub messagewrapper {
1.369     www      3616:     my ($link,$username,$domain,$subject,$text)=@_;
1.62      www      3617:     return 
1.441     albertel 3618:         '<a href="/adm/email?compose=individual&amp;'.
                   3619:         'recname='.$username.'&amp;recdom='.$domain.
                   3620: 	'&amp;subject='.&escape($subject).'&amp;text='.&escape($text).'" '.
1.200     matthew  3621:         'title="'.&mt('Send message').'">'.$link.'</a>';
1.74      www      3622: }
1.802     bisitz   3623: 
1.74      www      3624: # --------------------------------------------------------------- Notes Wrapper
                   3625: 
                   3626: sub noteswrapper {
                   3627:     my ($link,$un,$do)=@_;
                   3628:     return 
1.896     amueller 3629: "<a href='/adm/email?recordftf=retrieve&amp;recname=$un&amp;recdom=$do'>$link</a>";
1.62      www      3630: }
1.802     bisitz   3631: 
1.62      www      3632: # ------------------------------------------------------------- Aboutme Wrapper
                   3633: 
                   3634: sub aboutmewrapper {
1.1070    raeburn  3635:     my ($link,$username,$domain,$target,$class)=@_;
1.447     raeburn  3636:     if (!defined($username)  && !defined($domain)) {
                   3637:         return;
                   3638:     }
1.1096    raeburn  3639:     return '<a href="/adm/'.$domain.'/'.$username.'/aboutme"'.
1.1070    raeburn  3640: 	($target?' target="'.$target.'"':'').($class?' class="'.$class.'"':'').' title="'.&mt("View this user's personal information page").'">'.$link.'</a>';
1.62      www      3641: }
                   3642: 
                   3643: # ------------------------------------------------------------ Syllabus Wrapper
                   3644: 
                   3645: sub syllabuswrapper {
1.707     bisitz   3646:     my ($linktext,$coursedir,$domain)=@_;
1.208     matthew  3647:     return qq{<a href="/public/$domain/$coursedir/syllabus">$linktext</a>};
1.61      www      3648: }
1.14      harris41 3649: 
1.802     bisitz   3650: # -----------------------------------------------------------------------------
                   3651: 
1.208     matthew  3652: sub track_student_link {
1.887     raeburn  3653:     my ($linktext,$sname,$sdom,$target,$start,$only_body) = @_;
1.268     albertel 3654:     my $link ="/adm/trackstudent?";
1.208     matthew  3655:     my $title = 'View recent activity';
                   3656:     if (defined($sname) && $sname !~ /^\s*$/ &&
                   3657:         defined($sdom)  && $sdom  !~ /^\s*$/) {
1.268     albertel 3658:         $link .= "selected_student=$sname:$sdom";
1.208     matthew  3659:         $title .= ' of this student';
1.268     albertel 3660:     } 
1.208     matthew  3661:     if (defined($target) && $target !~ /^\s*$/) {
                   3662:         $target = qq{target="$target"};
                   3663:     } else {
                   3664:         $target = '';
                   3665:     }
1.268     albertel 3666:     if ($start) { $link.='&amp;start='.$start; }
1.887     raeburn  3667:     if ($only_body) { $link .= '&amp;only_body=1'; }
1.554     albertel 3668:     $title = &mt($title);
                   3669:     $linktext = &mt($linktext);
1.448     albertel 3670:     return qq{<a href="$link" title="$title" $target>$linktext</a>}.
                   3671: 	&help_open_topic('View_recent_activity');
1.208     matthew  3672: }
                   3673: 
1.781     raeburn  3674: sub slot_reservations_link {
                   3675:     my ($linktext,$sname,$sdom,$target) = @_;
                   3676:     my $link ="/adm/slotrequest?command=showresv&amp;origin=aboutme";
                   3677:     my $title = 'View slot reservation history';
                   3678:     if (defined($sname) && $sname !~ /^\s*$/ &&
                   3679:         defined($sdom)  && $sdom  !~ /^\s*$/) {
                   3680:         $link .= "&amp;uname=$sname&amp;udom=$sdom";
                   3681:         $title .= ' of this student';
                   3682:     }
                   3683:     if (defined($target) && $target !~ /^\s*$/) {
                   3684:         $target = qq{target="$target"};
                   3685:     } else {
                   3686:         $target = '';
                   3687:     }
                   3688:     $title = &mt($title);
                   3689:     $linktext = &mt($linktext);
                   3690:     return qq{<a href="$link" title="$title" $target>$linktext</a>};
                   3691: # FIXME uncomment when help item created: &help_open_topic('Slot_Reservation_History');
                   3692: 
                   3693: }
                   3694: 
1.508     www      3695: # ===================================================== Display a student photo
                   3696: 
                   3697: 
1.509     albertel 3698: sub student_image_tag {
1.508     www      3699:     my ($domain,$user)=@_;
                   3700:     my $imgsrc=&Apache::lonnet::studentphoto($domain,$user,'jpg');
                   3701:     if (($imgsrc) && ($imgsrc ne '/adm/lonKaputt/lonlogo_broken.gif')) {
                   3702: 	return '<img src="'.$imgsrc.'" align="right" />';
                   3703:     } else {
                   3704: 	return '';
                   3705:     }
                   3706: }
                   3707: 
1.112     bowersj2 3708: =pod
                   3709: 
                   3710: =back
                   3711: 
                   3712: =head1 Access .tab File Data
                   3713: 
                   3714: =over 4
                   3715: 
1.648     raeburn  3716: =item * &languageids() 
1.112     bowersj2 3717: 
                   3718: returns list of all language ids
                   3719: 
                   3720: =cut
                   3721: 
1.14      harris41 3722: sub languageids {
1.16      harris41 3723:     return sort(keys(%language));
1.14      harris41 3724: }
                   3725: 
1.112     bowersj2 3726: =pod
                   3727: 
1.648     raeburn  3728: =item * &languagedescription() 
1.112     bowersj2 3729: 
                   3730: returns description of a specified language id
                   3731: 
                   3732: =cut
                   3733: 
1.14      harris41 3734: sub languagedescription {
1.125     www      3735:     my $code=shift;
                   3736:     return  ($supported_language{$code}?'* ':'').
                   3737:             $language{$code}.
1.126     www      3738: 	    ($supported_language{$code}?' ('.&mt('interface available').')':'');
1.145     www      3739: }
                   3740: 
1.1048    foxr     3741: =pod
                   3742: 
                   3743: =item * &plainlanguagedescription
                   3744: 
                   3745: Returns both the plain language description (e.g. 'Creoles and Pidgins, English-based (Other)')
                   3746: and the language character encoding (e.g. ISO) separated by a ' - ' string.
                   3747: 
                   3748: =cut
                   3749: 
1.145     www      3750: sub plainlanguagedescription {
                   3751:     my $code=shift;
                   3752:     return $language{$code};
                   3753: }
                   3754: 
1.1048    foxr     3755: =pod
                   3756: 
                   3757: =item * &supportedlanguagecode
                   3758: 
                   3759: Returns the supported language code (e.g. sptutf maps to pt) given a language
                   3760: code.
                   3761: 
                   3762: =cut
                   3763: 
1.145     www      3764: sub supportedlanguagecode {
                   3765:     my $code=shift;
                   3766:     return $supported_language{$code};
1.97      www      3767: }
                   3768: 
1.112     bowersj2 3769: =pod
                   3770: 
1.1048    foxr     3771: =item * &latexlanguage()
                   3772: 
                   3773: Given a language key code returns the correspondnig language to use
                   3774: to select the correct hyphenation on LaTeX printouts.  This is undef if there
                   3775: is no supported hyphenation for the language code.
                   3776: 
                   3777: =cut
                   3778: 
                   3779: sub latexlanguage {
                   3780:     my $code = shift;
                   3781:     return $latex_language{$code};
                   3782: }
                   3783: 
                   3784: =pod
                   3785: 
                   3786: =item * &latexhyphenation()
                   3787: 
                   3788: Same as above but what's supplied is the language as it might be stored
                   3789: in the metadata.
                   3790: 
                   3791: =cut
                   3792: 
                   3793: sub latexhyphenation {
                   3794:     my $key = shift;
                   3795:     return $latex_language_bykey{$key};
                   3796: }
                   3797: 
                   3798: =pod
                   3799: 
1.648     raeburn  3800: =item * &copyrightids() 
1.112     bowersj2 3801: 
                   3802: returns list of all copyrights
                   3803: 
                   3804: =cut
                   3805: 
                   3806: sub copyrightids {
                   3807:     return sort(keys(%cprtag));
                   3808: }
                   3809: 
                   3810: =pod
                   3811: 
1.648     raeburn  3812: =item * &copyrightdescription() 
1.112     bowersj2 3813: 
                   3814: returns description of a specified copyright id
                   3815: 
                   3816: =cut
                   3817: 
                   3818: sub copyrightdescription {
1.166     www      3819:     return &mt($cprtag{shift(@_)});
1.112     bowersj2 3820: }
1.197     matthew  3821: 
                   3822: =pod
                   3823: 
1.648     raeburn  3824: =item * &source_copyrightids() 
1.192     taceyjo1 3825: 
                   3826: returns list of all source copyrights
                   3827: 
                   3828: =cut
                   3829: 
                   3830: sub source_copyrightids {
                   3831:     return sort(keys(%scprtag));
                   3832: }
                   3833: 
                   3834: =pod
                   3835: 
1.648     raeburn  3836: =item * &source_copyrightdescription() 
1.192     taceyjo1 3837: 
                   3838: returns description of a specified source copyright id
                   3839: 
                   3840: =cut
                   3841: 
                   3842: sub source_copyrightdescription {
                   3843:     return &mt($scprtag{shift(@_)});
                   3844: }
1.112     bowersj2 3845: 
                   3846: =pod
                   3847: 
1.648     raeburn  3848: =item * &filecategories() 
1.112     bowersj2 3849: 
                   3850: returns list of all file categories
                   3851: 
                   3852: =cut
                   3853: 
                   3854: sub filecategories {
                   3855:     return sort(keys(%category_extensions));
                   3856: }
                   3857: 
                   3858: =pod
                   3859: 
1.648     raeburn  3860: =item * &filecategorytypes() 
1.112     bowersj2 3861: 
                   3862: returns list of file types belonging to a given file
                   3863: category
                   3864: 
                   3865: =cut
                   3866: 
                   3867: sub filecategorytypes {
1.356     albertel 3868:     my ($cat) = @_;
                   3869:     return @{$category_extensions{lc($cat)}};
1.112     bowersj2 3870: }
                   3871: 
                   3872: =pod
                   3873: 
1.648     raeburn  3874: =item * &fileembstyle() 
1.112     bowersj2 3875: 
                   3876: returns embedding style for a specified file type
                   3877: 
                   3878: =cut
                   3879: 
                   3880: sub fileembstyle {
                   3881:     return $fe{lc(shift(@_))};
1.169     www      3882: }
                   3883: 
1.351     www      3884: sub filemimetype {
                   3885:     return $fm{lc(shift(@_))};
                   3886: }
                   3887: 
1.169     www      3888: 
                   3889: sub filecategoryselect {
                   3890:     my ($name,$value)=@_;
1.189     matthew  3891:     return &select_form($value,$name,
1.970     raeburn  3892:                         {'' => &mt('Any category'), map { $_,$_ } sort(keys(%category_extensions))});
1.112     bowersj2 3893: }
                   3894: 
                   3895: =pod
                   3896: 
1.648     raeburn  3897: =item * &filedescription() 
1.112     bowersj2 3898: 
                   3899: returns description for a specified file type
                   3900: 
                   3901: =cut
                   3902: 
                   3903: sub filedescription {
1.188     matthew  3904:     my $file_description = $fd{lc(shift())};
                   3905:     $file_description =~ s:([\[\]]):~$1:g;
                   3906:     return &mt($file_description);
1.112     bowersj2 3907: }
                   3908: 
                   3909: =pod
                   3910: 
1.648     raeburn  3911: =item * &filedescriptionex() 
1.112     bowersj2 3912: 
                   3913: returns description for a specified file type with
                   3914: extra formatting
                   3915: 
                   3916: =cut
                   3917: 
                   3918: sub filedescriptionex {
                   3919:     my $ex=shift;
1.188     matthew  3920:     my $file_description = $fd{lc($ex)};
                   3921:     $file_description =~ s:([\[\]]):~$1:g;
                   3922:     return '.'.$ex.' '.&mt($file_description);
1.112     bowersj2 3923: }
                   3924: 
                   3925: # End of .tab access
                   3926: =pod
                   3927: 
                   3928: =back
                   3929: 
                   3930: =cut
                   3931: 
                   3932: # ------------------------------------------------------------------ File Types
                   3933: sub fileextensions {
                   3934:     return sort(keys(%fe));
                   3935: }
                   3936: 
1.97      www      3937: # ----------------------------------------------------------- Display Languages
                   3938: # returns a hash with all desired display languages
                   3939: #
                   3940: 
                   3941: sub display_languages {
                   3942:     my %languages=();
1.695     raeburn  3943:     foreach my $lang (&Apache::lonlocal::preferred_languages()) {
1.356     albertel 3944: 	$languages{$lang}=1;
1.97      www      3945:     }
                   3946:     &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']);
1.258     albertel 3947:     if ($env{'form.displaylanguage'}) {
1.356     albertel 3948: 	foreach my $lang (split(/\s*(\,|\;|\:)\s*/,$env{'form.displaylanguage'})) {
                   3949: 	    $languages{$lang}=1;
1.97      www      3950:         }
                   3951:     }
                   3952:     return %languages;
1.14      harris41 3953: }
                   3954: 
1.582     albertel 3955: sub languages {
                   3956:     my ($possible_langs) = @_;
1.695     raeburn  3957:     my @preferred_langs = &Apache::lonlocal::preferred_languages();
1.582     albertel 3958:     if (!ref($possible_langs)) {
                   3959: 	if( wantarray ) {
                   3960: 	    return @preferred_langs;
                   3961: 	} else {
                   3962: 	    return $preferred_langs[0];
                   3963: 	}
                   3964:     }
                   3965:     my %possibilities = map { $_ => 1 } (@$possible_langs);
                   3966:     my @preferred_possibilities;
                   3967:     foreach my $preferred_lang (@preferred_langs) {
                   3968: 	if (exists($possibilities{$preferred_lang})) {
                   3969: 	    push(@preferred_possibilities, $preferred_lang);
                   3970: 	}
                   3971:     }
                   3972:     if( wantarray ) {
                   3973: 	return @preferred_possibilities;
                   3974:     }
                   3975:     return $preferred_possibilities[0];
                   3976: }
                   3977: 
1.742     raeburn  3978: sub user_lang {
                   3979:     my ($touname,$toudom,$fromcid) = @_;
                   3980:     my @userlangs;
                   3981:     if (($fromcid ne '') && ($env{'course.'.$fromcid.'.languages'} ne '')) {
                   3982:         @userlangs=(@userlangs,split(/\s*(\,|\;|\:)\s*/,
                   3983:                     $env{'course.'.$fromcid.'.languages'}));
                   3984:     } else {
                   3985:         my %langhash = &getlangs($touname,$toudom);
                   3986:         if ($langhash{'languages'} ne '') {
                   3987:             @userlangs = split(/\s*(\,|\;|\:)\s*/,$langhash{'languages'});
                   3988:         } else {
                   3989:             my %domdefs = &Apache::lonnet::get_domain_defaults($toudom);
                   3990:             if ($domdefs{'lang_def'} ne '') {
                   3991:                 @userlangs = ($domdefs{'lang_def'});
                   3992:             }
                   3993:         }
                   3994:     }
                   3995:     my @languages=&Apache::lonlocal::get_genlanguages(@userlangs);
                   3996:     my $user_lh = Apache::localize->get_handle(@languages);
                   3997:     return $user_lh;
                   3998: }
                   3999: 
                   4000: 
1.112     bowersj2 4001: ###############################################################
                   4002: ##               Student Answer Attempts                     ##
                   4003: ###############################################################
                   4004: 
                   4005: =pod
                   4006: 
                   4007: =head1 Alternate Problem Views
                   4008: 
                   4009: =over 4
                   4010: 
1.648     raeburn  4011: =item * &get_previous_attempt($symb, $username, $domain, $course,
1.1199    raeburn  4012:     $getattempt, $regexp, $gradesub, $usec, $identifier)
1.112     bowersj2 4013: 
                   4014: Return string with previous attempt on problem. Arguments:
                   4015: 
                   4016: =over 4
                   4017: 
                   4018: =item * $symb: Problem, including path
                   4019: 
                   4020: =item * $username: username of the desired student
                   4021: 
                   4022: =item * $domain: domain of the desired student
1.14      harris41 4023: 
1.112     bowersj2 4024: =item * $course: Course ID
1.14      harris41 4025: 
1.112     bowersj2 4026: =item * $getattempt: Leave blank for all attempts, otherwise put
                   4027:     something
1.14      harris41 4028: 
1.112     bowersj2 4029: =item * $regexp: if string matches this regexp, the string will be
                   4030:     sent to $gradesub
1.14      harris41 4031: 
1.112     bowersj2 4032: =item * $gradesub: routine that processes the string if it matches $regexp
1.14      harris41 4033: 
1.1199    raeburn  4034: =item * $usec: section of the desired student
                   4035: 
                   4036: =item * $identifier: counter for student (multiple students one problem) or 
                   4037:     problem (one student; whole sequence).
                   4038: 
1.112     bowersj2 4039: =back
1.14      harris41 4040: 
1.112     bowersj2 4041: The output string is a table containing all desired attempts, if any.
1.16      harris41 4042: 
1.112     bowersj2 4043: =cut
1.1       albertel 4044: 
                   4045: sub get_previous_attempt {
1.1199    raeburn  4046:   my ($symb,$username,$domain,$course,$getattempt,$regexp,$gradesub,$usec,$identifier)=@_;
1.1       albertel 4047:   my $prevattempts='';
1.43      ng       4048:   no strict 'refs';
1.1       albertel 4049:   if ($symb) {
1.3       albertel 4050:     my (%returnhash)=
                   4051:       &Apache::lonnet::restore($symb,$course,$domain,$username);
1.1       albertel 4052:     if ($returnhash{'version'}) {
                   4053:       my %lasthash=();
                   4054:       my $version;
                   4055:       for ($version=1;$version<=$returnhash{'version'};$version++) {
1.1212    raeburn  4056:         foreach my $key (reverse(sort(split(/\:/,$returnhash{$version.':keys'})))) {
                   4057:             if ($key =~ /\.rawrndseed$/) {
                   4058:                 my ($id) = ($key =~ /^(.+)\.rawrndseed$/);
                   4059:                 $lasthash{$id.'.rndseed'} = $returnhash{$version.':'.$key};
                   4060:             } else {
                   4061:                 $lasthash{$key}=$returnhash{$version.':'.$key};
                   4062:             }
1.19      harris41 4063:         }
1.1       albertel 4064:       }
1.596     albertel 4065:       $prevattempts=&start_data_table().&start_data_table_header_row();
                   4066:       $prevattempts.='<th>'.&mt('History').'</th>';
1.1199    raeburn  4067:       my (%typeparts,%lasthidden,%regraded,%hidestatus);
1.945     raeburn  4068:       my $showsurv=&Apache::lonnet::allowed('vas',$env{'request.course.id'});
1.356     albertel 4069:       foreach my $key (sort(keys(%lasthash))) {
                   4070: 	my ($ign,@parts) = split(/\./,$key);
1.41      ng       4071: 	if ($#parts > 0) {
1.31      albertel 4072: 	  my $data=$parts[-1];
1.989     raeburn  4073:           next if ($data eq 'foilorder');
1.31      albertel 4074: 	  pop(@parts);
1.1010    www      4075:           $prevattempts.='<th>'.&mt('Part ').join('.',@parts).'<br />'.$data.'&nbsp;</th>';
1.945     raeburn  4076:           if ($data eq 'type') {
                   4077:               unless ($showsurv) {
                   4078:                   my $id = join(',',@parts);
                   4079:                   $typeparts{$ign.'.'.$id} = $lasthash{$key};
1.978     raeburn  4080:                   if (($lasthash{$key} eq 'anonsurvey') || ($lasthash{$key} eq 'anonsurveycred')) {
                   4081:                       $lasthidden{$ign.'.'.$id} = 1;
                   4082:                   }
1.945     raeburn  4083:               }
1.1199    raeburn  4084:               if ($identifier ne '') {
                   4085:                   my $id = join(',',@parts);
                   4086:                   if (&Apache::lonnet::EXT("resource.$id.problemstatus",$symb,
                   4087:                                                $domain,$username,$usec,undef,$course) =~ /^no/) {
                   4088:                       $hidestatus{$ign.'.'.$id} = 1;
                   4089:                   }
                   4090:               }
                   4091:           } elsif ($data eq 'regrader') {
                   4092:               if (($identifier ne '') && (@parts)) {
1.1200    raeburn  4093:                   my $id = join(',',@parts);
                   4094:                   $regraded{$ign.'.'.$id} = 1;
1.1199    raeburn  4095:               }
1.1010    www      4096:           } 
1.31      albertel 4097: 	} else {
1.41      ng       4098: 	  if ($#parts == 0) {
                   4099: 	    $prevattempts.='<th>'.$parts[0].'</th>';
                   4100: 	  } else {
                   4101: 	    $prevattempts.='<th>'.$ign.'</th>';
                   4102: 	  }
1.31      albertel 4103: 	}
1.16      harris41 4104:       }
1.596     albertel 4105:       $prevattempts.=&end_data_table_header_row();
1.40      ng       4106:       if ($getattempt eq '') {
1.1199    raeburn  4107:         my (%solved,%resets,%probstatus);
1.1200    raeburn  4108:         if (($identifier ne '') && (keys(%regraded) > 0)) {
                   4109:             for ($version=1;$version<=$returnhash{'version'};$version++) {
                   4110:                 foreach my $id (keys(%regraded)) {
                   4111:                     if (($returnhash{$version.':'.$id.'.regrader'}) &&
                   4112:                         ($returnhash{$version.':'.$id.'.tries'} eq '') &&
                   4113:                         ($returnhash{$version.':'.$id.'.award'} eq '')) {
                   4114:                         push(@{$resets{$id}},$version);
1.1199    raeburn  4115:                     }
                   4116:                 }
                   4117:             }
1.1200    raeburn  4118:         }
                   4119: 	for ($version=1;$version<=$returnhash{'version'};$version++) {
1.1199    raeburn  4120:             my (@hidden,@unsolved);
1.945     raeburn  4121:             if (%typeparts) {
                   4122:                 foreach my $id (keys(%typeparts)) {
1.1199    raeburn  4123:                     if (($returnhash{$version.':'.$id.'.type'} eq 'anonsurvey') || 
                   4124:                         ($returnhash{$version.':'.$id.'.type'} eq 'anonsurveycred')) {
1.945     raeburn  4125:                         push(@hidden,$id);
1.1199    raeburn  4126:                     } elsif ($identifier ne '') {
                   4127:                         unless (($returnhash{$version.':'.$id.'.type'} eq 'survey') ||
                   4128:                                 ($returnhash{$version.':'.$id.'.type'} eq 'surveycred') ||
                   4129:                                 ($hidestatus{$id})) {
1.1200    raeburn  4130:                             next if ((ref($resets{$id}) eq 'ARRAY') && grep(/^\Q$version\E$/,@{$resets{$id}}));
1.1199    raeburn  4131:                             if ($returnhash{$version.':'.$id.'.solved'} eq 'correct_by_student') {
                   4132:                                 push(@{$solved{$id}},$version);
                   4133:                             } elsif (($returnhash{$version.':'.$id.'.solved'} ne '') &&
                   4134:                                      (ref($solved{$id}) eq 'ARRAY')) {
                   4135:                                 my $skip;
                   4136:                                 if (ref($resets{$id}) eq 'ARRAY') {
                   4137:                                     foreach my $reset (@{$resets{$id}}) {
                   4138:                                         if ($reset > $solved{$id}[-1]) {
                   4139:                                             $skip=1;
                   4140:                                             last;
                   4141:                                         }
                   4142:                                     }
                   4143:                                 }
                   4144:                                 unless ($skip) {
                   4145:                                     my ($ign,$partslist) = split(/\./,$id,2);
                   4146:                                     push(@unsolved,$partslist);
                   4147:                                 }
                   4148:                             }
                   4149:                         }
1.945     raeburn  4150:                     }
                   4151:                 }
                   4152:             }
                   4153:             $prevattempts.=&start_data_table_row().
1.1199    raeburn  4154:                            '<td>'.&mt('Transaction [_1]',$version);
                   4155:             if (@unsolved) {
                   4156:                 $prevattempts .= '<span class="LC_nobreak"><label>'.
                   4157:                                  '<input type="checkbox" name="HIDE'.$identifier.'" value="'.$version.':'.join('_',@unsolved).'" />'.
                   4158:                                  &mt('Hide').'</label></span>';
                   4159:             }
                   4160:             $prevattempts .= '</td>';
1.945     raeburn  4161:             if (@hidden) {
                   4162:                 foreach my $key (sort(keys(%lasthash))) {
1.989     raeburn  4163:                     next if ($key =~ /\.foilorder$/);
1.945     raeburn  4164:                     my $hide;
                   4165:                     foreach my $id (@hidden) {
                   4166:                         if ($key =~ /^\Q$id\E/) {
                   4167:                             $hide = 1;
                   4168:                             last;
                   4169:                         }
                   4170:                     }
                   4171:                     if ($hide) {
                   4172:                         my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
                   4173:                         if (($data eq 'award') || ($data eq 'awarddetail')) {
                   4174:                             my $value = &format_previous_attempt_value($key,
                   4175:                                              $returnhash{$version.':'.$key});
1.1173    kruse    4176:                             $prevattempts.='<td>'.$value.'&nbsp;</td>';
1.945     raeburn  4177:                         } else {
                   4178:                             $prevattempts.='<td>&nbsp;</td>';
                   4179:                         }
                   4180:                     } else {
                   4181:                         if ($key =~ /\./) {
1.1212    raeburn  4182:                             my $value = $returnhash{$version.':'.$key};
                   4183:                             if ($key =~ /\.rndseed$/) {
                   4184:                                 my ($id) = ($key =~ /^(.+)\.[^.]+$/);
                   4185:                                 if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) {
                   4186:                                     $value = $returnhash{$version.':'.$id.'.rawrndseed'};
                   4187:                                 }
                   4188:                             }
                   4189:                             $prevattempts.='<td>'.&format_previous_attempt_value($key,$value).
                   4190:                                            '&nbsp;</td>';
1.945     raeburn  4191:                         } else {
                   4192:                             $prevattempts.='<td>&nbsp;</td>';
                   4193:                         }
                   4194:                     }
                   4195:                 }
                   4196:             } else {
                   4197: 	        foreach my $key (sort(keys(%lasthash))) {
1.989     raeburn  4198:                     next if ($key =~ /\.foilorder$/);
1.1212    raeburn  4199:                     my $value = $returnhash{$version.':'.$key};
                   4200:                     if ($key =~ /\.rndseed$/) {
                   4201:                         my ($id) = ($key =~ /^(.+)\.[^.]+$/);
                   4202:                         if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) {
                   4203:                             $value = $returnhash{$version.':'.$id.'.rawrndseed'};
                   4204:                         }
                   4205:                     }
                   4206:                     $prevattempts.='<td>'.&format_previous_attempt_value($key,$value).
                   4207:                                    '&nbsp;</td>';
1.945     raeburn  4208: 	        }
                   4209:             }
                   4210: 	    $prevattempts.=&end_data_table_row();
1.40      ng       4211: 	 }
1.1       albertel 4212:       }
1.945     raeburn  4213:       my @currhidden = keys(%lasthidden);
1.596     albertel 4214:       $prevattempts.=&start_data_table_row().'<td>'.&mt('Current').'</td>';
1.356     albertel 4215:       foreach my $key (sort(keys(%lasthash))) {
1.989     raeburn  4216:           next if ($key =~ /\.foilorder$/);
1.945     raeburn  4217:           if (%typeparts) {
                   4218:               my $hidden;
                   4219:               foreach my $id (@currhidden) {
                   4220:                   if ($key =~ /^\Q$id\E/) {
                   4221:                       $hidden = 1;
                   4222:                       last;
                   4223:                   }
                   4224:               }
                   4225:               if ($hidden) {
                   4226:                   my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
                   4227:                   if (($data eq 'award') || ($data eq 'awarddetail')) {
                   4228:                       my $value = &format_previous_attempt_value($key,$lasthash{$key});
                   4229:                       if ($key =~/$regexp$/ && (defined &$gradesub)) {
                   4230:                           $value = &$gradesub($value);
                   4231:                       }
1.1173    kruse    4232:                       $prevattempts.='<td>'. $value.'&nbsp;</td>';
1.945     raeburn  4233:                   } else {
                   4234:                       $prevattempts.='<td>&nbsp;</td>';
                   4235:                   }
                   4236:               } else {
                   4237:                   my $value = &format_previous_attempt_value($key,$lasthash{$key});
                   4238:                   if ($key =~/$regexp$/ && (defined &$gradesub)) {
                   4239:                       $value = &$gradesub($value);
                   4240:                   }
1.1173    kruse    4241:                   $prevattempts.='<td>'.$value.'&nbsp;</td>';
1.945     raeburn  4242:               }
                   4243:           } else {
                   4244: 	      my $value = &format_previous_attempt_value($key,$lasthash{$key});
                   4245: 	      if ($key =~/$regexp$/ && (defined &$gradesub)) {
                   4246:                   $value = &$gradesub($value);
                   4247:               }
1.1173    kruse    4248: 	     $prevattempts.='<td>'.$value.'&nbsp;</td>';
1.945     raeburn  4249:           }
1.16      harris41 4250:       }
1.596     albertel 4251:       $prevattempts.= &end_data_table_row().&end_data_table();
1.1       albertel 4252:     } else {
1.596     albertel 4253:       $prevattempts=
                   4254: 	  &start_data_table().&start_data_table_row().
                   4255: 	  '<td>'.&mt('Nothing submitted - no attempts.').'</td>'.
                   4256: 	  &end_data_table_row().&end_data_table();
1.1       albertel 4257:     }
                   4258:   } else {
1.596     albertel 4259:     $prevattempts=
                   4260: 	  &start_data_table().&start_data_table_row().
                   4261: 	  '<td>'.&mt('No data.').'</td>'.
                   4262: 	  &end_data_table_row().&end_data_table();
1.1       albertel 4263:   }
1.10      albertel 4264: }
                   4265: 
1.581     albertel 4266: sub format_previous_attempt_value {
                   4267:     my ($key,$value) = @_;
1.1011    www      4268:     if (($key =~ /timestamp/) || ($key=~/duedate/)) {
1.1173    kruse    4269:         $value = &Apache::lonlocal::locallocaltime($value);
1.581     albertel 4270:     } elsif (ref($value) eq 'ARRAY') {
1.1173    kruse    4271:         $value = &HTML::Entities::encode('('.join(', ', @{ $value }).')','"<>&');
1.988     raeburn  4272:     } elsif ($key =~ /answerstring$/) {
                   4273:         my %answers = &Apache::lonnet::str2hash($value);
1.1173    kruse    4274:         my @answer = %answers;
                   4275:         %answers = map {&HTML::Entities::encode($_, '"<>&')} @answer;
1.988     raeburn  4276:         my @anskeys = sort(keys(%answers));
                   4277:         if (@anskeys == 1) {
                   4278:             my $answer = $answers{$anskeys[0]};
1.1001    raeburn  4279:             if ($answer =~ m{\0}) {
                   4280:                 $answer =~ s{\0}{,}g;
1.988     raeburn  4281:             }
                   4282:             my $tag_internal_answer_name = 'INTERNAL';
                   4283:             if ($anskeys[0] eq $tag_internal_answer_name) {
                   4284:                 $value = $answer; 
                   4285:             } else {
                   4286:                 $value = $anskeys[0].'='.$answer;
                   4287:             }
                   4288:         } else {
                   4289:             foreach my $ans (@anskeys) {
                   4290:                 my $answer = $answers{$ans};
1.1001    raeburn  4291:                 if ($answer =~ m{\0}) {
                   4292:                     $answer =~ s{\0}{,}g;
1.988     raeburn  4293:                 }
                   4294:                 $value .=  $ans.'='.$answer.'<br />';;
                   4295:             } 
                   4296:         }
1.581     albertel 4297:     } else {
1.1173    kruse    4298:         $value = &HTML::Entities::encode(&unescape($value), '"<>&');
1.581     albertel 4299:     }
                   4300:     return $value;
                   4301: }
                   4302: 
                   4303: 
1.107     albertel 4304: sub relative_to_absolute {
                   4305:     my ($url,$output)=@_;
                   4306:     my $parser=HTML::TokeParser->new(\$output);
                   4307:     my $token;
                   4308:     my $thisdir=$url;
                   4309:     my @rlinks=();
                   4310:     while ($token=$parser->get_token) {
                   4311: 	if ($token->[0] eq 'S') {
                   4312: 	    if ($token->[1] eq 'a') {
                   4313: 		if ($token->[2]->{'href'}) {
                   4314: 		    $rlinks[$#rlinks+1]=$token->[2]->{'href'};
                   4315: 		}
                   4316: 	    } elsif ($token->[1] eq 'img' || $token->[1] eq 'embed' ) {
                   4317: 		$rlinks[$#rlinks+1]=$token->[2]->{'src'};
                   4318: 	    } elsif ($token->[1] eq 'base') {
                   4319: 		$thisdir=$token->[2]->{'href'};
                   4320: 	    }
                   4321: 	}
                   4322:     }
                   4323:     $thisdir=~s-/[^/]*$--;
1.356     albertel 4324:     foreach my $link (@rlinks) {
1.726     raeburn  4325: 	unless (($link=~/^https?\:\/\//i) ||
1.356     albertel 4326: 		($link=~/^\//) ||
                   4327: 		($link=~/^javascript:/i) ||
                   4328: 		($link=~/^mailto:/i) ||
                   4329: 		($link=~/^\#/)) {
                   4330: 	    my $newlocation=&Apache::lonnet::hreflocation($thisdir,$link);
                   4331: 	    $output=~s/(\"|\'|\=\s*)\Q$link\E(\"|\'|\s|\>)/$1$newlocation$2/;
1.107     albertel 4332: 	}
                   4333:     }
                   4334: # -------------------------------------------------- Deal with Applet codebases
                   4335:     $output=~s/(\<applet[^\>]+)(codebase\=[^\S\>]+)*([^\>]*)\>/$1.($2?$2:' codebase="'.$thisdir.'"').$3.'>'/gei;
                   4336:     return $output;
                   4337: }
                   4338: 
1.112     bowersj2 4339: =pod
                   4340: 
1.648     raeburn  4341: =item * &get_student_view()
1.112     bowersj2 4342: 
                   4343: show a snapshot of what student was looking at
                   4344: 
                   4345: =cut
                   4346: 
1.10      albertel 4347: sub get_student_view {
1.186     albertel 4348:   my ($symb,$username,$domain,$courseid,$target,$moreenv) = @_;
1.114     www      4349:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 4350:   my (%form);
1.10      albertel 4351:   my @elements=('symb','courseid','domain','username');
                   4352:   foreach my $element (@elements) {
1.186     albertel 4353:       $form{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 4354:   }
1.186     albertel 4355:   if (defined($moreenv)) {
                   4356:       %form=(%form,%{$moreenv});
                   4357:   }
1.236     albertel 4358:   if (defined($target)) { $form{'grade_target'} = $target; }
1.107     albertel 4359:   $feedurl=&Apache::lonnet::clutter($feedurl);
1.650     www      4360:   my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form);
1.11      albertel 4361:   $userview=~s/\<body[^\>]*\>//gi;
                   4362:   $userview=~s/\<\/body\>//gi;
                   4363:   $userview=~s/\<html\>//gi;
                   4364:   $userview=~s/\<\/html\>//gi;
                   4365:   $userview=~s/\<head\>//gi;
                   4366:   $userview=~s/\<\/head\>//gi;
                   4367:   $userview=~s/action\s*\=/would_be_action\=/gi;
1.107     albertel 4368:   $userview=&relative_to_absolute($feedurl,$userview);
1.650     www      4369:   if (wantarray) {
                   4370:      return ($userview,$response);
                   4371:   } else {
                   4372:      return $userview;
                   4373:   }
                   4374: }
                   4375: 
                   4376: sub get_student_view_with_retries {
                   4377:   my ($symb,$retries,$username,$domain,$courseid,$target,$moreenv) = @_;
                   4378: 
                   4379:     my $ok = 0;                 # True if we got a good response.
                   4380:     my $content;
                   4381:     my $response;
                   4382: 
                   4383:     # Try to get the student_view done. within the retries count:
                   4384:     
                   4385:     do {
                   4386:          ($content, $response) = &get_student_view($symb,$username,$domain,$courseid,$target,$moreenv);
                   4387:          $ok      = $response->is_success;
                   4388:          if (!$ok) {
                   4389:             &Apache::lonnet::logthis("Failed get_student_view_with_retries on $symb: ".$response->is_success.', '.$response->code.', '.$response->message);
                   4390:          }
                   4391:          $retries--;
                   4392:     } while (!$ok && ($retries > 0));
                   4393:     
                   4394:     if (!$ok) {
                   4395:        $content = '';          # On error return an empty content.
                   4396:     }
1.651     www      4397:     if (wantarray) {
                   4398:        return ($content, $response);
                   4399:     } else {
                   4400:        return $content;
                   4401:     }
1.11      albertel 4402: }
                   4403: 
1.112     bowersj2 4404: =pod
                   4405: 
1.648     raeburn  4406: =item * &get_student_answers() 
1.112     bowersj2 4407: 
                   4408: show a snapshot of how student was answering problem
                   4409: 
                   4410: =cut
                   4411: 
1.11      albertel 4412: sub get_student_answers {
1.100     sakharuk 4413:   my ($symb,$username,$domain,$courseid,%form) = @_;
1.114     www      4414:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 4415:   my (%moreenv);
1.11      albertel 4416:   my @elements=('symb','courseid','domain','username');
                   4417:   foreach my $element (@elements) {
1.186     albertel 4418:     $moreenv{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 4419:   }
1.186     albertel 4420:   $moreenv{'grade_target'}='answer';
                   4421:   %moreenv=(%form,%moreenv);
1.497     raeburn  4422:   $feedurl = &Apache::lonnet::clutter($feedurl);
                   4423:   my $userview=&Apache::lonnet::ssi($feedurl,%moreenv);
1.10      albertel 4424:   return $userview;
1.1       albertel 4425: }
1.116     albertel 4426: 
                   4427: =pod
                   4428: 
                   4429: =item * &submlink()
                   4430: 
1.242     albertel 4431: Inputs: $text $uname $udom $symb $target
1.116     albertel 4432: 
                   4433: Returns: A link to grades.pm such as to see the SUBM view of a student
                   4434: 
                   4435: =cut
                   4436: 
                   4437: ###############################################
                   4438: sub submlink {
1.242     albertel 4439:     my ($text,$uname,$udom,$symb,$target)=@_;
1.116     albertel 4440:     if (!($uname && $udom)) {
                   4441: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 4442: 	    &Apache::lonnet::whichuser($symb);
1.116     albertel 4443: 	if (!$symb) { $symb=$cursymb; }
                   4444:     }
1.254     matthew  4445:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      4446:     $symb=&escape($symb);
1.960     bisitz   4447:     if ($target) { $target=" target=\"$target\""; }
                   4448:     return
                   4449:         '<a href="/adm/grades?command=submission'.
                   4450:         '&amp;symb='.$symb.
                   4451:         '&amp;student='.$uname.
                   4452:         '&amp;userdom='.$udom.'"'.
                   4453:         $target.'>'.$text.'</a>';
1.242     albertel 4454: }
                   4455: ##############################################
                   4456: 
                   4457: =pod
                   4458: 
                   4459: =item * &pgrdlink()
                   4460: 
                   4461: Inputs: $text $uname $udom $symb $target
                   4462: 
                   4463: Returns: A link to grades.pm such as to see the PGRD view of a student
                   4464: 
                   4465: =cut
                   4466: 
                   4467: ###############################################
                   4468: sub pgrdlink {
                   4469:     my $link=&submlink(@_);
                   4470:     $link=~s/(&command=submission)/$1&showgrading=yes/;
                   4471:     return $link;
                   4472: }
                   4473: ##############################################
                   4474: 
                   4475: =pod
                   4476: 
                   4477: =item * &pprmlink()
                   4478: 
                   4479: Inputs: $text $uname $udom $symb $target
                   4480: 
                   4481: Returns: A link to parmset.pm such as to see the PPRM view of a
1.283     albertel 4482: student and a specific resource
1.242     albertel 4483: 
                   4484: =cut
                   4485: 
                   4486: ###############################################
                   4487: sub pprmlink {
                   4488:     my ($text,$uname,$udom,$symb,$target)=@_;
                   4489:     if (!($uname && $udom)) {
                   4490: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 4491: 	    &Apache::lonnet::whichuser($symb);
1.242     albertel 4492: 	if (!$symb) { $symb=$cursymb; }
                   4493:     }
1.254     matthew  4494:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      4495:     $symb=&escape($symb);
1.242     albertel 4496:     if ($target) { $target="target=\"$target\""; }
1.595     albertel 4497:     return '<a href="/adm/parmset?command=set&amp;'.
                   4498: 	'symb='.$symb.'&amp;uname='.$uname.
                   4499: 	'&amp;udom='.$udom.'" '.$target.'>'.$text.'</a>';
1.116     albertel 4500: }
                   4501: ##############################################
1.37      matthew  4502: 
1.112     bowersj2 4503: =pod
                   4504: 
                   4505: =back
                   4506: 
                   4507: =cut
                   4508: 
1.37      matthew  4509: ###############################################
1.51      www      4510: 
                   4511: 
                   4512: sub timehash {
1.687     raeburn  4513:     my ($thistime) = @_;
                   4514:     my $timezone = &Apache::lonlocal::gettimezone();
                   4515:     my $dt = DateTime->from_epoch(epoch => $thistime)
                   4516:                      ->set_time_zone($timezone);
                   4517:     my $wday = $dt->day_of_week();
                   4518:     if ($wday == 7) { $wday = 0; }
                   4519:     return ( 'second' => $dt->second(),
                   4520:              'minute' => $dt->minute(),
                   4521:              'hour'   => $dt->hour(),
                   4522:              'day'     => $dt->day_of_month(),
                   4523:              'month'   => $dt->month(),
                   4524:              'year'    => $dt->year(),
                   4525:              'weekday' => $wday,
                   4526:              'dayyear' => $dt->day_of_year(),
                   4527:              'dlsav'   => $dt->is_dst() );
1.51      www      4528: }
                   4529: 
1.370     www      4530: sub utc_string {
                   4531:     my ($date)=@_;
1.371     www      4532:     return strftime("%Y%m%dT%H%M%SZ",gmtime($date));
1.370     www      4533: }
                   4534: 
1.51      www      4535: sub maketime {
                   4536:     my %th=@_;
1.687     raeburn  4537:     my ($epoch_time,$timezone,$dt);
                   4538:     $timezone = &Apache::lonlocal::gettimezone();
                   4539:     eval {
                   4540:         $dt = DateTime->new( year   => $th{'year'},
                   4541:                              month  => $th{'month'},
                   4542:                              day    => $th{'day'},
                   4543:                              hour   => $th{'hour'},
                   4544:                              minute => $th{'minute'},
                   4545:                              second => $th{'second'},
                   4546:                              time_zone => $timezone,
                   4547:                          );
                   4548:     };
                   4549:     if (!$@) {
                   4550:         $epoch_time = $dt->epoch;
                   4551:         if ($epoch_time) {
                   4552:             return $epoch_time;
                   4553:         }
                   4554:     }
1.51      www      4555:     return POSIX::mktime(
                   4556:         ($th{'seconds'},$th{'minutes'},$th{'hours'},
1.210     www      4557:          $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1));
1.70      www      4558: }
                   4559: 
                   4560: #########################################
1.51      www      4561: 
                   4562: sub findallcourses {
1.482     raeburn  4563:     my ($roles,$uname,$udom) = @_;
1.355     albertel 4564:     my %roles;
                   4565:     if (ref($roles)) { %roles = map { $_ => 1 } @{$roles}; }
1.348     albertel 4566:     my %courses;
1.51      www      4567:     my $now=time;
1.482     raeburn  4568:     if (!defined($uname)) {
                   4569:         $uname = $env{'user.name'};
                   4570:     }
                   4571:     if (!defined($udom)) {
                   4572:         $udom = $env{'user.domain'};
                   4573:     }
                   4574:     if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
1.1073    raeburn  4575:         my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname);
1.482     raeburn  4576:         if (!%roles) {
                   4577:             %roles = (
                   4578:                        cc => 1,
1.907     raeburn  4579:                        co => 1,
1.482     raeburn  4580:                        in => 1,
                   4581:                        ep => 1,
                   4582:                        ta => 1,
                   4583:                        cr => 1,
                   4584:                        st => 1,
                   4585:              );
                   4586:         }
                   4587:         foreach my $entry (keys(%roleshash)) {
                   4588:             my ($trole,$tend,$tstart) = split(/_/,$roleshash{$entry});
                   4589:             if ($trole =~ /^cr/) { 
                   4590:                 next if (!exists($roles{$trole}) && !exists($roles{'cr'}));
                   4591:             } else {
                   4592:                 next if (!exists($roles{$trole}));
                   4593:             }
                   4594:             if ($tend) {
                   4595:                 next if ($tend < $now);
                   4596:             }
                   4597:             if ($tstart) {
                   4598:                 next if ($tstart > $now);
                   4599:             }
1.1058    raeburn  4600:             my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role);
1.482     raeburn  4601:             (undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
1.1058    raeburn  4602:             my $value = $trole.'/'.$cdom.'/';
1.482     raeburn  4603:             if ($secpart eq '') {
                   4604:                 ($cnum,$role) = split(/_/,$cnumpart); 
                   4605:                 $sec = 'none';
1.1058    raeburn  4606:                 $value .= $cnum.'/';
1.482     raeburn  4607:             } else {
                   4608:                 $cnum = $cnumpart;
                   4609:                 ($sec,$role) = split(/_/,$secpart);
1.1058    raeburn  4610:                 $value .= $cnum.'/'.$sec;
                   4611:             }
                   4612:             if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
                   4613:                 unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
                   4614:                     push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
                   4615:                 }
                   4616:             } else {
                   4617:                 @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
1.490     raeburn  4618:             }
1.482     raeburn  4619:         }
                   4620:     } else {
                   4621:         foreach my $key (keys(%env)) {
1.483     albertel 4622: 	    if ( $key=~m{^user\.role\.(\w+)\./($match_domain)/($match_courseid)/?(\w*)$} ||
                   4623:                  $key=~m{^user\.role\.(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_courseid)/?(\w*)$}) {
1.482     raeburn  4624: 	        my ($role,$cdom,$cnum,$sec) = ($1,$2,$3,$4);
                   4625: 	        next if ($role eq 'ca' || $role eq 'aa');
                   4626: 	        next if (%roles && !exists($roles{$role}));
                   4627: 	        my ($starttime,$endtime)=split(/\./,$env{$key});
                   4628:                 my $active=1;
                   4629:                 if ($starttime) {
                   4630: 		    if ($now<$starttime) { $active=0; }
                   4631:                 }
                   4632:                 if ($endtime) {
                   4633:                     if ($now>$endtime) { $active=0; }
                   4634:                 }
                   4635:                 if ($active) {
1.1058    raeburn  4636:                     my $value = $role.'/'.$cdom.'/'.$cnum.'/';
1.482     raeburn  4637:                     if ($sec eq '') {
                   4638:                         $sec = 'none';
1.1058    raeburn  4639:                     } else {
                   4640:                         $value .= $sec;
                   4641:                     }
                   4642:                     if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
                   4643:                         unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
                   4644:                             push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
                   4645:                         }
                   4646:                     } else {
                   4647:                         @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
1.482     raeburn  4648:                     }
1.474     raeburn  4649:                 }
                   4650:             }
1.51      www      4651:         }
                   4652:     }
1.474     raeburn  4653:     return %courses;
1.51      www      4654: }
1.37      matthew  4655: 
1.54      www      4656: ###############################################
1.474     raeburn  4657: 
                   4658: sub blockcheck {
1.1189    raeburn  4659:     my ($setters,$activity,$uname,$udom,$url,$is_course) = @_;
1.490     raeburn  4660: 
1.1189    raeburn  4661:     if (defined($udom) && defined($uname)) {
                   4662:         # If uname and udom are for a course, check for blocks in the course.
                   4663:         if (($is_course) || (&Apache::lonnet::is_course($udom,$uname))) {
                   4664:             my ($startblock,$endblock,$triggerblock) =
                   4665:                 &get_blocks($setters,$activity,$udom,$uname,$url);
                   4666:             return ($startblock,$endblock,$triggerblock);
                   4667:         }
                   4668:     } else {
1.490     raeburn  4669:         $udom = $env{'user.domain'};
                   4670:         $uname = $env{'user.name'};
                   4671:     }
                   4672: 
1.502     raeburn  4673:     my $startblock = 0;
                   4674:     my $endblock = 0;
1.1062    raeburn  4675:     my $triggerblock = '';
1.482     raeburn  4676:     my %live_courses = &findallcourses(undef,$uname,$udom);
1.474     raeburn  4677: 
1.490     raeburn  4678:     # If uname is for a user, and activity is course-specific, i.e.,
                   4679:     # boards, chat or groups, check for blocking in current course only.
1.474     raeburn  4680: 
1.490     raeburn  4681:     if (($activity eq 'boards' || $activity eq 'chat' ||
1.1189    raeburn  4682:          $activity eq 'groups' || $activity eq 'printout') &&
                   4683:         ($env{'request.course.id'})) {
1.490     raeburn  4684:         foreach my $key (keys(%live_courses)) {
                   4685:             if ($key ne $env{'request.course.id'}) {
                   4686:                 delete($live_courses{$key});
                   4687:             }
                   4688:         }
                   4689:     }
                   4690: 
                   4691:     my $otheruser = 0;
                   4692:     my %own_courses;
                   4693:     if ((($uname ne $env{'user.name'})) || ($udom ne $env{'user.domain'})) {
                   4694:         # Resource belongs to user other than current user.
                   4695:         $otheruser = 1;
                   4696:         # Gather courses for current user
                   4697:         %own_courses = 
                   4698:             &findallcourses(undef,$env{'user.name'},$env{'user.domain'});
                   4699:     }
                   4700: 
                   4701:     # Gather active course roles - course coordinator, instructor, 
                   4702:     # exam proctor, ta, student, or custom role.
1.474     raeburn  4703: 
                   4704:     foreach my $course (keys(%live_courses)) {
1.482     raeburn  4705:         my ($cdom,$cnum);
                   4706:         if ((defined($env{'course.'.$course.'.domain'})) && (defined($env{'course.'.$course.'.num'}))) {
                   4707:             $cdom = $env{'course.'.$course.'.domain'};
                   4708:             $cnum = $env{'course.'.$course.'.num'};
                   4709:         } else {
1.490     raeburn  4710:             ($cdom,$cnum) = split(/_/,$course); 
1.482     raeburn  4711:         }
                   4712:         my $no_ownblock = 0;
                   4713:         my $no_userblock = 0;
1.533     raeburn  4714:         if ($otheruser && $activity ne 'com') {
1.490     raeburn  4715:             # Check if current user has 'evb' priv for this
                   4716:             if (defined($own_courses{$course})) {
                   4717:                 foreach my $sec (keys(%{$own_courses{$course}})) {
                   4718:                     my $checkrole = 'cm./'.$cdom.'/'.$cnum;
                   4719:                     if ($sec ne 'none') {
                   4720:                         $checkrole .= '/'.$sec;
                   4721:                     }
                   4722:                     if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   4723:                         $no_ownblock = 1;
                   4724:                         last;
                   4725:                     }
                   4726:                 }
                   4727:             }
                   4728:             # if they have 'evb' priv and are currently not playing student
                   4729:             next if (($no_ownblock) &&
                   4730:                  ($env{'request.role'} !~ m{^st\./$cdom/$cnum}));
                   4731:         }
1.474     raeburn  4732:         foreach my $sec (keys(%{$live_courses{$course}})) {
1.482     raeburn  4733:             my $checkrole = 'cm./'.$cdom.'/'.$cnum;
1.474     raeburn  4734:             if ($sec ne 'none') {
1.482     raeburn  4735:                 $checkrole .= '/'.$sec;
1.474     raeburn  4736:             }
1.490     raeburn  4737:             if ($otheruser) {
                   4738:                 # Resource belongs to user other than current user.
                   4739:                 # Assemble privs for that user, and check for 'evb' priv.
1.1058    raeburn  4740:                 my (%allroles,%userroles);
                   4741:                 if (ref($live_courses{$course}{$sec}) eq 'ARRAY') {
                   4742:                     foreach my $entry (@{$live_courses{$course}{$sec}}) { 
                   4743:                         my ($trole,$tdom,$tnum,$tsec);
                   4744:                         if ($entry =~ /^cr/) {
                   4745:                             ($trole,$tdom,$tnum,$tsec) = 
                   4746:                                 ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
                   4747:                         } else {
                   4748:                            ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
                   4749:                         }
                   4750:                         my ($spec,$area,$trest);
                   4751:                         $area = '/'.$tdom.'/'.$tnum;
                   4752:                         $trest = $tnum;
                   4753:                         if ($tsec ne '') {
                   4754:                             $area .= '/'.$tsec;
                   4755:                             $trest .= '/'.$tsec;
                   4756:                         }
                   4757:                         $spec = $trole.'.'.$area;
                   4758:                         if ($trole =~ /^cr/) {
                   4759:                             &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
                   4760:                                                               $tdom,$spec,$trest,$area);
                   4761:                         } else {
                   4762:                             &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
                   4763:                                                                 $tdom,$spec,$trest,$area);
                   4764:                         }
                   4765:                     }
                   4766:                     my ($author,$adv) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
                   4767:                     if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
                   4768:                         if ($1) {
                   4769:                             $no_userblock = 1;
                   4770:                             last;
                   4771:                         }
1.486     raeburn  4772:                     }
                   4773:                 }
1.490     raeburn  4774:             } else {
                   4775:                 # Resource belongs to current user
                   4776:                 # Check for 'evb' priv via lonnet::allowed().
1.482     raeburn  4777:                 if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   4778:                     $no_ownblock = 1;
                   4779:                     last;
                   4780:                 }
1.474     raeburn  4781:             }
                   4782:         }
                   4783:         # if they have the evb priv and are currently not playing student
1.482     raeburn  4784:         next if (($no_ownblock) &&
1.491     albertel 4785:                  ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E}));
1.482     raeburn  4786:         next if ($no_userblock);
1.474     raeburn  4787: 
1.866     kalberla 4788:         # Retrieve blocking times and identity of locker for course
1.490     raeburn  4789:         # of specified user, unless user has 'evb' privilege.
1.502     raeburn  4790:         
1.1062    raeburn  4791:         my ($start,$end,$trigger) = 
                   4792:             &get_blocks($setters,$activity,$cdom,$cnum,$url);
1.502     raeburn  4793:         if (($start != 0) && 
                   4794:             (($startblock == 0) || ($startblock > $start))) {
                   4795:             $startblock = $start;
1.1062    raeburn  4796:             if ($trigger ne '') {
                   4797:                 $triggerblock = $trigger;
                   4798:             }
1.502     raeburn  4799:         }
                   4800:         if (($end != 0)  &&
                   4801:             (($endblock == 0) || ($endblock < $end))) {
                   4802:             $endblock = $end;
1.1062    raeburn  4803:             if ($trigger ne '') {
                   4804:                 $triggerblock = $trigger;
                   4805:             }
1.502     raeburn  4806:         }
1.490     raeburn  4807:     }
1.1062    raeburn  4808:     return ($startblock,$endblock,$triggerblock);
1.490     raeburn  4809: }
                   4810: 
                   4811: sub get_blocks {
1.1062    raeburn  4812:     my ($setters,$activity,$cdom,$cnum,$url) = @_;
1.490     raeburn  4813:     my $startblock = 0;
                   4814:     my $endblock = 0;
1.1062    raeburn  4815:     my $triggerblock = '';
1.490     raeburn  4816:     my $course = $cdom.'_'.$cnum;
                   4817:     $setters->{$course} = {};
                   4818:     $setters->{$course}{'staff'} = [];
                   4819:     $setters->{$course}{'times'} = [];
1.1062    raeburn  4820:     $setters->{$course}{'triggers'} = [];
                   4821:     my (@blockers,%triggered);
                   4822:     my $now = time;
                   4823:     my %commblocks = &Apache::lonnet::get_comm_blocks($cdom,$cnum);
                   4824:     if ($activity eq 'docs') {
                   4825:         @blockers = &Apache::lonnet::has_comm_blocking('bre',undef,$url,\%commblocks);
                   4826:         foreach my $block (@blockers) {
                   4827:             if ($block =~ /^firstaccess____(.+)$/) {
                   4828:                 my $item = $1;
                   4829:                 my $type = 'map';
                   4830:                 my $timersymb = $item;
                   4831:                 if ($item eq 'course') {
                   4832:                     $type = 'course';
                   4833:                 } elsif ($item =~ /___\d+___/) {
                   4834:                     $type = 'resource';
                   4835:                 } else {
                   4836:                     $timersymb = &Apache::lonnet::symbread($item);
                   4837:                 }
                   4838:                 my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
                   4839:                 my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb};
                   4840:                 $triggered{$block} = {
                   4841:                                        start => $start,
                   4842:                                        end   => $end,
                   4843:                                        type  => $type,
                   4844:                                      };
                   4845:             }
                   4846:         }
                   4847:     } else {
                   4848:         foreach my $block (keys(%commblocks)) {
                   4849:             if ($block =~ m/^(\d+)____(\d+)$/) { 
                   4850:                 my ($start,$end) = ($1,$2);
                   4851:                 if ($start <= time && $end >= time) {
                   4852:                     if (ref($commblocks{$block}) eq 'HASH') {
                   4853:                         if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
                   4854:                             if ($commblocks{$block}{'blocks'}{$activity} eq 'on') {
                   4855:                                 unless(grep(/^\Q$block\E$/,@blockers)) {
                   4856:                                     push(@blockers,$block);
                   4857:                                 }
                   4858:                             }
                   4859:                         }
                   4860:                     }
                   4861:                 }
                   4862:             } elsif ($block =~ /^firstaccess____(.+)$/) {
                   4863:                 my $item = $1;
                   4864:                 my $timersymb = $item; 
                   4865:                 my $type = 'map';
                   4866:                 if ($item eq 'course') {
                   4867:                     $type = 'course';
                   4868:                 } elsif ($item =~ /___\d+___/) {
                   4869:                     $type = 'resource';
                   4870:                 } else {
                   4871:                     $timersymb = &Apache::lonnet::symbread($item);
                   4872:                 }
                   4873:                 my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
                   4874:                 my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb}; 
                   4875:                 if ($start && $end) {
                   4876:                     if (($start <= time) && ($end >= time)) {
                   4877:                         unless (grep(/^\Q$block\E$/,@blockers)) {
                   4878:                             push(@blockers,$block);
                   4879:                             $triggered{$block} = {
                   4880:                                                    start => $start,
                   4881:                                                    end   => $end,
                   4882:                                                    type  => $type,
                   4883:                                                  };
                   4884:                         }
                   4885:                     }
1.490     raeburn  4886:                 }
1.1062    raeburn  4887:             }
                   4888:         }
                   4889:     }
                   4890:     foreach my $blocker (@blockers) {
                   4891:         my ($staff_name,$staff_dom,$title,$blocks) =
                   4892:             &parse_block_record($commblocks{$blocker});
                   4893:         push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
                   4894:         my ($start,$end,$triggertype);
                   4895:         if ($blocker =~ m/^(\d+)____(\d+)$/) {
                   4896:             ($start,$end) = ($1,$2);
                   4897:         } elsif (ref($triggered{$blocker}) eq 'HASH') {
                   4898:             $start = $triggered{$blocker}{'start'};
                   4899:             $end = $triggered{$blocker}{'end'};
                   4900:             $triggertype = $triggered{$blocker}{'type'};
                   4901:         }
                   4902:         if ($start) {
                   4903:             push(@{$$setters{$course}{'times'}}, [$start,$end]);
                   4904:             if ($triggertype) {
                   4905:                 push(@{$$setters{$course}{'triggers'}},$triggertype);
                   4906:             } else {
                   4907:                 push(@{$$setters{$course}{'triggers'}},0);
                   4908:             }
                   4909:             if ( ($startblock == 0) || ($startblock > $start) ) {
                   4910:                 $startblock = $start;
                   4911:                 if ($triggertype) {
                   4912:                     $triggerblock = $blocker;
1.474     raeburn  4913:                 }
                   4914:             }
1.1062    raeburn  4915:             if ( ($endblock == 0) || ($endblock < $end) ) {
                   4916:                $endblock = $end;
                   4917:                if ($triggertype) {
                   4918:                    $triggerblock = $blocker;
                   4919:                }
                   4920:             }
1.474     raeburn  4921:         }
                   4922:     }
1.1062    raeburn  4923:     return ($startblock,$endblock,$triggerblock);
1.474     raeburn  4924: }
                   4925: 
                   4926: sub parse_block_record {
                   4927:     my ($record) = @_;
                   4928:     my ($setuname,$setudom,$title,$blocks);
                   4929:     if (ref($record) eq 'HASH') {
                   4930:         ($setuname,$setudom) = split(/:/,$record->{'setter'});
                   4931:         $title = &unescape($record->{'event'});
                   4932:         $blocks = $record->{'blocks'};
                   4933:     } else {
                   4934:         my @data = split(/:/,$record,3);
                   4935:         if (scalar(@data) eq 2) {
                   4936:             $title = $data[1];
                   4937:             ($setuname,$setudom) = split(/@/,$data[0]);
                   4938:         } else {
                   4939:             ($setuname,$setudom,$title) = @data;
                   4940:         }
                   4941:         $blocks = { 'com' => 'on' };
                   4942:     }
                   4943:     return ($setuname,$setudom,$title,$blocks);
                   4944: }
                   4945: 
1.854     kalberla 4946: sub blocking_status {
1.1189    raeburn  4947:     my ($activity,$uname,$udom,$url,$is_course) = @_;
1.1061    raeburn  4948:     my %setters;
1.890     droeschl 4949: 
1.1061    raeburn  4950: # check for active blocking
1.1062    raeburn  4951:     my ($startblock,$endblock,$triggerblock) = 
1.1189    raeburn  4952:         &blockcheck(\%setters,$activity,$uname,$udom,$url,$is_course);
1.1062    raeburn  4953:     my $blocked = 0;
                   4954:     if ($startblock && $endblock) {
                   4955:         $blocked = 1;
                   4956:     }
1.890     droeschl 4957: 
1.1061    raeburn  4958: # caller just wants to know whether a block is active
                   4959:     if (!wantarray) { return $blocked; }
                   4960: 
                   4961: # build a link to a popup window containing the details
                   4962:     my $querystring  = "?activity=$activity";
                   4963: # $uname and $udom decide whose portfolio the user is trying to look at
1.1232    raeburn  4964:     if (($activity eq 'port') || ($activity eq 'passwd')) {
                   4965:         $querystring .= "&amp;udom=$udom"      if ($udom =~ /^$match_domain$/); 
                   4966:         $querystring .= "&amp;uname=$uname"    if ($uname =~ /^$match_username$/);
1.1062    raeburn  4967:     } elsif ($activity eq 'docs') {
                   4968:         $querystring .= '&amp;url='.&HTML::Entities::encode($url,'&"');
                   4969:     }
1.1061    raeburn  4970: 
                   4971:     my $output .= <<'END_MYBLOCK';
                   4972: function openWindow(url, wdwName, w, h, toolbar,scrollbar) {
                   4973:     var options = "width=" + w + ",height=" + h + ",";
                   4974:     options += "resizable=yes,scrollbars="+scrollbar+",status=no,";
                   4975:     options += "menubar=no,toolbar="+toolbar+",location=no,directories=no";
                   4976:     var newWin = window.open(url, wdwName, options);
                   4977:     newWin.focus();
                   4978: }
1.890     droeschl 4979: END_MYBLOCK
1.854     kalberla 4980: 
1.1061    raeburn  4981:     $output = Apache::lonhtmlcommon::scripttag($output);
1.890     droeschl 4982:   
1.1061    raeburn  4983:     my $popupUrl = "/adm/blockingstatus/$querystring";
1.1062    raeburn  4984:     my $text = &mt('Communication Blocked');
1.1217    raeburn  4985:     my $class = 'LC_comblock';
1.1062    raeburn  4986:     if ($activity eq 'docs') {
                   4987:         $text = &mt('Content Access Blocked');
1.1217    raeburn  4988:         $class = '';
1.1063    raeburn  4989:     } elsif ($activity eq 'printout') {
                   4990:         $text = &mt('Printing Blocked');
1.1232    raeburn  4991:     } elsif ($activity eq 'passwd') {
                   4992:         $text = &mt('Password Changing Blocked');
1.1062    raeburn  4993:     }
1.1061    raeburn  4994:     $output .= <<"END_BLOCK";
1.1217    raeburn  4995: <div class='$class'>
1.869     kalberla 4996:   <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring'
1.890     droeschl 4997:   title='$text'>
                   4998:   <img class='LC_noBorder LC_middle' title='$text' src='/res/adm/pages/comblock.png' alt='$text'/></a>
1.869     kalberla 4999:   <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring' 
1.890     droeschl 5000:   title='$text'>$text</a>
1.867     kalberla 5001: </div>
                   5002: 
                   5003: END_BLOCK
1.474     raeburn  5004: 
1.1061    raeburn  5005:     return ($blocked, $output);
1.854     kalberla 5006: }
1.490     raeburn  5007: 
1.60      matthew  5008: ###############################################
                   5009: 
1.682     raeburn  5010: sub check_ip_acc {
1.1201    raeburn  5011:     my ($acc,$clientip)=@_;
1.682     raeburn  5012:     &Apache::lonxml::debug("acc is $acc");
                   5013:     if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) {
                   5014:         return 1;
                   5015:     }
1.1219    raeburn  5016:     my $allowed;
1.1201    raeburn  5017:     my $ip=$env{'request.host'} || $ENV{'REMOTE_ADDR'} || $clientip;
1.682     raeburn  5018: 
                   5019:     my $name;
1.1219    raeburn  5020:     my %access = (
                   5021:                      allowfrom => 1,
                   5022:                      denyfrom  => 0,
                   5023:                  );
                   5024:     my @allows;
                   5025:     my @denies;
                   5026:     foreach my $item (split(',',$acc)) {
                   5027:         $item =~ s/^\s*//;
                   5028:         $item =~ s/\s*$//;
                   5029:         my $pattern;
                   5030:         if ($item =~ /^\!(.+)$/) {
                   5031:             push(@denies,$1);
                   5032:         } else {
                   5033:             push(@allows,$item);
                   5034:         }
                   5035:    }
                   5036:    my $numdenies = scalar(@denies);
                   5037:    my $numallows = scalar(@allows);
                   5038:    my $count = 0;
                   5039:    foreach my $pattern (@denies,@allows) {
                   5040:         $count ++; 
                   5041:         my $acctype = 'allowfrom';
                   5042:         if ($count <= $numdenies) {
                   5043:             $acctype = 'denyfrom';
                   5044:         }
1.682     raeburn  5045:         if ($pattern =~ /\*$/) {
                   5046:             #35.8.*
                   5047:             $pattern=~s/\*//;
1.1219    raeburn  5048:             if ($ip =~ /^\Q$pattern\E/) { $allowed=$access{$acctype}; }
1.682     raeburn  5049:         } elsif ($pattern =~ /(\d+\.\d+\.\d+)\.\[(\d+)-(\d+)\]$/) {
                   5050:             #35.8.3.[34-56]
                   5051:             my $low=$2;
                   5052:             my $high=$3;
                   5053:             $pattern=$1;
                   5054:             if ($ip =~ /^\Q$pattern\E/) {
                   5055:                 my $last=(split(/\./,$ip))[3];
1.1219    raeburn  5056:                 if ($last <=$high && $last >=$low) { $allowed=$access{$acctype}; }
1.682     raeburn  5057:             }
                   5058:         } elsif ($pattern =~ /^\*/) {
                   5059:             #*.msu.edu
                   5060:             $pattern=~s/\*//;
                   5061:             if (!defined($name)) {
                   5062:                 use Socket;
                   5063:                 my $netaddr=inet_aton($ip);
                   5064:                 ($name)=gethostbyaddr($netaddr,AF_INET);
                   5065:             }
1.1219    raeburn  5066:             if ($name =~ /\Q$pattern\E$/i) { $allowed=$access{$acctype}; }
1.682     raeburn  5067:         } elsif ($pattern =~ /\d+\.\d+\.\d+\.\d+/) {
                   5068:             #127.0.0.1
1.1219    raeburn  5069:             if ($ip =~ /^\Q$pattern\E/) { $allowed=$access{$acctype}; }
1.682     raeburn  5070:         } else {
                   5071:             #some.name.com
                   5072:             if (!defined($name)) {
                   5073:                 use Socket;
                   5074:                 my $netaddr=inet_aton($ip);
                   5075:                 ($name)=gethostbyaddr($netaddr,AF_INET);
                   5076:             }
1.1219    raeburn  5077:             if ($name =~ /\Q$pattern\E$/i) { $allowed=$access{$acctype}; }
                   5078:         }
                   5079:         if ($allowed =~ /^(0|1)$/) { last; }
                   5080:     }
                   5081:     if ($allowed eq '') {
                   5082:         if ($numdenies && !$numallows) {
                   5083:             $allowed = 1;
                   5084:         } else {
                   5085:             $allowed = 0;
1.682     raeburn  5086:         }
                   5087:     }
                   5088:     return $allowed;
                   5089: }
                   5090: 
                   5091: ###############################################
                   5092: 
1.60      matthew  5093: =pod
                   5094: 
1.112     bowersj2 5095: =head1 Domain Template Functions
                   5096: 
                   5097: =over 4
                   5098: 
                   5099: =item * &determinedomain()
1.60      matthew  5100: 
                   5101: Inputs: $domain (usually will be undef)
                   5102: 
1.63      www      5103: Returns: Determines which domain should be used for designs
1.60      matthew  5104: 
                   5105: =cut
1.54      www      5106: 
1.60      matthew  5107: ###############################################
1.63      www      5108: sub determinedomain {
                   5109:     my $domain=shift;
1.531     albertel 5110:     if (! $domain) {
1.60      matthew  5111:         # Determine domain if we have not been given one
1.893     raeburn  5112:         $domain = &Apache::lonnet::default_login_domain();
1.258     albertel 5113:         if ($env{'user.domain'}) { $domain=$env{'user.domain'}; }
                   5114:         if ($env{'request.role.domain'}) { 
                   5115:             $domain=$env{'request.role.domain'}; 
1.60      matthew  5116:         }
                   5117:     }
1.63      www      5118:     return $domain;
                   5119: }
                   5120: ###############################################
1.517     raeburn  5121: 
1.518     albertel 5122: sub devalidate_domconfig_cache {
                   5123:     my ($udom)=@_;
                   5124:     &Apache::lonnet::devalidate_cache_new('domainconfig',$udom);
                   5125: }
                   5126: 
                   5127: # ---------------------- Get domain configuration for a domain
                   5128: sub get_domainconf {
                   5129:     my ($udom) = @_;
                   5130:     my $cachetime=1800;
                   5131:     my ($result,$cached)=&Apache::lonnet::is_cached_new('domainconfig',$udom);
                   5132:     if (defined($cached)) { return %{$result}; }
                   5133: 
                   5134:     my %domconfig = &Apache::lonnet::get_dom('configuration',
1.948     raeburn  5135: 					     ['login','rolecolors','autoenroll'],$udom);
1.632     raeburn  5136:     my (%designhash,%legacy);
1.518     albertel 5137:     if (keys(%domconfig) > 0) {
                   5138:         if (ref($domconfig{'login'}) eq 'HASH') {
1.632     raeburn  5139:             if (keys(%{$domconfig{'login'}})) {
                   5140:                 foreach my $key (keys(%{$domconfig{'login'}})) {
1.699     raeburn  5141:                     if (ref($domconfig{'login'}{$key}) eq 'HASH') {
1.1208    raeburn  5142:                         if (($key eq 'loginvia') || ($key eq 'headtag')) {
                   5143:                             if (ref($domconfig{'login'}{$key}) eq 'HASH') {
                   5144:                                 foreach my $hostname (keys(%{$domconfig{'login'}{$key}})) {
                   5145:                                     if (ref($domconfig{'login'}{$key}{$hostname}) eq 'HASH') {
                   5146:                                         if ($key eq 'loginvia') {
                   5147:                                             if ($domconfig{'login'}{'loginvia'}{$hostname}{'server'}) {
                   5148:                                                 my $server = $domconfig{'login'}{'loginvia'}{$hostname}{'server'};
                   5149:                                                 $designhash{$udom.'.login.loginvia'} = $server;
                   5150:                                                 if ($domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'} eq 'custom') {
                   5151: 
                   5152:                                                     $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'custompath'};
                   5153:                                                 } else {
                   5154:                                                     $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'};
                   5155:                                                 }
1.948     raeburn  5156:                                             }
1.1208    raeburn  5157:                                         } elsif ($key eq 'headtag') {
                   5158:                                             if ($domconfig{'login'}{'headtag'}{$hostname}{'url'}) {
                   5159:                                                 $designhash{$udom.'.login.headtag_'.$hostname} = $domconfig{'login'}{'headtag'}{$hostname}{'url'};
1.948     raeburn  5160:                                             }
1.946     raeburn  5161:                                         }
1.1208    raeburn  5162:                                         if ($domconfig{'login'}{$key}{$hostname}{'exempt'}) {
                   5163:                                             $designhash{$udom.'.login.'.$key.'_exempt_'.$hostname} = $domconfig{'login'}{$key}{$hostname}{'exempt'};
                   5164:                                         }
1.946     raeburn  5165:                                     }
                   5166:                                 }
                   5167:                             }
                   5168:                         } else {
                   5169:                             foreach my $img (keys(%{$domconfig{'login'}{$key}})) {
                   5170:                                 $designhash{$udom.'.login.'.$key.'_'.$img} = 
                   5171:                                     $domconfig{'login'}{$key}{$img};
                   5172:                             }
1.699     raeburn  5173:                         }
                   5174:                     } else {
                   5175:                         $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
                   5176:                     }
1.632     raeburn  5177:                 }
                   5178:             } else {
                   5179:                 $legacy{'login'} = 1;
1.518     albertel 5180:             }
1.632     raeburn  5181:         } else {
                   5182:             $legacy{'login'} = 1;
1.518     albertel 5183:         }
                   5184:         if (ref($domconfig{'rolecolors'}) eq 'HASH') {
1.632     raeburn  5185:             if (keys(%{$domconfig{'rolecolors'}})) {
                   5186:                 foreach my $role (keys(%{$domconfig{'rolecolors'}})) {
                   5187:                     if (ref($domconfig{'rolecolors'}{$role}) eq 'HASH') {
                   5188:                         foreach my $item (keys(%{$domconfig{'rolecolors'}{$role}})) {
                   5189:                             $designhash{$udom.'.'.$role.'.'.$item}=$domconfig{'rolecolors'}{$role}{$item};
                   5190:                         }
1.518     albertel 5191:                     }
                   5192:                 }
1.632     raeburn  5193:             } else {
                   5194:                 $legacy{'rolecolors'} = 1;
1.518     albertel 5195:             }
1.632     raeburn  5196:         } else {
                   5197:             $legacy{'rolecolors'} = 1;
1.518     albertel 5198:         }
1.948     raeburn  5199:         if (ref($domconfig{'autoenroll'}) eq 'HASH') {
                   5200:             if ($domconfig{'autoenroll'}{'co-owners'}) {
                   5201:                 $designhash{$udom.'.autoassign.co-owners'}=$domconfig{'autoenroll'}{'co-owners'};
                   5202:             }
                   5203:         }
1.632     raeburn  5204:         if (keys(%legacy) > 0) {
                   5205:             my %legacyhash = &get_legacy_domconf($udom);
                   5206:             foreach my $item (keys(%legacyhash)) {
                   5207:                 if ($item =~ /^\Q$udom\E\.login/) {
                   5208:                     if ($legacy{'login'}) { 
                   5209:                         $designhash{$item} = $legacyhash{$item};
                   5210:                     }
                   5211:                 } else {
                   5212:                     if ($legacy{'rolecolors'}) {
                   5213:                         $designhash{$item} = $legacyhash{$item};
                   5214:                     }
1.518     albertel 5215:                 }
                   5216:             }
                   5217:         }
1.632     raeburn  5218:     } else {
                   5219:         %designhash = &get_legacy_domconf($udom); 
1.518     albertel 5220:     }
                   5221:     &Apache::lonnet::do_cache_new('domainconfig',$udom,\%designhash,
                   5222: 				  $cachetime);
                   5223:     return %designhash;
                   5224: }
                   5225: 
1.632     raeburn  5226: sub get_legacy_domconf {
                   5227:     my ($udom) = @_;
                   5228:     my %legacyhash;
                   5229:     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
                   5230:     my $designfile =  $designdir.'/'.$udom.'.tab';
                   5231:     if (-e $designfile) {
                   5232:         if ( open (my $fh,"<$designfile") ) {
                   5233:             while (my $line = <$fh>) {
                   5234:                 next if ($line =~ /^\#/);
                   5235:                 chomp($line);
                   5236:                 my ($key,$val)=(split(/\=/,$line));
                   5237:                 if ($val) { $legacyhash{$udom.'.'.$key}=$val; }
                   5238:             }
                   5239:             close($fh);
                   5240:         }
                   5241:     }
1.1026    raeburn  5242:     if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/adm/lonDomLogos/'.$udom.'.gif') {
1.632     raeburn  5243:         $legacyhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
                   5244:     }
                   5245:     return %legacyhash;
                   5246: }
                   5247: 
1.63      www      5248: =pod
                   5249: 
1.112     bowersj2 5250: =item * &domainlogo()
1.63      www      5251: 
                   5252: Inputs: $domain (usually will be undef)
                   5253: 
                   5254: Returns: A link to a domain logo, if the domain logo exists.
                   5255: If the domain logo does not exist, a description of the domain.
                   5256: 
                   5257: =cut
1.112     bowersj2 5258: 
1.63      www      5259: ###############################################
                   5260: sub domainlogo {
1.517     raeburn  5261:     my $domain = &determinedomain(shift);
1.518     albertel 5262:     my %designhash = &get_domainconf($domain);    
1.517     raeburn  5263:     # See if there is a logo
                   5264:     if ($designhash{$domain.'.login.domlogo'} ne '') {
1.519     raeburn  5265:         my $imgsrc = $designhash{$domain.'.login.domlogo'};
1.538     albertel 5266:         if ($imgsrc =~ m{^/(adm|res)/}) {
                   5267: 	    if ($imgsrc =~ m{^/res/}) {
                   5268: 		my $local_name = &Apache::lonnet::filelocation('',$imgsrc);
                   5269: 		&Apache::lonnet::repcopy($local_name);
                   5270: 	    }
                   5271: 	   $imgsrc = &lonhttpdurl($imgsrc);
1.519     raeburn  5272:         } 
                   5273:         return '<img src="'.$imgsrc.'" alt="'.$domain.'" />';
1.514     albertel 5274:     } elsif (defined(&Apache::lonnet::domain($domain,'description'))) {
                   5275:         return &Apache::lonnet::domain($domain,'description');
1.59      www      5276:     } else {
1.60      matthew  5277:         return '';
1.59      www      5278:     }
                   5279: }
1.63      www      5280: ##############################################
                   5281: 
                   5282: =pod
                   5283: 
1.112     bowersj2 5284: =item * &designparm()
1.63      www      5285: 
                   5286: Inputs: $which parameter; $domain (usually will be undef)
                   5287: 
                   5288: Returns: value of designparamter $which
                   5289: 
                   5290: =cut
1.112     bowersj2 5291: 
1.397     albertel 5292: 
1.400     albertel 5293: ##############################################
1.397     albertel 5294: sub designparm {
                   5295:     my ($which,$domain)=@_;
                   5296:     if (exists($env{'environment.color.'.$which})) {
1.817     bisitz   5297:         return $env{'environment.color.'.$which};
1.96      www      5298:     }
1.63      www      5299:     $domain=&determinedomain($domain);
1.1016    raeburn  5300:     my %domdesign;
                   5301:     unless ($domain eq 'public') {
                   5302:         %domdesign = &get_domainconf($domain);
                   5303:     }
1.520     raeburn  5304:     my $output;
1.517     raeburn  5305:     if ($domdesign{$domain.'.'.$which} ne '') {
1.817     bisitz   5306:         $output = $domdesign{$domain.'.'.$which};
1.63      www      5307:     } else {
1.520     raeburn  5308:         $output = $defaultdesign{$which};
                   5309:     }
                   5310:     if (($which =~ /^(student|coordinator|author|admin)\.img$/) ||
1.635     raeburn  5311:         ($which =~ /login\.(img|logo|domlogo|login)/)) {
1.538     albertel 5312:         if ($output =~ m{^/(adm|res)/}) {
1.817     bisitz   5313:             if ($output =~ m{^/res/}) {
                   5314:                 my $local_name = &Apache::lonnet::filelocation('',$output);
                   5315:                 &Apache::lonnet::repcopy($local_name);
                   5316:             }
1.520     raeburn  5317:             $output = &lonhttpdurl($output);
                   5318:         }
1.63      www      5319:     }
1.520     raeburn  5320:     return $output;
1.63      www      5321: }
1.59      www      5322: 
1.822     bisitz   5323: ##############################################
                   5324: =pod
                   5325: 
1.832     bisitz   5326: =item * &authorspace()
                   5327: 
1.1028    raeburn  5328: Inputs: $url (usually will be undef).
1.832     bisitz   5329: 
1.1132    raeburn  5330: Returns: Path to Authoring Space containing the resource or 
1.1028    raeburn  5331:          directory being viewed (or for which action is being taken). 
                   5332:          If $url is provided, and begins /priv/<domain>/<uname>
                   5333:          the path will be that portion of the $context argument.
                   5334:          Otherwise the path will be for the author space of the current
                   5335:          user when the current role is author, or for that of the 
                   5336:          co-author/assistant co-author space when the current role 
                   5337:          is co-author or assistant co-author.
1.832     bisitz   5338: 
                   5339: =cut
                   5340: 
                   5341: sub authorspace {
1.1028    raeburn  5342:     my ($url) = @_;
                   5343:     if ($url ne '') {
                   5344:         if ($url =~ m{^(/priv/$match_domain/$match_username/)}) {
                   5345:            return $1;
                   5346:         }
                   5347:     }
1.832     bisitz   5348:     my $caname = '';
1.1024    www      5349:     my $cadom = '';
1.1028    raeburn  5350:     if ($env{'request.role'} =~ /^(?:ca|aa)/) {
1.1024    www      5351:         ($cadom,$caname) =
1.832     bisitz   5352:             ($env{'request.role'}=~/($match_domain)\/($match_username)$/);
1.1028    raeburn  5353:     } elsif ($env{'request.role'} =~ m{^au\./($match_domain)/}) {
1.832     bisitz   5354:         $caname = $env{'user.name'};
1.1024    www      5355:         $cadom = $env{'user.domain'};
1.832     bisitz   5356:     }
1.1028    raeburn  5357:     if (($caname ne '') && ($cadom ne '')) {
                   5358:         return "/priv/$cadom/$caname/";
                   5359:     }
                   5360:     return;
1.832     bisitz   5361: }
                   5362: 
                   5363: ##############################################
                   5364: =pod
                   5365: 
1.822     bisitz   5366: =item * &head_subbox()
                   5367: 
                   5368: Inputs: $content (contains HTML code with page functions, etc.)
                   5369: 
                   5370: Returns: HTML div with $content
                   5371:          To be included in page header
                   5372: 
                   5373: =cut
                   5374: 
                   5375: sub head_subbox {
                   5376:     my ($content)=@_;
                   5377:     my $output =
1.993     raeburn  5378:         '<div class="LC_head_subbox">'
1.822     bisitz   5379:        .$content
                   5380:        .'</div>'
                   5381: }
                   5382: 
                   5383: ##############################################
                   5384: =pod
                   5385: 
                   5386: =item * &CSTR_pageheader()
                   5387: 
1.1026    raeburn  5388: Input: (optional) filename from which breadcrumb trail is built.
                   5389:        In most cases no input as needed, as $env{'request.filename'}
                   5390:        is appropriate for use in building the breadcrumb trail.
1.822     bisitz   5391: 
                   5392: Returns: HTML div with CSTR path and recent box
1.1132    raeburn  5393:          To be included on Authoring Space pages
1.822     bisitz   5394: 
                   5395: =cut
                   5396: 
                   5397: sub CSTR_pageheader {
1.1026    raeburn  5398:     my ($trailfile) = @_;
                   5399:     if ($trailfile eq '') {
                   5400:         $trailfile = $env{'request.filename'};
                   5401:     }
                   5402: 
                   5403: # this is for resources; directories have customtitle, and crumbs
                   5404: # and select recent are created in lonpubdir.pm
                   5405: 
                   5406:     my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
1.1022    www      5407:     my ($udom,$uname,$thisdisfn)=
1.1113    raeburn  5408:         ($trailfile =~ m{^\Q$londocroot\E/priv/([^/]+)/([^/]+)(?:|/(.*))$});
1.1026    raeburn  5409:     my $formaction = "/priv/$udom/$uname/$thisdisfn";
                   5410:     $formaction =~ s{/+}{/}g;
1.822     bisitz   5411: 
                   5412:     my $parentpath = '';
                   5413:     my $lastitem = '';
                   5414:     if ($thisdisfn =~ m-(.+/)([^/]*)$-) {
                   5415:         $parentpath = $1;
                   5416:         $lastitem = $2;
                   5417:     } else {
                   5418:         $lastitem = $thisdisfn;
                   5419:     }
1.921     bisitz   5420: 
                   5421:     my $output =
1.822     bisitz   5422:          '<div>'
                   5423:         .&Apache::loncommon::help_open_menu('','',3,'Authoring') #FIXME: Broken? Where is it?
1.1132    raeburn  5424:         .'<b>'.&mt('Authoring Space:').'</b> '
1.822     bisitz   5425:         .'<form name="dirs" method="post" action="'.$formaction
1.921     bisitz   5426:         .'" target="_top">' #FIXME lonpubdir: target="_parent"
1.1024    www      5427:         .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv/'.$udom,undef,undef);
1.921     bisitz   5428: 
                   5429:     if ($lastitem) {
                   5430:         $output .=
                   5431:              '<span class="LC_filename">'
                   5432:             .$lastitem
                   5433:             .'</span>';
                   5434:     }
                   5435:     $output .=
                   5436:          '<br />'
1.822     bisitz   5437:         #FIXME lonpubdir: &Apache::lonhtmlcommon::crumbs($uname.$thisdisfn.'/','_top','/priv','','+1',1)."</b></tt><br />"
                   5438:         .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
                   5439:         .'</form>'
                   5440:         .&Apache::lonmenu::constspaceform()
                   5441:         .'</div>';
1.921     bisitz   5442: 
                   5443:     return $output;
1.822     bisitz   5444: }
                   5445: 
1.60      matthew  5446: ###############################################
                   5447: ###############################################
                   5448: 
                   5449: =pod
                   5450: 
1.112     bowersj2 5451: =back
                   5452: 
1.549     albertel 5453: =head1 HTML Helpers
1.112     bowersj2 5454: 
                   5455: =over 4
                   5456: 
                   5457: =item * &bodytag()
1.60      matthew  5458: 
                   5459: Returns a uniform header for LON-CAPA web pages.
                   5460: 
                   5461: Inputs: 
                   5462: 
1.112     bowersj2 5463: =over 4
                   5464: 
                   5465: =item * $title, A title to be displayed on the page.
                   5466: 
                   5467: =item * $function, the current role (can be undef).
                   5468: 
                   5469: =item * $addentries, extra parameters for the <body> tag.
                   5470: 
                   5471: =item * $bodyonly, if defined, only return the <body> tag.
                   5472: 
                   5473: =item * $domain, if defined, force a given domain.
                   5474: 
                   5475: =item * $forcereg, if page should register as content page (relevant for 
1.86      www      5476:             text interface only)
1.60      matthew  5477: 
1.814     bisitz   5478: =item * $no_nav_bar, if true, keep the 'what is this' info but remove the
                   5479:                      navigational links
1.317     albertel 5480: 
1.338     albertel 5481: =item * $bgcolor, used to override the bgcolor on a webpage to a specific value
                   5482: 
1.460     albertel 5483: =item * $args, optional argument valid values are
                   5484:             no_auto_mt_title -> prevents &mt()ing the title arg
                   5485: 
1.1096    raeburn  5486: =item * $advtoolsref, optional argument, ref to an array containing
                   5487:             inlineremote items to be added in "Functions" menu below
                   5488:             breadcrumbs.
                   5489: 
1.112     bowersj2 5490: =back
                   5491: 
1.60      matthew  5492: Returns: A uniform header for LON-CAPA web pages.  
                   5493: If $bodyonly is nonzero, a string containing a <body> tag will be returned.
                   5494: If $bodyonly is undef or zero, an html string containing a <body> tag and 
                   5495: other decorations will be returned.
                   5496: 
                   5497: =cut
                   5498: 
1.54      www      5499: sub bodytag {
1.831     bisitz   5500:     my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,
1.1096    raeburn  5501:         $no_nav_bar,$bgcolor,$args,$advtoolsref)=@_;
1.339     albertel 5502: 
1.954     raeburn  5503:     my $public;
                   5504:     if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public'))
                   5505:         || ($env{'user.name'} eq '') && ($env{'user.domain'} eq '')) {
                   5506:         $public = 1;
                   5507:     }
1.460     albertel 5508:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
1.1154    raeburn  5509:     my $httphost = $args->{'use_absolute'};
1.339     albertel 5510: 
1.183     matthew  5511:     $function = &get_users_function() if (!$function);
1.339     albertel 5512:     my $img =    &designparm($function.'.img',$domain);
                   5513:     my $font =   &designparm($function.'.font',$domain);
                   5514:     my $pgbg   = $bgcolor || &designparm($function.'.pgbg',$domain);
                   5515: 
1.803     bisitz   5516:     my %design = ( 'style'   => 'margin-top: 0',
1.535     albertel 5517: 		   'bgcolor' => $pgbg,
1.339     albertel 5518: 		   'text'    => $font,
                   5519:                    'alink'   => &designparm($function.'.alink',$domain),
                   5520: 		   'vlink'   => &designparm($function.'.vlink',$domain),
                   5521: 		   'link'    => &designparm($function.'.link',$domain),);
1.438     albertel 5522:     @design{keys(%$addentries)} = @$addentries{keys(%$addentries)}; 
1.339     albertel 5523: 
1.63      www      5524:  # role and realm
1.1178    raeburn  5525:     my ($role,$realm) = split(m{\./},$env{'request.role'},2);
                   5526:     if ($realm) {
                   5527:         $realm = '/'.$realm;
                   5528:     }
1.378     raeburn  5529:     if ($role  eq 'ca') {
1.479     albertel 5530:         my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
1.500     albertel 5531:         $realm = &plainname($rname,$rdom);
1.378     raeburn  5532:     } 
1.55      www      5533: # realm
1.258     albertel 5534:     if ($env{'request.course.id'}) {
1.378     raeburn  5535:         if ($env{'request.role'} !~ /^cr/) {
                   5536:             $role = &Apache::lonnet::plaintext($role,&course_type());
                   5537:         }
1.898     raeburn  5538:         if ($env{'request.course.sec'}) {
                   5539:             $role .= ('&nbsp;'x2).'-&nbsp;'.&mt('section:').'&nbsp;'.$env{'request.course.sec'};
                   5540:         }   
1.359     albertel 5541: 	$realm = $env{'course.'.$env{'request.course.id'}.'.description'};
1.378     raeburn  5542:     } else {
                   5543:         $role = &Apache::lonnet::plaintext($role);
1.54      www      5544:     }
1.433     albertel 5545: 
1.359     albertel 5546:     if (!$realm) { $realm='&nbsp;'; }
1.330     albertel 5547: 
1.438     albertel 5548:     my $extra_body_attr = &make_attr_string($forcereg,\%design);
1.329     albertel 5549: 
1.101     www      5550: # construct main body tag
1.359     albertel 5551:     my $bodytag = "<body $extra_body_attr>".
1.1235  ! raeburn  5552: 	&Apache::lontexconvert::init_math_support();
1.252     albertel 5553: 
1.1131    raeburn  5554:     &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
                   5555: 
1.1130    raeburn  5556:     if (($bodyonly) || ($no_nav_bar) || ($env{'form.inhibitmenu'} eq 'yes')) {
1.60      matthew  5557:         return $bodytag;
1.1130    raeburn  5558:     }
1.359     albertel 5559: 
1.954     raeburn  5560:     if ($public) {
1.433     albertel 5561: 	undef($role);
                   5562:     }
1.359     albertel 5563:     
1.762     bisitz   5564:     my $titleinfo = '<h1>'.$title.'</h1>';
1.359     albertel 5565:     #
                   5566:     # Extra info if you are the DC
                   5567:     my $dc_info = '';
                   5568:     if ($env{'user.adv'} && exists($env{'user.role.dc./'.
                   5569:                         $env{'course.'.$env{'request.course.id'}.
                   5570:                                  '.domain'}.'/'})) {
                   5571:         my $cid = $env{'request.course.id'};
1.917     raeburn  5572:         $dc_info = $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
1.380     www      5573:         $dc_info =~ s/\s+$//;
1.359     albertel 5574:     }
                   5575: 
1.898     raeburn  5576:     $role = '<span class="LC_nobreak">('.$role.')</span>' if $role;
1.853     droeschl 5577: 
1.903     droeschl 5578:         if ($env{'request.state'} eq 'construct') { $forcereg=1; }
                   5579: 
                   5580:         #    if ($env{'request.state'} eq 'construct') {
                   5581:         #        $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls
                   5582:         #    }
                   5583: 
1.1130    raeburn  5584:         $bodytag .= Apache::lonhtmlcommon::scripttag(
1.1154    raeburn  5585:             Apache::lonmenu::utilityfunctions($httphost), 'start');
1.359     albertel 5586: 
1.1130    raeburn  5587:         my ($left,$right) = Apache::lonmenu::primary_menu();
1.359     albertel 5588: 
1.916     droeschl 5589:         if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
1.917     raeburn  5590:              if ($dc_info) {
                   5591:                  $dc_info = qq|<span class="LC_cusr_subheading">$dc_info</span>|;
                   5592:              }
1.1130    raeburn  5593:              $bodytag .= qq|<div id="LC_nav_bar">$left $role<br />
1.916     droeschl 5594:                 <em>$realm</em> $dc_info</div>|;
1.903     droeschl 5595:             return $bodytag;
                   5596:         }
1.894     droeschl 5597: 
1.927     raeburn  5598:         unless ($env{'request.symb'} =~ m/\.page___\d+___/) {
1.1130    raeburn  5599:             $bodytag .= qq|<div id="LC_nav_bar">$left $role</div>|;
1.927     raeburn  5600:         }
1.916     droeschl 5601: 
1.1130    raeburn  5602:         $bodytag .= $right;
1.852     droeschl 5603: 
1.917     raeburn  5604:         if ($dc_info) {
                   5605:             $dc_info = &dc_courseid_toggle($dc_info);
                   5606:         }
                   5607:         $bodytag .= qq|<div id="LC_realm">$realm $dc_info</div>|;
1.916     droeschl 5608: 
1.1169    raeburn  5609:         #if directed to not display the secondary menu, don't.  
1.1168    raeburn  5610:         if ($args->{'no_secondary_menu'}) {
                   5611:             return $bodytag;
                   5612:         }
1.1169    raeburn  5613:         #don't show menus for public users
1.954     raeburn  5614:         if (!$public){
1.1154    raeburn  5615:             $bodytag .= Apache::lonmenu::secondary_menu($httphost);
1.903     droeschl 5616:             $bodytag .= Apache::lonmenu::serverform();
1.920     raeburn  5617:             $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
                   5618:             if ($env{'request.state'} eq 'construct') {
1.962     droeschl 5619:                 $bodytag .= &Apache::lonmenu::innerregister($forcereg,
1.920     raeburn  5620:                                 $args->{'bread_crumbs'});
1.1096    raeburn  5621:             } elsif ($forcereg) {
                   5622:                 $bodytag .= &Apache::lonmenu::innerregister($forcereg,undef,
                   5623:                                                             $args->{'group'});
                   5624:             } else {
                   5625:                 $bodytag .= 
                   5626:                     &Apache::lonmenu::prepare_functions($env{'request.noversionuri'},
                   5627:                                                         $forcereg,$args->{'group'},
                   5628:                                                         $args->{'bread_crumbs'},
                   5629:                                                         $advtoolsref);
1.920     raeburn  5630:             }
1.903     droeschl 5631:         }else{
                   5632:             # this is to seperate menu from content when there's no secondary
                   5633:             # menu. Especially needed for public accessible ressources.
                   5634:             $bodytag .= '<hr style="clear:both" />';
                   5635:             $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); 
1.235     raeburn  5636:         }
1.903     droeschl 5637: 
1.235     raeburn  5638:         return $bodytag;
1.182     matthew  5639: }
                   5640: 
1.917     raeburn  5641: sub dc_courseid_toggle {
                   5642:     my ($dc_info) = @_;
1.980     raeburn  5643:     return ' <span id="dccidtext" class="LC_cusr_subheading LC_nobreak">'.
1.1069    raeburn  5644:            '<a href="javascript:showCourseID();" class="LC_menubuttons_link">'.
1.917     raeburn  5645:            &mt('(More ...)').'</a></span>'.
                   5646:            '<div id="dccid" class="LC_dccid">'.$dc_info.'</div>';
                   5647: }
                   5648: 
1.330     albertel 5649: sub make_attr_string {
                   5650:     my ($register,$attr_ref) = @_;
                   5651: 
                   5652:     if ($attr_ref && !ref($attr_ref)) {
                   5653: 	die("addentries Must be a hash ref ".
                   5654: 	    join(':',caller(1))." ".
                   5655: 	    join(':',caller(0))." ");
                   5656:     }
                   5657: 
                   5658:     if ($register) {
1.339     albertel 5659: 	my ($on_load,$on_unload);
                   5660: 	foreach my $key (keys(%{$attr_ref})) {
                   5661: 	    if      (lc($key) eq 'onload') {
                   5662: 		$on_load.=$attr_ref->{$key}.';';
                   5663: 		delete($attr_ref->{$key});
                   5664: 
                   5665: 	    } elsif (lc($key) eq 'onunload') {
                   5666: 		$on_unload.=$attr_ref->{$key}.';';
                   5667: 		delete($attr_ref->{$key});
                   5668: 	    }
                   5669: 	}
1.953     droeschl 5670: 	$attr_ref->{'onload'}  = $on_load;
                   5671: 	$attr_ref->{'onunload'}= $on_unload;
1.330     albertel 5672:     }
1.339     albertel 5673: 
1.330     albertel 5674:     my $attr_string;
1.1159    raeburn  5675:     foreach my $attr (sort(keys(%$attr_ref))) {
1.330     albertel 5676: 	$attr_string .= " $attr=\"".$attr_ref->{$attr}.'" ';
                   5677:     }
                   5678:     return $attr_string;
                   5679: }
                   5680: 
                   5681: 
1.182     matthew  5682: ###############################################
1.251     albertel 5683: ###############################################
                   5684: 
                   5685: =pod
                   5686: 
                   5687: =item * &endbodytag()
                   5688: 
                   5689: Returns a uniform footer for LON-CAPA web pages.
                   5690: 
1.635     raeburn  5691: Inputs: 1 - optional reference to an args hash
                   5692: If in the hash, key for noredirectlink has a value which evaluates to true,
                   5693: a 'Continue' link is not displayed if the page contains an
                   5694: internal redirect in the <head></head> section,
                   5695: i.e., $env{'internal.head.redirect'} exists   
1.251     albertel 5696: 
                   5697: =cut
                   5698: 
                   5699: sub endbodytag {
1.635     raeburn  5700:     my ($args) = @_;
1.1080    raeburn  5701:     my $endbodytag;
                   5702:     unless ((ref($args) eq 'HASH') && ($args->{'notbody'})) {
                   5703:         $endbodytag='</body>';
                   5704:     }
1.315     albertel 5705:     if ( exists( $env{'internal.head.redirect'} ) ) {
1.635     raeburn  5706:         if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
                   5707: 	    $endbodytag=
                   5708: 	        "<br /><a href=\"$env{'internal.head.redirect'}\">".
                   5709: 	        &mt('Continue').'</a>'.
                   5710: 	        $endbodytag;
                   5711:         }
1.315     albertel 5712:     }
1.251     albertel 5713:     return $endbodytag;
                   5714: }
                   5715: 
1.352     albertel 5716: =pod
                   5717: 
                   5718: =item * &standard_css()
                   5719: 
                   5720: Returns a style sheet
                   5721: 
                   5722: Inputs: (all optional)
                   5723:             domain         -> force to color decorate a page for a specific
                   5724:                                domain
                   5725:             function       -> force usage of a specific rolish color scheme
                   5726:             bgcolor        -> override the default page bgcolor
                   5727: 
                   5728: =cut
                   5729: 
1.343     albertel 5730: sub standard_css {
1.345     albertel 5731:     my ($function,$domain,$bgcolor) = @_;
1.352     albertel 5732:     $function  = &get_users_function() if (!$function);
                   5733:     my $img    = &designparm($function.'.img',   $domain);
                   5734:     my $tabbg  = &designparm($function.'.tabbg', $domain);
                   5735:     my $font   = &designparm($function.'.font',  $domain);
1.801     tempelho 5736:     my $fontmenu = &designparm($function.'.fontmenu', $domain);
1.791     tempelho 5737: #second colour for later usage
1.345     albertel 5738:     my $sidebg = &designparm($function.'.sidebg',$domain);
1.382     albertel 5739:     my $pgbg_or_bgcolor =
                   5740: 	         $bgcolor ||
1.352     albertel 5741: 	         &designparm($function.'.pgbg',  $domain);
1.382     albertel 5742:     my $pgbg   = &designparm($function.'.pgbg',  $domain);
1.352     albertel 5743:     my $alink  = &designparm($function.'.alink', $domain);
                   5744:     my $vlink  = &designparm($function.'.vlink', $domain);
                   5745:     my $link   = &designparm($function.'.link',  $domain);
                   5746: 
1.602     albertel 5747:     my $sans                 = 'Verdana,Arial,Helvetica,sans-serif';
1.395     albertel 5748:     my $mono                 = 'monospace';
1.850     bisitz   5749:     my $data_table_head      = $sidebg;
                   5750:     my $data_table_light     = '#FAFAFA';
1.1060    bisitz   5751:     my $data_table_dark      = '#E0E0E0';
1.470     banghart 5752:     my $data_table_darker    = '#CCCCCC';
1.349     albertel 5753:     my $data_table_highlight = '#FFFF00';
1.352     albertel 5754:     my $mail_new             = '#FFBB77';
                   5755:     my $mail_new_hover       = '#DD9955';
                   5756:     my $mail_read            = '#BBBB77';
                   5757:     my $mail_read_hover      = '#999944';
                   5758:     my $mail_replied         = '#AAAA88';
                   5759:     my $mail_replied_hover   = '#888855';
                   5760:     my $mail_other           = '#99BBBB';
                   5761:     my $mail_other_hover     = '#669999';
1.391     albertel 5762:     my $table_header         = '#DDDDDD';
1.489     raeburn  5763:     my $feedback_link_bg     = '#BBBBBB';
1.911     bisitz   5764:     my $lg_border_color      = '#C8C8C8';
1.952     onken    5765:     my $button_hover         = '#BF2317';
1.392     albertel 5766: 
1.608     albertel 5767:     my $border = ($env{'browser.type'} eq 'explorer' ||
1.911     bisitz   5768:       $env{'browser.type'} eq 'safari'     ) ? '0 2px 0 2px'
                   5769:                                              : '0 3px 0 4px';
1.448     albertel 5770: 
1.523     albertel 5771: 
1.343     albertel 5772:     return <<END;
1.947     droeschl 5773: 
                   5774: /* needed for iframe to allow 100% height in FF */
                   5775: body, html { 
                   5776:     margin: 0;
                   5777:     padding: 0 0.5%;
                   5778:     height: 99%; /* to avoid scrollbars */
                   5779: }
                   5780: 
1.795     www      5781: body {
1.911     bisitz   5782:   font-family: $sans;
                   5783:   line-height:130%;
                   5784:   font-size:0.83em;
                   5785:   color:$font;
1.795     www      5786: }
                   5787: 
1.959     onken    5788: a:focus,
                   5789: a:focus img {
1.795     www      5790:   color: red;
                   5791: }
1.698     harmsja  5792: 
1.911     bisitz   5793: form, .inline {
                   5794:   display: inline;
1.795     www      5795: }
1.721     harmsja  5796: 
1.795     www      5797: .LC_right {
1.911     bisitz   5798:   text-align:right;
1.795     www      5799: }
                   5800: 
                   5801: .LC_middle {
1.911     bisitz   5802:   vertical-align:middle;
1.795     www      5803: }
1.721     harmsja  5804: 
1.1130    raeburn  5805: .LC_floatleft {
                   5806:   float: left;
                   5807: }
                   5808: 
                   5809: .LC_floatright {
                   5810:   float: right;
                   5811: }
                   5812: 
1.911     bisitz   5813: .LC_400Box {
                   5814:   width:400px;
                   5815: }
1.721     harmsja  5816: 
1.947     droeschl 5817: .LC_iframecontainer {
                   5818:     width: 98%;
                   5819:     margin: 0;
                   5820:     position: fixed;
                   5821:     top: 8.5em;
                   5822:     bottom: 0;
                   5823: }
                   5824: 
                   5825: .LC_iframecontainer iframe{
                   5826:     border: none;
                   5827:     width: 100%;
                   5828:     height: 100%;
                   5829: }
                   5830: 
1.778     bisitz   5831: .LC_filename {
                   5832:   font-family: $mono;
                   5833:   white-space:pre;
1.921     bisitz   5834:   font-size: 120%;
1.778     bisitz   5835: }
                   5836: 
                   5837: .LC_fileicon {
                   5838:   border: none;
                   5839:   height: 1.3em;
                   5840:   vertical-align: text-bottom;
                   5841:   margin-right: 0.3em;
                   5842:   text-decoration:none;
                   5843: }
                   5844: 
1.1008    www      5845: .LC_setting {
                   5846:   text-decoration:underline;
                   5847: }
                   5848: 
1.350     albertel 5849: .LC_error {
                   5850:   color: red;
                   5851: }
1.795     www      5852: 
1.1097    bisitz   5853: .LC_warning {
                   5854:   color: darkorange;
                   5855: }
                   5856: 
1.457     albertel 5857: .LC_diff_removed {
1.733     bisitz   5858:   color: red;
1.394     albertel 5859: }
1.532     albertel 5860: 
                   5861: .LC_info,
1.457     albertel 5862: .LC_success,
                   5863: .LC_diff_added {
1.350     albertel 5864:   color: green;
                   5865: }
1.795     www      5866: 
1.802     bisitz   5867: div.LC_confirm_box {
                   5868:   background-color: #FAFAFA;
                   5869:   border: 1px solid $lg_border_color;
                   5870:   margin-right: 0;
                   5871:   padding: 5px;
                   5872: }
                   5873: 
                   5874: div.LC_confirm_box .LC_error img,
                   5875: div.LC_confirm_box .LC_success img {
                   5876:   vertical-align: middle;
                   5877: }
                   5878: 
1.440     albertel 5879: .LC_icon {
1.771     droeschl 5880:   border: none;
1.790     droeschl 5881:   vertical-align: middle;
1.771     droeschl 5882: }
                   5883: 
1.543     albertel 5884: .LC_docs_spacer {
                   5885:   width: 25px;
                   5886:   height: 1px;
1.771     droeschl 5887:   border: none;
1.543     albertel 5888: }
1.346     albertel 5889: 
1.532     albertel 5890: .LC_internal_info {
1.735     bisitz   5891:   color: #999999;
1.532     albertel 5892: }
                   5893: 
1.794     www      5894: .LC_discussion {
1.1050    www      5895:   background: $data_table_dark;
1.911     bisitz   5896:   border: 1px solid black;
                   5897:   margin: 2px;
1.794     www      5898: }
                   5899: 
                   5900: .LC_disc_action_left {
1.1050    www      5901:   background: $sidebg;
1.911     bisitz   5902:   text-align: left;
1.1050    www      5903:   padding: 4px;
                   5904:   margin: 2px;
1.794     www      5905: }
                   5906: 
                   5907: .LC_disc_action_right {
1.1050    www      5908:   background: $sidebg;
1.911     bisitz   5909:   text-align: right;
1.1050    www      5910:   padding: 4px;
                   5911:   margin: 2px;
1.794     www      5912: }
                   5913: 
                   5914: .LC_disc_new_item {
1.911     bisitz   5915:   background: white;
                   5916:   border: 2px solid red;
1.1050    www      5917:   margin: 4px;
                   5918:   padding: 4px;
1.794     www      5919: }
                   5920: 
                   5921: .LC_disc_old_item {
1.911     bisitz   5922:   background: white;
1.1050    www      5923:   margin: 4px;
                   5924:   padding: 4px;
1.794     www      5925: }
                   5926: 
1.458     albertel 5927: table.LC_pastsubmission {
                   5928:   border: 1px solid black;
                   5929:   margin: 2px;
                   5930: }
                   5931: 
1.924     bisitz   5932: table#LC_menubuttons {
1.345     albertel 5933:   width: 100%;
                   5934:   background: $pgbg;
1.392     albertel 5935:   border: 2px;
1.402     albertel 5936:   border-collapse: separate;
1.803     bisitz   5937:   padding: 0;
1.345     albertel 5938: }
1.392     albertel 5939: 
1.801     tempelho 5940: table#LC_title_bar a {
                   5941:   color: $fontmenu;
                   5942: }
1.836     bisitz   5943: 
1.807     droeschl 5944: table#LC_title_bar {
1.819     tempelho 5945:   clear: both;
1.836     bisitz   5946:   display: none;
1.807     droeschl 5947: }
                   5948: 
1.795     www      5949: table#LC_title_bar,
1.933     droeschl 5950: table.LC_breadcrumbs, /* obsolete? */
1.393     albertel 5951: table#LC_title_bar.LC_with_remote {
1.359     albertel 5952:   width: 100%;
1.392     albertel 5953:   border-color: $pgbg;
                   5954:   border-style: solid;
                   5955:   border-width: $border;
1.379     albertel 5956:   background: $pgbg;
1.801     tempelho 5957:   color: $fontmenu;
1.392     albertel 5958:   border-collapse: collapse;
1.803     bisitz   5959:   padding: 0;
1.819     tempelho 5960:   margin: 0;
1.359     albertel 5961: }
1.795     www      5962: 
1.933     droeschl 5963: ul.LC_breadcrumb_tools_outerlist {
1.913     droeschl 5964:     margin: 0;
                   5965:     padding: 0;
1.933     droeschl 5966:     position: relative;
                   5967:     list-style: none;
1.913     droeschl 5968: }
1.933     droeschl 5969: ul.LC_breadcrumb_tools_outerlist li {
1.913     droeschl 5970:     display: inline;
                   5971: }
1.933     droeschl 5972: 
                   5973: .LC_breadcrumb_tools_navigation {
1.913     droeschl 5974:     padding: 0;
1.933     droeschl 5975:     margin: 0;
                   5976:     float: left;
1.913     droeschl 5977: }
1.933     droeschl 5978: .LC_breadcrumb_tools_tools {
                   5979:     padding: 0;
                   5980:     margin: 0;
1.913     droeschl 5981:     float: right;
                   5982: }
                   5983: 
1.359     albertel 5984: table#LC_title_bar td {
                   5985:   background: $tabbg;
                   5986: }
1.795     www      5987: 
1.911     bisitz   5988: table#LC_menubuttons img {
1.803     bisitz   5989:   border: none;
1.346     albertel 5990: }
1.795     www      5991: 
1.842     droeschl 5992: .LC_breadcrumbs_component {
1.911     bisitz   5993:   float: right;
                   5994:   margin: 0 1em;
1.357     albertel 5995: }
1.842     droeschl 5996: .LC_breadcrumbs_component img {
1.911     bisitz   5997:   vertical-align: middle;
1.777     tempelho 5998: }
1.795     www      5999: 
1.383     albertel 6000: td.LC_table_cell_checkbox {
                   6001:   text-align: center;
                   6002: }
1.795     www      6003: 
                   6004: .LC_fontsize_small {
1.911     bisitz   6005:   font-size: 70%;
1.705     tempelho 6006: }
                   6007: 
1.844     bisitz   6008: #LC_breadcrumbs {
1.911     bisitz   6009:   clear:both;
                   6010:   background: $sidebg;
                   6011:   border-bottom: 1px solid $lg_border_color;
                   6012:   line-height: 2.5em;
1.933     droeschl 6013:   overflow: hidden;
1.911     bisitz   6014:   margin: 0;
                   6015:   padding: 0;
1.995     raeburn  6016:   text-align: left;
1.819     tempelho 6017: }
1.862     bisitz   6018: 
1.1098    bisitz   6019: .LC_head_subbox, .LC_actionbox {
1.911     bisitz   6020:   clear:both;
                   6021:   background: #F8F8F8; /* $sidebg; */
1.915     droeschl 6022:   border: 1px solid $sidebg;
1.1098    bisitz   6023:   margin: 0 0 10px 0;
1.966     bisitz   6024:   padding: 3px;
1.995     raeburn  6025:   text-align: left;
1.822     bisitz   6026: }
                   6027: 
1.795     www      6028: .LC_fontsize_medium {
1.911     bisitz   6029:   font-size: 85%;
1.705     tempelho 6030: }
                   6031: 
1.795     www      6032: .LC_fontsize_large {
1.911     bisitz   6033:   font-size: 120%;
1.705     tempelho 6034: }
                   6035: 
1.346     albertel 6036: .LC_menubuttons_inline_text {
                   6037:   color: $font;
1.698     harmsja  6038:   font-size: 90%;
1.701     harmsja  6039:   padding-left:3px;
1.346     albertel 6040: }
                   6041: 
1.934     droeschl 6042: .LC_menubuttons_inline_text img{
                   6043:   vertical-align: middle;
                   6044: }
                   6045: 
1.1051    www      6046: li.LC_menubuttons_inline_text img {
1.951     onken    6047:   cursor:pointer;
1.1002    droeschl 6048:   text-decoration: none;
1.951     onken    6049: }
                   6050: 
1.526     www      6051: .LC_menubuttons_link {
                   6052:   text-decoration: none;
                   6053: }
1.795     www      6054: 
1.522     albertel 6055: .LC_menubuttons_category {
1.521     www      6056:   color: $font;
1.526     www      6057:   background: $pgbg;
1.521     www      6058:   font-size: larger;
                   6059:   font-weight: bold;
                   6060: }
                   6061: 
1.346     albertel 6062: td.LC_menubuttons_text {
1.911     bisitz   6063:   color: $font;
1.346     albertel 6064: }
1.706     harmsja  6065: 
1.346     albertel 6066: .LC_current_location {
                   6067:   background: $tabbg;
                   6068: }
1.795     www      6069: 
1.938     bisitz   6070: table.LC_data_table {
1.347     albertel 6071:   border: 1px solid #000000;
1.402     albertel 6072:   border-collapse: separate;
1.426     albertel 6073:   border-spacing: 1px;
1.610     albertel 6074:   background: $pgbg;
1.347     albertel 6075: }
1.795     www      6076: 
1.422     albertel 6077: .LC_data_table_dense {
                   6078:   font-size: small;
                   6079: }
1.795     www      6080: 
1.507     raeburn  6081: table.LC_nested_outer {
                   6082:   border: 1px solid #000000;
1.589     raeburn  6083:   border-collapse: collapse;
1.803     bisitz   6084:   border-spacing: 0;
1.507     raeburn  6085:   width: 100%;
                   6086: }
1.795     www      6087: 
1.879     raeburn  6088: table.LC_innerpickbox,
1.507     raeburn  6089: table.LC_nested {
1.803     bisitz   6090:   border: none;
1.589     raeburn  6091:   border-collapse: collapse;
1.803     bisitz   6092:   border-spacing: 0;
1.507     raeburn  6093:   width: 100%;
                   6094: }
1.795     www      6095: 
1.911     bisitz   6096: table.LC_data_table tr th,
                   6097: table.LC_calendar tr th,
1.879     raeburn  6098: table.LC_prior_tries tr th,
                   6099: table.LC_innerpickbox tr th {
1.349     albertel 6100:   font-weight: bold;
                   6101:   background-color: $data_table_head;
1.801     tempelho 6102:   color:$fontmenu;
1.701     harmsja  6103:   font-size:90%;
1.347     albertel 6104: }
1.795     www      6105: 
1.879     raeburn  6106: table.LC_innerpickbox tr th,
                   6107: table.LC_innerpickbox tr td {
                   6108:   vertical-align: top;
                   6109: }
                   6110: 
1.711     raeburn  6111: table.LC_data_table tr.LC_info_row > td {
1.735     bisitz   6112:   background-color: #CCCCCC;
1.711     raeburn  6113:   font-weight: bold;
                   6114:   text-align: left;
                   6115: }
1.795     www      6116: 
1.912     bisitz   6117: table.LC_data_table tr.LC_odd_row > td {
                   6118:   background-color: $data_table_light;
                   6119:   padding: 2px;
                   6120:   vertical-align: top;
                   6121: }
                   6122: 
1.809     bisitz   6123: table.LC_pick_box tr > td.LC_odd_row {
1.349     albertel 6124:   background-color: $data_table_light;
1.912     bisitz   6125:   vertical-align: top;
                   6126: }
                   6127: 
                   6128: table.LC_data_table tr.LC_even_row > td {
                   6129:   background-color: $data_table_dark;
1.425     albertel 6130:   padding: 2px;
1.900     bisitz   6131:   vertical-align: top;
1.347     albertel 6132: }
1.795     www      6133: 
1.809     bisitz   6134: table.LC_pick_box tr > td.LC_even_row {
1.349     albertel 6135:   background-color: $data_table_dark;
1.900     bisitz   6136:   vertical-align: top;
1.347     albertel 6137: }
1.795     www      6138: 
1.425     albertel 6139: table.LC_data_table tr.LC_data_table_highlight td {
                   6140:   background-color: $data_table_darker;
                   6141: }
1.795     www      6142: 
1.639     raeburn  6143: table.LC_data_table tr td.LC_leftcol_header {
                   6144:   background-color: $data_table_head;
                   6145:   font-weight: bold;
                   6146: }
1.795     www      6147: 
1.451     albertel 6148: table.LC_data_table tr.LC_empty_row td,
1.507     raeburn  6149: table.LC_nested tr.LC_empty_row td {
1.421     albertel 6150:   font-weight: bold;
                   6151:   font-style: italic;
                   6152:   text-align: center;
                   6153:   padding: 8px;
1.347     albertel 6154: }
1.795     www      6155: 
1.1114    raeburn  6156: table.LC_data_table tr.LC_empty_row td,
                   6157: table.LC_data_table tr.LC_footer_row td {
1.940     bisitz   6158:   background-color: $sidebg;
                   6159: }
                   6160: 
                   6161: table.LC_nested tr.LC_empty_row td {
                   6162:   background-color: #FFFFFF;
                   6163: }
                   6164: 
1.890     droeschl 6165: table.LC_caption {
                   6166: }
                   6167: 
1.507     raeburn  6168: table.LC_nested tr.LC_empty_row td {
1.465     albertel 6169:   padding: 4ex
                   6170: }
1.795     www      6171: 
1.507     raeburn  6172: table.LC_nested_outer tr th {
                   6173:   font-weight: bold;
1.801     tempelho 6174:   color:$fontmenu;
1.507     raeburn  6175:   background-color: $data_table_head;
1.701     harmsja  6176:   font-size: small;
1.507     raeburn  6177:   border-bottom: 1px solid #000000;
                   6178: }
1.795     www      6179: 
1.507     raeburn  6180: table.LC_nested_outer tr td.LC_subheader {
                   6181:   background-color: $data_table_head;
                   6182:   font-weight: bold;
                   6183:   font-size: small;
                   6184:   border-bottom: 1px solid #000000;
                   6185:   text-align: right;
1.451     albertel 6186: }
1.795     www      6187: 
1.507     raeburn  6188: table.LC_nested tr.LC_info_row td {
1.735     bisitz   6189:   background-color: #CCCCCC;
1.451     albertel 6190:   font-weight: bold;
                   6191:   font-size: small;
1.507     raeburn  6192:   text-align: center;
                   6193: }
1.795     www      6194: 
1.589     raeburn  6195: table.LC_nested tr.LC_info_row td.LC_left_item,
                   6196: table.LC_nested_outer tr th.LC_left_item {
1.507     raeburn  6197:   text-align: left;
1.451     albertel 6198: }
1.795     www      6199: 
1.507     raeburn  6200: table.LC_nested td {
1.735     bisitz   6201:   background-color: #FFFFFF;
1.451     albertel 6202:   font-size: small;
1.507     raeburn  6203: }
1.795     www      6204: 
1.507     raeburn  6205: table.LC_nested_outer tr th.LC_right_item,
                   6206: table.LC_nested tr.LC_info_row td.LC_right_item,
                   6207: table.LC_nested tr.LC_odd_row td.LC_right_item,
                   6208: table.LC_nested tr td.LC_right_item {
1.451     albertel 6209:   text-align: right;
                   6210: }
                   6211: 
1.507     raeburn  6212: table.LC_nested tr.LC_odd_row td {
1.735     bisitz   6213:   background-color: #EEEEEE;
1.451     albertel 6214: }
                   6215: 
1.473     raeburn  6216: table.LC_createuser {
                   6217: }
                   6218: 
                   6219: table.LC_createuser tr.LC_section_row td {
1.701     harmsja  6220:   font-size: small;
1.473     raeburn  6221: }
                   6222: 
                   6223: table.LC_createuser tr.LC_info_row td  {
1.735     bisitz   6224:   background-color: #CCCCCC;
1.473     raeburn  6225:   font-weight: bold;
                   6226:   text-align: center;
                   6227: }
                   6228: 
1.349     albertel 6229: table.LC_calendar {
                   6230:   border: 1px solid #000000;
                   6231:   border-collapse: collapse;
1.917     raeburn  6232:   width: 98%;
1.349     albertel 6233: }
1.795     www      6234: 
1.349     albertel 6235: table.LC_calendar_pickdate {
                   6236:   font-size: xx-small;
                   6237: }
1.795     www      6238: 
1.349     albertel 6239: table.LC_calendar tr td {
                   6240:   border: 1px solid #000000;
                   6241:   vertical-align: top;
1.917     raeburn  6242:   width: 14%;
1.349     albertel 6243: }
1.795     www      6244: 
1.349     albertel 6245: table.LC_calendar tr td.LC_calendar_day_empty {
                   6246:   background-color: $data_table_dark;
                   6247: }
1.795     www      6248: 
1.779     bisitz   6249: table.LC_calendar tr td.LC_calendar_day_current {
                   6250:   background-color: $data_table_highlight;
1.777     tempelho 6251: }
1.795     www      6252: 
1.938     bisitz   6253: table.LC_data_table tr td.LC_mail_new {
1.349     albertel 6254:   background-color: $mail_new;
                   6255: }
1.795     www      6256: 
1.938     bisitz   6257: table.LC_data_table tr.LC_mail_new:hover {
1.349     albertel 6258:   background-color: $mail_new_hover;
                   6259: }
1.795     www      6260: 
1.938     bisitz   6261: table.LC_data_table tr td.LC_mail_read {
1.349     albertel 6262:   background-color: $mail_read;
                   6263: }
1.795     www      6264: 
1.938     bisitz   6265: /*
                   6266: table.LC_data_table tr.LC_mail_read:hover {
1.349     albertel 6267:   background-color: $mail_read_hover;
                   6268: }
1.938     bisitz   6269: */
1.795     www      6270: 
1.938     bisitz   6271: table.LC_data_table tr td.LC_mail_replied {
1.349     albertel 6272:   background-color: $mail_replied;
                   6273: }
1.795     www      6274: 
1.938     bisitz   6275: /*
                   6276: table.LC_data_table tr.LC_mail_replied:hover {
1.349     albertel 6277:   background-color: $mail_replied_hover;
                   6278: }
1.938     bisitz   6279: */
1.795     www      6280: 
1.938     bisitz   6281: table.LC_data_table tr td.LC_mail_other {
1.349     albertel 6282:   background-color: $mail_other;
                   6283: }
1.795     www      6284: 
1.938     bisitz   6285: /*
                   6286: table.LC_data_table tr.LC_mail_other:hover {
1.349     albertel 6287:   background-color: $mail_other_hover;
                   6288: }
1.938     bisitz   6289: */
1.494     raeburn  6290: 
1.777     tempelho 6291: table.LC_data_table tr > td.LC_browser_file,
                   6292: table.LC_data_table tr > td.LC_browser_file_published {
1.899     bisitz   6293:   background: #AAEE77;
1.389     albertel 6294: }
1.795     www      6295: 
1.777     tempelho 6296: table.LC_data_table tr > td.LC_browser_file_locked,
                   6297: table.LC_data_table tr > td.LC_browser_file_unpublished {
1.389     albertel 6298:   background: #FFAA99;
1.387     albertel 6299: }
1.795     www      6300: 
1.777     tempelho 6301: table.LC_data_table tr > td.LC_browser_file_obsolete {
1.899     bisitz   6302:   background: #888888;
1.779     bisitz   6303: }
1.795     www      6304: 
1.777     tempelho 6305: table.LC_data_table tr > td.LC_browser_file_modified,
1.779     bisitz   6306: table.LC_data_table tr > td.LC_browser_file_metamodified {
1.899     bisitz   6307:   background: #F8F866;
1.777     tempelho 6308: }
1.795     www      6309: 
1.696     bisitz   6310: table.LC_data_table tr.LC_browser_folder > td {
1.899     bisitz   6311:   background: #E0E8FF;
1.387     albertel 6312: }
1.696     bisitz   6313: 
1.707     bisitz   6314: table.LC_data_table tr > td.LC_roles_is {
1.911     bisitz   6315:   /* background: #77FF77; */
1.707     bisitz   6316: }
1.795     www      6317: 
1.707     bisitz   6318: table.LC_data_table tr > td.LC_roles_future {
1.939     bisitz   6319:   border-right: 8px solid #FFFF77;
1.707     bisitz   6320: }
1.795     www      6321: 
1.707     bisitz   6322: table.LC_data_table tr > td.LC_roles_will {
1.939     bisitz   6323:   border-right: 8px solid #FFAA77;
1.707     bisitz   6324: }
1.795     www      6325: 
1.707     bisitz   6326: table.LC_data_table tr > td.LC_roles_expired {
1.939     bisitz   6327:   border-right: 8px solid #FF7777;
1.707     bisitz   6328: }
1.795     www      6329: 
1.707     bisitz   6330: table.LC_data_table tr > td.LC_roles_will_not {
1.939     bisitz   6331:   border-right: 8px solid #AAFF77;
1.707     bisitz   6332: }
1.795     www      6333: 
1.707     bisitz   6334: table.LC_data_table tr > td.LC_roles_selected {
1.939     bisitz   6335:   border-right: 8px solid #11CC55;
1.707     bisitz   6336: }
                   6337: 
1.388     albertel 6338: span.LC_current_location {
1.701     harmsja  6339:   font-size:larger;
1.388     albertel 6340:   background: $pgbg;
                   6341: }
1.387     albertel 6342: 
1.1029    www      6343: span.LC_current_nav_location {
                   6344:   font-weight:bold;
                   6345:   background: $sidebg;
                   6346: }
                   6347: 
1.395     albertel 6348: span.LC_parm_menu_item {
                   6349:   font-size: larger;
                   6350: }
1.795     www      6351: 
1.395     albertel 6352: span.LC_parm_scope_all {
                   6353:   color: red;
                   6354: }
1.795     www      6355: 
1.395     albertel 6356: span.LC_parm_scope_folder {
                   6357:   color: green;
                   6358: }
1.795     www      6359: 
1.395     albertel 6360: span.LC_parm_scope_resource {
                   6361:   color: orange;
                   6362: }
1.795     www      6363: 
1.395     albertel 6364: span.LC_parm_part {
                   6365:   color: blue;
                   6366: }
1.795     www      6367: 
1.911     bisitz   6368: span.LC_parm_folder,
                   6369: span.LC_parm_symb {
1.395     albertel 6370:   font-size: x-small;
                   6371:   font-family: $mono;
                   6372:   color: #AAAAAA;
                   6373: }
                   6374: 
1.977     bisitz   6375: ul.LC_parm_parmlist li {
                   6376:   display: inline-block;
                   6377:   padding: 0.3em 0.8em;
                   6378:   vertical-align: top;
                   6379:   width: 150px;
                   6380:   border-top:1px solid $lg_border_color;
                   6381: }
                   6382: 
1.795     www      6383: td.LC_parm_overview_level_menu,
                   6384: td.LC_parm_overview_map_menu,
                   6385: td.LC_parm_overview_parm_selectors,
                   6386: td.LC_parm_overview_restrictions  {
1.396     albertel 6387:   border: 1px solid black;
                   6388:   border-collapse: collapse;
                   6389: }
1.795     www      6390: 
1.396     albertel 6391: table.LC_parm_overview_restrictions td {
                   6392:   border-width: 1px 4px 1px 4px;
                   6393:   border-style: solid;
                   6394:   border-color: $pgbg;
                   6395:   text-align: center;
                   6396: }
1.795     www      6397: 
1.396     albertel 6398: table.LC_parm_overview_restrictions th {
                   6399:   background: $tabbg;
                   6400:   border-width: 1px 4px 1px 4px;
                   6401:   border-style: solid;
                   6402:   border-color: $pgbg;
                   6403: }
1.795     www      6404: 
1.398     albertel 6405: table#LC_helpmenu {
1.803     bisitz   6406:   border: none;
1.398     albertel 6407:   height: 55px;
1.803     bisitz   6408:   border-spacing: 0;
1.398     albertel 6409: }
                   6410: 
                   6411: table#LC_helpmenu fieldset legend {
                   6412:   font-size: larger;
                   6413: }
1.795     www      6414: 
1.397     albertel 6415: table#LC_helpmenu_links {
                   6416:   width: 100%;
                   6417:   border: 1px solid black;
                   6418:   background: $pgbg;
1.803     bisitz   6419:   padding: 0;
1.397     albertel 6420:   border-spacing: 1px;
                   6421: }
1.795     www      6422: 
1.397     albertel 6423: table#LC_helpmenu_links tr td {
                   6424:   padding: 1px;
                   6425:   background: $tabbg;
1.399     albertel 6426:   text-align: center;
                   6427:   font-weight: bold;
1.397     albertel 6428: }
1.396     albertel 6429: 
1.795     www      6430: table#LC_helpmenu_links a:link,
                   6431: table#LC_helpmenu_links a:visited,
1.397     albertel 6432: table#LC_helpmenu_links a:active {
                   6433:   text-decoration: none;
                   6434:   color: $font;
                   6435: }
1.795     www      6436: 
1.397     albertel 6437: table#LC_helpmenu_links a:hover {
                   6438:   text-decoration: underline;
                   6439:   color: $vlink;
                   6440: }
1.396     albertel 6441: 
1.417     albertel 6442: .LC_chrt_popup_exists {
                   6443:   border: 1px solid #339933;
                   6444:   margin: -1px;
                   6445: }
1.795     www      6446: 
1.417     albertel 6447: .LC_chrt_popup_up {
                   6448:   border: 1px solid yellow;
                   6449:   margin: -1px;
                   6450: }
1.795     www      6451: 
1.417     albertel 6452: .LC_chrt_popup {
                   6453:   border: 1px solid #8888FF;
                   6454:   background: #CCCCFF;
                   6455: }
1.795     www      6456: 
1.421     albertel 6457: table.LC_pick_box {
                   6458:   border-collapse: separate;
                   6459:   background: white;
                   6460:   border: 1px solid black;
                   6461:   border-spacing: 1px;
                   6462: }
1.795     www      6463: 
1.421     albertel 6464: table.LC_pick_box td.LC_pick_box_title {
1.850     bisitz   6465:   background: $sidebg;
1.421     albertel 6466:   font-weight: bold;
1.900     bisitz   6467:   text-align: left;
1.740     bisitz   6468:   vertical-align: top;
1.421     albertel 6469:   width: 184px;
                   6470:   padding: 8px;
                   6471: }
1.795     www      6472: 
1.579     raeburn  6473: table.LC_pick_box td.LC_pick_box_value {
                   6474:   text-align: left;
                   6475:   padding: 8px;
                   6476: }
1.795     www      6477: 
1.579     raeburn  6478: table.LC_pick_box td.LC_pick_box_select {
                   6479:   text-align: left;
                   6480:   padding: 8px;
                   6481: }
1.795     www      6482: 
1.424     albertel 6483: table.LC_pick_box td.LC_pick_box_separator {
1.803     bisitz   6484:   padding: 0;
1.421     albertel 6485:   height: 1px;
                   6486:   background: black;
                   6487: }
1.795     www      6488: 
1.421     albertel 6489: table.LC_pick_box td.LC_pick_box_submit {
                   6490:   text-align: right;
                   6491: }
1.795     www      6492: 
1.579     raeburn  6493: table.LC_pick_box td.LC_evenrow_value {
                   6494:   text-align: left;
                   6495:   padding: 8px;
                   6496:   background-color: $data_table_light;
                   6497: }
1.795     www      6498: 
1.579     raeburn  6499: table.LC_pick_box td.LC_oddrow_value {
                   6500:   text-align: left;
                   6501:   padding: 8px;
                   6502:   background-color: $data_table_light;
                   6503: }
1.795     www      6504: 
1.579     raeburn  6505: span.LC_helpform_receipt_cat {
                   6506:   font-weight: bold;
                   6507: }
1.795     www      6508: 
1.424     albertel 6509: table.LC_group_priv_box {
                   6510:   background: white;
                   6511:   border: 1px solid black;
                   6512:   border-spacing: 1px;
                   6513: }
1.795     www      6514: 
1.424     albertel 6515: table.LC_group_priv_box td.LC_pick_box_title {
                   6516:   background: $tabbg;
                   6517:   font-weight: bold;
                   6518:   text-align: right;
                   6519:   width: 184px;
                   6520: }
1.795     www      6521: 
1.424     albertel 6522: table.LC_group_priv_box td.LC_groups_fixed {
                   6523:   background: $data_table_light;
                   6524:   text-align: center;
                   6525: }
1.795     www      6526: 
1.424     albertel 6527: table.LC_group_priv_box td.LC_groups_optional {
                   6528:   background: $data_table_dark;
                   6529:   text-align: center;
                   6530: }
1.795     www      6531: 
1.424     albertel 6532: table.LC_group_priv_box td.LC_groups_functionality {
                   6533:   background: $data_table_darker;
                   6534:   text-align: center;
                   6535:   font-weight: bold;
                   6536: }
1.795     www      6537: 
1.424     albertel 6538: table.LC_group_priv td {
                   6539:   text-align: left;
1.803     bisitz   6540:   padding: 0;
1.424     albertel 6541: }
                   6542: 
                   6543: .LC_navbuttons {
                   6544:   margin: 2ex 0ex 2ex 0ex;
                   6545: }
1.795     www      6546: 
1.423     albertel 6547: .LC_topic_bar {
                   6548:   font-weight: bold;
                   6549:   background: $tabbg;
1.918     wenzelju 6550:   margin: 1em 0em 1em 2em;
1.805     bisitz   6551:   padding: 3px;
1.918     wenzelju 6552:   font-size: 1.2em;
1.423     albertel 6553: }
1.795     www      6554: 
1.423     albertel 6555: .LC_topic_bar span {
1.918     wenzelju 6556:   left: 0.5em;
                   6557:   position: absolute;
1.423     albertel 6558:   vertical-align: middle;
1.918     wenzelju 6559:   font-size: 1.2em;
1.423     albertel 6560: }
1.795     www      6561: 
1.423     albertel 6562: table.LC_course_group_status {
                   6563:   margin: 20px;
                   6564: }
1.795     www      6565: 
1.423     albertel 6566: table.LC_status_selector td {
                   6567:   vertical-align: top;
                   6568:   text-align: center;
1.424     albertel 6569:   padding: 4px;
                   6570: }
1.795     www      6571: 
1.599     albertel 6572: div.LC_feedback_link {
1.616     albertel 6573:   clear: both;
1.829     kalberla 6574:   background: $sidebg;
1.779     bisitz   6575:   width: 100%;
1.829     kalberla 6576:   padding-bottom: 10px;
                   6577:   border: 1px $tabbg solid;
1.833     kalberla 6578:   height: 22px;
                   6579:   line-height: 22px;
                   6580:   padding-top: 5px;
                   6581: }
                   6582: 
                   6583: div.LC_feedback_link img {
                   6584:   height: 22px;
1.867     kalberla 6585:   vertical-align:middle;
1.829     kalberla 6586: }
                   6587: 
1.911     bisitz   6588: div.LC_feedback_link a {
1.829     kalberla 6589:   text-decoration: none;
1.489     raeburn  6590: }
1.795     www      6591: 
1.867     kalberla 6592: div.LC_comblock {
1.911     bisitz   6593:   display:inline;
1.867     kalberla 6594:   color:$font;
                   6595:   font-size:90%;
                   6596: }
                   6597: 
                   6598: div.LC_feedback_link div.LC_comblock {
                   6599:   padding-left:5px;
                   6600: }
                   6601: 
                   6602: div.LC_feedback_link div.LC_comblock a {
                   6603:   color:$font;
                   6604: }
                   6605: 
1.489     raeburn  6606: span.LC_feedback_link {
1.858     bisitz   6607:   /* background: $feedback_link_bg; */
1.599     albertel 6608:   font-size: larger;
                   6609: }
1.795     www      6610: 
1.599     albertel 6611: span.LC_message_link {
1.858     bisitz   6612:   /* background: $feedback_link_bg; */
1.599     albertel 6613:   font-size: larger;
                   6614:   position: absolute;
                   6615:   right: 1em;
1.489     raeburn  6616: }
1.421     albertel 6617: 
1.515     albertel 6618: table.LC_prior_tries {
1.524     albertel 6619:   border: 1px solid #000000;
                   6620:   border-collapse: separate;
                   6621:   border-spacing: 1px;
1.515     albertel 6622: }
1.523     albertel 6623: 
1.515     albertel 6624: table.LC_prior_tries td {
1.524     albertel 6625:   padding: 2px;
1.515     albertel 6626: }
1.523     albertel 6627: 
                   6628: .LC_answer_correct {
1.795     www      6629:   background: lightgreen;
                   6630:   color: darkgreen;
                   6631:   padding: 6px;
1.523     albertel 6632: }
1.795     www      6633: 
1.523     albertel 6634: .LC_answer_charged_try {
1.797     www      6635:   background: #FFAAAA;
1.795     www      6636:   color: darkred;
                   6637:   padding: 6px;
1.523     albertel 6638: }
1.795     www      6639: 
1.779     bisitz   6640: .LC_answer_not_charged_try,
1.523     albertel 6641: .LC_answer_no_grade,
                   6642: .LC_answer_late {
1.795     www      6643:   background: lightyellow;
1.523     albertel 6644:   color: black;
1.795     www      6645:   padding: 6px;
1.523     albertel 6646: }
1.795     www      6647: 
1.523     albertel 6648: .LC_answer_previous {
1.795     www      6649:   background: lightblue;
                   6650:   color: darkblue;
                   6651:   padding: 6px;
1.523     albertel 6652: }
1.795     www      6653: 
1.779     bisitz   6654: .LC_answer_no_message {
1.777     tempelho 6655:   background: #FFFFFF;
                   6656:   color: black;
1.795     www      6657:   padding: 6px;
1.779     bisitz   6658: }
1.795     www      6659: 
1.779     bisitz   6660: .LC_answer_unknown {
                   6661:   background: orange;
                   6662:   color: black;
1.795     www      6663:   padding: 6px;
1.777     tempelho 6664: }
1.795     www      6665: 
1.529     albertel 6666: span.LC_prior_numerical,
                   6667: span.LC_prior_string,
                   6668: span.LC_prior_custom,
                   6669: span.LC_prior_reaction,
                   6670: span.LC_prior_math {
1.925     bisitz   6671:   font-family: $mono;
1.523     albertel 6672:   white-space: pre;
                   6673: }
                   6674: 
1.525     albertel 6675: span.LC_prior_string {
1.925     bisitz   6676:   font-family: $mono;
1.525     albertel 6677:   white-space: pre;
                   6678: }
                   6679: 
1.523     albertel 6680: table.LC_prior_option {
                   6681:   width: 100%;
                   6682:   border-collapse: collapse;
                   6683: }
1.795     www      6684: 
1.911     bisitz   6685: table.LC_prior_rank,
1.795     www      6686: table.LC_prior_match {
1.528     albertel 6687:   border-collapse: collapse;
                   6688: }
1.795     www      6689: 
1.528     albertel 6690: table.LC_prior_option tr td,
                   6691: table.LC_prior_rank tr td,
                   6692: table.LC_prior_match tr td {
1.524     albertel 6693:   border: 1px solid #000000;
1.515     albertel 6694: }
                   6695: 
1.855     bisitz   6696: .LC_nobreak {
1.544     albertel 6697:   white-space: nowrap;
1.519     raeburn  6698: }
                   6699: 
1.576     raeburn  6700: span.LC_cusr_emph {
                   6701:   font-style: italic;
                   6702: }
                   6703: 
1.633     raeburn  6704: span.LC_cusr_subheading {
                   6705:   font-weight: normal;
                   6706:   font-size: 85%;
                   6707: }
                   6708: 
1.861     bisitz   6709: div.LC_docs_entry_move {
1.859     bisitz   6710:   border: 1px solid #BBBBBB;
1.545     albertel 6711:   background: #DDDDDD;
1.861     bisitz   6712:   width: 22px;
1.859     bisitz   6713:   padding: 1px;
                   6714:   margin: 0;
1.545     albertel 6715: }
                   6716: 
1.861     bisitz   6717: table.LC_data_table tr > td.LC_docs_entry_commands,
                   6718: table.LC_data_table tr > td.LC_docs_entry_parameter {
1.545     albertel 6719:   font-size: x-small;
                   6720: }
1.795     www      6721: 
1.861     bisitz   6722: .LC_docs_entry_parameter {
                   6723:   white-space: nowrap;
                   6724: }
                   6725: 
1.544     albertel 6726: .LC_docs_copy {
1.545     albertel 6727:   color: #000099;
1.544     albertel 6728: }
1.795     www      6729: 
1.544     albertel 6730: .LC_docs_cut {
1.545     albertel 6731:   color: #550044;
1.544     albertel 6732: }
1.795     www      6733: 
1.544     albertel 6734: .LC_docs_rename {
1.545     albertel 6735:   color: #009900;
1.544     albertel 6736: }
1.795     www      6737: 
1.544     albertel 6738: .LC_docs_remove {
1.545     albertel 6739:   color: #990000;
                   6740: }
                   6741: 
1.547     albertel 6742: .LC_docs_reinit_warn,
                   6743: .LC_docs_ext_edit {
                   6744:   font-size: x-small;
                   6745: }
                   6746: 
1.545     albertel 6747: table.LC_docs_adddocs td,
                   6748: table.LC_docs_adddocs th {
                   6749:   border: 1px solid #BBBBBB;
                   6750:   padding: 4px;
                   6751:   background: #DDDDDD;
1.543     albertel 6752: }
                   6753: 
1.584     albertel 6754: table.LC_sty_begin {
                   6755:   background: #BBFFBB;
                   6756: }
1.795     www      6757: 
1.584     albertel 6758: table.LC_sty_end {
                   6759:   background: #FFBBBB;
                   6760: }
                   6761: 
1.589     raeburn  6762: table.LC_double_column {
1.803     bisitz   6763:   border-width: 0;
1.589     raeburn  6764:   border-collapse: collapse;
                   6765:   width: 100%;
                   6766:   padding: 2px;
                   6767: }
                   6768: 
                   6769: table.LC_double_column tr td.LC_left_col {
1.590     raeburn  6770:   top: 2px;
1.589     raeburn  6771:   left: 2px;
                   6772:   width: 47%;
                   6773:   vertical-align: top;
                   6774: }
                   6775: 
                   6776: table.LC_double_column tr td.LC_right_col {
                   6777:   top: 2px;
1.779     bisitz   6778:   right: 2px;
1.589     raeburn  6779:   width: 47%;
                   6780:   vertical-align: top;
                   6781: }
                   6782: 
1.591     raeburn  6783: div.LC_left_float {
                   6784:   float: left;
                   6785:   padding-right: 5%;
1.597     albertel 6786:   padding-bottom: 4px;
1.591     raeburn  6787: }
                   6788: 
                   6789: div.LC_clear_float_header {
1.597     albertel 6790:   padding-bottom: 2px;
1.591     raeburn  6791: }
                   6792: 
                   6793: div.LC_clear_float_footer {
1.597     albertel 6794:   padding-top: 10px;
1.591     raeburn  6795:   clear: both;
                   6796: }
                   6797: 
1.597     albertel 6798: div.LC_grade_show_user {
1.941     bisitz   6799: /*  border-left: 5px solid $sidebg; */
                   6800:   border-top: 5px solid #000000;
                   6801:   margin: 50px 0 0 0;
1.936     bisitz   6802:   padding: 15px 0 5px 10px;
1.597     albertel 6803: }
1.795     www      6804: 
1.936     bisitz   6805: div.LC_grade_show_user_odd_row {
1.941     bisitz   6806: /*  border-left: 5px solid #000000; */
                   6807: }
                   6808: 
                   6809: div.LC_grade_show_user div.LC_Box {
                   6810:   margin-right: 50px;
1.597     albertel 6811: }
                   6812: 
                   6813: div.LC_grade_submissions,
                   6814: div.LC_grade_message_center,
1.936     bisitz   6815: div.LC_grade_info_links {
1.597     albertel 6816:   margin: 5px;
                   6817:   width: 99%;
                   6818:   background: #FFFFFF;
                   6819: }
1.795     www      6820: 
1.597     albertel 6821: div.LC_grade_submissions_header,
1.936     bisitz   6822: div.LC_grade_message_center_header {
1.705     tempelho 6823:   font-weight: bold;
                   6824:   font-size: large;
1.597     albertel 6825: }
1.795     www      6826: 
1.597     albertel 6827: div.LC_grade_submissions_body,
1.936     bisitz   6828: div.LC_grade_message_center_body {
1.597     albertel 6829:   border: 1px solid black;
                   6830:   width: 99%;
                   6831:   background: #FFFFFF;
                   6832: }
1.795     www      6833: 
1.613     albertel 6834: table.LC_scantron_action {
                   6835:   width: 100%;
                   6836: }
1.795     www      6837: 
1.613     albertel 6838: table.LC_scantron_action tr th {
1.698     harmsja  6839:   font-weight:bold;
                   6840:   font-style:normal;
1.613     albertel 6841: }
1.795     www      6842: 
1.779     bisitz   6843: .LC_edit_problem_header,
1.614     albertel 6844: div.LC_edit_problem_footer {
1.705     tempelho 6845:   font-weight: normal;
                   6846:   font-size:  medium;
1.602     albertel 6847:   margin: 2px;
1.1060    bisitz   6848:   background-color: $sidebg;
1.600     albertel 6849: }
1.795     www      6850: 
1.600     albertel 6851: div.LC_edit_problem_header,
1.602     albertel 6852: div.LC_edit_problem_header div,
1.614     albertel 6853: div.LC_edit_problem_footer,
                   6854: div.LC_edit_problem_footer div,
1.602     albertel 6855: div.LC_edit_problem_editxml_header,
                   6856: div.LC_edit_problem_editxml_header div {
1.1205    golterma 6857:   z-index: 100;
1.600     albertel 6858: }
1.795     www      6859: 
1.600     albertel 6860: div.LC_edit_problem_header_title {
1.705     tempelho 6861:   font-weight: bold;
                   6862:   font-size: larger;
1.602     albertel 6863:   background: $tabbg;
                   6864:   padding: 3px;
1.1060    bisitz   6865:   margin: 0 0 5px 0;
1.602     albertel 6866: }
1.795     www      6867: 
1.602     albertel 6868: table.LC_edit_problem_header_title {
                   6869:   width: 100%;
1.600     albertel 6870:   background: $tabbg;
1.602     albertel 6871: }
                   6872: 
1.1205    golterma 6873: div.LC_edit_actionbar {
                   6874:     background-color: $sidebg;
1.1218    droeschl 6875:     margin: 0;
                   6876:     padding: 0;
                   6877:     line-height: 200%;
1.602     albertel 6878: }
1.795     www      6879: 
1.1218    droeschl 6880: div.LC_edit_actionbar div{
                   6881:     padding: 0;
                   6882:     margin: 0;
                   6883:     display: inline-block;
1.600     albertel 6884: }
1.795     www      6885: 
1.1124    bisitz   6886: .LC_edit_opt {
                   6887:   padding-left: 1em;
                   6888:   white-space: nowrap;
                   6889: }
                   6890: 
1.1152    golterma 6891: .LC_edit_problem_latexhelper{
                   6892:     text-align: right;
                   6893: }
                   6894: 
                   6895: #LC_edit_problem_colorful div{
                   6896:     margin-left: 40px;
                   6897: }
                   6898: 
1.1205    golterma 6899: #LC_edit_problem_codemirror div{
                   6900:     margin-left: 0px;
                   6901: }
                   6902: 
1.911     bisitz   6903: img.stift {
1.803     bisitz   6904:   border-width: 0;
                   6905:   vertical-align: middle;
1.677     riegler  6906: }
1.680     riegler  6907: 
1.923     bisitz   6908: table td.LC_mainmenu_col_fieldset {
1.680     riegler  6909:   vertical-align: top;
1.777     tempelho 6910: }
1.795     www      6911: 
1.716     raeburn  6912: div.LC_createcourse {
1.911     bisitz   6913:   margin: 10px 10px 10px 10px;
1.716     raeburn  6914: }
                   6915: 
1.917     raeburn  6916: .LC_dccid {
1.1130    raeburn  6917:   float: right;
1.917     raeburn  6918:   margin: 0.2em 0 0 0;
                   6919:   padding: 0;
                   6920:   font-size: 90%;
                   6921:   display:none;
                   6922: }
                   6923: 
1.897     wenzelju 6924: ol.LC_primary_menu a:hover,
1.721     harmsja  6925: ol#LC_MenuBreadcrumbs a:hover,
                   6926: ol#LC_PathBreadcrumbs a:hover,
1.897     wenzelju 6927: ul#LC_secondary_menu a:hover,
1.721     harmsja  6928: .LC_FormSectionClearButton input:hover
1.795     www      6929: ul.LC_TabContent   li:hover a {
1.952     onken    6930:   color:$button_hover;
1.911     bisitz   6931:   text-decoration:none;
1.693     droeschl 6932: }
                   6933: 
1.779     bisitz   6934: h1 {
1.911     bisitz   6935:   padding: 0;
                   6936:   line-height:130%;
1.693     droeschl 6937: }
1.698     harmsja  6938: 
1.911     bisitz   6939: h2,
                   6940: h3,
                   6941: h4,
                   6942: h5,
                   6943: h6 {
                   6944:   margin: 5px 0 5px 0;
                   6945:   padding: 0;
                   6946:   line-height:130%;
1.693     droeschl 6947: }
1.795     www      6948: 
                   6949: .LC_hcell {
1.911     bisitz   6950:   padding:3px 15px 3px 15px;
                   6951:   margin: 0;
                   6952:   background-color:$tabbg;
                   6953:   color:$fontmenu;
                   6954:   border-bottom:solid 1px $lg_border_color;
1.693     droeschl 6955: }
1.795     www      6956: 
1.840     bisitz   6957: .LC_Box > .LC_hcell {
1.911     bisitz   6958:   margin: 0 -10px 10px -10px;
1.835     bisitz   6959: }
                   6960: 
1.721     harmsja  6961: .LC_noBorder {
1.911     bisitz   6962:   border: 0;
1.698     harmsja  6963: }
1.693     droeschl 6964: 
1.721     harmsja  6965: .LC_FormSectionClearButton input {
1.911     bisitz   6966:   background-color:transparent;
                   6967:   border: none;
                   6968:   cursor:pointer;
                   6969:   text-decoration:underline;
1.693     droeschl 6970: }
1.763     bisitz   6971: 
                   6972: .LC_help_open_topic {
1.911     bisitz   6973:   color: #FFFFFF;
                   6974:   background-color: #EEEEFF;
                   6975:   margin: 1px;
                   6976:   padding: 4px;
                   6977:   border: 1px solid #000033;
                   6978:   white-space: nowrap;
                   6979:   /* vertical-align: middle; */
1.759     neumanie 6980: }
1.693     droeschl 6981: 
1.911     bisitz   6982: dl,
                   6983: ul,
                   6984: div,
                   6985: fieldset {
                   6986:   margin: 10px 10px 10px 0;
                   6987:   /* overflow: hidden; */
1.693     droeschl 6988: }
1.795     www      6989: 
1.1211    raeburn  6990: article.geogebraweb div {
                   6991:     margin: 0;
                   6992: }
                   6993: 
1.838     bisitz   6994: fieldset > legend {
1.911     bisitz   6995:   font-weight: bold;
                   6996:   padding: 0 5px 0 5px;
1.838     bisitz   6997: }
                   6998: 
1.813     bisitz   6999: #LC_nav_bar {
1.911     bisitz   7000:   float: left;
1.995     raeburn  7001:   background-color: $pgbg_or_bgcolor;
1.966     bisitz   7002:   margin: 0 0 2px 0;
1.807     droeschl 7003: }
                   7004: 
1.916     droeschl 7005: #LC_realm {
                   7006:   margin: 0.2em 0 0 0;
                   7007:   padding: 0;
                   7008:   font-weight: bold;
                   7009:   text-align: center;
1.995     raeburn  7010:   background-color: $pgbg_or_bgcolor;
1.916     droeschl 7011: }
                   7012: 
1.911     bisitz   7013: #LC_nav_bar em {
                   7014:   font-weight: bold;
                   7015:   font-style: normal;
1.807     droeschl 7016: }
                   7017: 
1.897     wenzelju 7018: ol.LC_primary_menu {
1.934     droeschl 7019:   margin: 0;
1.1076    raeburn  7020:   padding: 0;
1.807     droeschl 7021: }
                   7022: 
1.852     droeschl 7023: ol#LC_PathBreadcrumbs {
1.911     bisitz   7024:   margin: 0;
1.693     droeschl 7025: }
                   7026: 
1.897     wenzelju 7027: ol.LC_primary_menu li {
1.1076    raeburn  7028:   color: RGB(80, 80, 80);
                   7029:   vertical-align: middle;
                   7030:   text-align: left;
                   7031:   list-style: none;
1.1205    golterma 7032:   position: relative;
1.1076    raeburn  7033:   float: left;
1.1205    golterma 7034:   z-index: 100; /* will be displayed above codemirror and underneath the help-layer */
                   7035:   line-height: 1.5em;
1.1076    raeburn  7036: }
                   7037: 
1.1205    golterma 7038: ol.LC_primary_menu li a,
                   7039: ol.LC_primary_menu li p {
1.1076    raeburn  7040:   display: block;
                   7041:   margin: 0;
                   7042:   padding: 0 5px 0 10px;
                   7043:   text-decoration: none;
                   7044: }
                   7045: 
1.1205    golterma 7046: ol.LC_primary_menu li p span.LC_primary_menu_innertitle {
                   7047:   display: inline-block;
                   7048:   width: 95%;
                   7049:   text-align: left;
                   7050: }
                   7051: 
                   7052: ol.LC_primary_menu li p span.LC_primary_menu_innerarrow {
                   7053:   display: inline-block;	
                   7054:   width: 5%;
                   7055:   float: right;
                   7056:   text-align: right;
                   7057:   font-size: 70%;
                   7058: }
                   7059: 
                   7060: ol.LC_primary_menu ul {
1.1076    raeburn  7061:   display: none;
1.1205    golterma 7062:   width: 15em;
1.1076    raeburn  7063:   background-color: $data_table_light;
1.1205    golterma 7064:   position: absolute;
                   7065:   top: 100%;
1.1076    raeburn  7066: }
                   7067: 
1.1205    golterma 7068: ol.LC_primary_menu ul ul {
                   7069:   left: 100%;
                   7070:   top: 0;
                   7071: }
                   7072: 
                   7073: ol.LC_primary_menu li:hover > ul, ol.LC_primary_menu li.hover > ul {
1.1076    raeburn  7074:   display: block;
                   7075:   position: absolute;
                   7076:   margin: 0;
                   7077:   padding: 0;
1.1078    raeburn  7078:   z-index: 2;
1.1076    raeburn  7079: }
                   7080: 
                   7081: ol.LC_primary_menu li:hover li, ol.LC_primary_menu li.hover li {
1.1205    golterma 7082: /* First Submenu -> size should be smaller than the menu title of the whole menu */
1.1076    raeburn  7083:   font-size: 90%;
1.911     bisitz   7084:   vertical-align: top;
1.1076    raeburn  7085:   float: none;
1.1079    raeburn  7086:   border-left: 1px solid black;
                   7087:   border-right: 1px solid black;
1.1205    golterma 7088: /* A dark bottom border to visualize different menu options; 
                   7089: overwritten in the create_submenu routine for the last border-bottom of the menu */
                   7090:   border-bottom: 1px solid $data_table_dark; 
1.1076    raeburn  7091: }
                   7092: 
1.1205    golterma 7093: ol.LC_primary_menu li li p:hover {
                   7094:   color:$button_hover;
                   7095:   text-decoration:none;
                   7096:   background-color:$data_table_dark;
1.1076    raeburn  7097: }
                   7098: 
                   7099: ol.LC_primary_menu li li a:hover {
                   7100:    color:$button_hover;
                   7101:    background-color:$data_table_dark;
1.693     droeschl 7102: }
                   7103: 
1.1205    golterma 7104: /* Font-size equal to the size of the predecessors*/
                   7105: ol.LC_primary_menu li:hover li li {
                   7106:   font-size: 100%;
                   7107: }
                   7108: 
1.897     wenzelju 7109: ol.LC_primary_menu li img {
1.911     bisitz   7110:   vertical-align: bottom;
1.934     droeschl 7111:   height: 1.1em;
1.1077    raeburn  7112:   margin: 0.2em 0 0 0;
1.693     droeschl 7113: }
                   7114: 
1.897     wenzelju 7115: ol.LC_primary_menu a {
1.911     bisitz   7116:   color: RGB(80, 80, 80);
                   7117:   text-decoration: none;
1.693     droeschl 7118: }
1.795     www      7119: 
1.949     droeschl 7120: ol.LC_primary_menu a.LC_new_message {
                   7121:   font-weight:bold;
                   7122:   color: darkred;
                   7123: }
                   7124: 
1.975     raeburn  7125: ol.LC_docs_parameters {
                   7126:   margin-left: 0;
                   7127:   padding: 0;
                   7128:   list-style: none;
                   7129: }
                   7130: 
                   7131: ol.LC_docs_parameters li {
                   7132:   margin: 0;
                   7133:   padding-right: 20px;
                   7134:   display: inline;
                   7135: }
                   7136: 
1.976     raeburn  7137: ol.LC_docs_parameters li:before {
                   7138:   content: "\\002022 \\0020";
                   7139: }
                   7140: 
                   7141: li.LC_docs_parameters_title {
                   7142:   font-weight: bold;
                   7143: }
                   7144: 
                   7145: ol.LC_docs_parameters li.LC_docs_parameters_title:before {
                   7146:   content: "";
                   7147: }
                   7148: 
1.897     wenzelju 7149: ul#LC_secondary_menu {
1.1107    raeburn  7150:   clear: right;
1.911     bisitz   7151:   color: $fontmenu;
                   7152:   background: $tabbg;
                   7153:   list-style: none;
                   7154:   padding: 0;
                   7155:   margin: 0;
                   7156:   width: 100%;
1.995     raeburn  7157:   text-align: left;
1.1107    raeburn  7158:   float: left;
1.808     droeschl 7159: }
                   7160: 
1.897     wenzelju 7161: ul#LC_secondary_menu li {
1.911     bisitz   7162:   font-weight: bold;
                   7163:   line-height: 1.8em;
1.1107    raeburn  7164:   border-right: 1px solid black;
                   7165:   float: left;
                   7166: }
                   7167: 
                   7168: ul#LC_secondary_menu li.LC_hoverable:hover, ul#LC_secondary_menu li.hover {
                   7169:   background-color: $data_table_light;
                   7170: }
                   7171: 
                   7172: ul#LC_secondary_menu li a {
1.911     bisitz   7173:   padding: 0 0.8em;
1.1107    raeburn  7174: }
                   7175: 
                   7176: ul#LC_secondary_menu li ul {
                   7177:   display: none;
                   7178: }
                   7179: 
                   7180: ul#LC_secondary_menu li:hover ul, ul#LC_secondary_menu li.hover ul {
                   7181:   display: block;
                   7182:   position: absolute;
                   7183:   margin: 0;
                   7184:   padding: 0;
                   7185:   list-style:none;
                   7186:   float: none;
                   7187:   background-color: $data_table_light;
                   7188:   z-index: 2;
                   7189:   margin-left: -1px;
                   7190: }
                   7191: 
                   7192: ul#LC_secondary_menu li ul li {
                   7193:   font-size: 90%;
                   7194:   vertical-align: top;
                   7195:   border-left: 1px solid black;
1.911     bisitz   7196:   border-right: 1px solid black;
1.1119    raeburn  7197:   background-color: $data_table_light;
1.1107    raeburn  7198:   list-style:none;
                   7199:   float: none;
                   7200: }
                   7201: 
                   7202: ul#LC_secondary_menu li ul li:hover, ul#LC_secondary_menu li ul li.hover {
                   7203:   background-color: $data_table_dark;
1.807     droeschl 7204: }
                   7205: 
1.847     tempelho 7206: ul.LC_TabContent {
1.911     bisitz   7207:   display:block;
                   7208:   background: $sidebg;
                   7209:   border-bottom: solid 1px $lg_border_color;
                   7210:   list-style:none;
1.1020    raeburn  7211:   margin: -1px -10px 0 -10px;
1.911     bisitz   7212:   padding: 0;
1.693     droeschl 7213: }
                   7214: 
1.795     www      7215: ul.LC_TabContent li,
                   7216: ul.LC_TabContentBigger li {
1.911     bisitz   7217:   float:left;
1.741     harmsja  7218: }
1.795     www      7219: 
1.897     wenzelju 7220: ul#LC_secondary_menu li a {
1.911     bisitz   7221:   color: $fontmenu;
                   7222:   text-decoration: none;
1.693     droeschl 7223: }
1.795     www      7224: 
1.721     harmsja  7225: ul.LC_TabContent {
1.952     onken    7226:   min-height:20px;
1.721     harmsja  7227: }
1.795     www      7228: 
                   7229: ul.LC_TabContent li {
1.911     bisitz   7230:   vertical-align:middle;
1.959     onken    7231:   padding: 0 16px 0 10px;
1.911     bisitz   7232:   background-color:$tabbg;
                   7233:   border-bottom:solid 1px $lg_border_color;
1.1020    raeburn  7234:   border-left: solid 1px $font;
1.721     harmsja  7235: }
1.795     www      7236: 
1.847     tempelho 7237: ul.LC_TabContent .right {
1.911     bisitz   7238:   float:right;
1.847     tempelho 7239: }
                   7240: 
1.911     bisitz   7241: ul.LC_TabContent li a,
                   7242: ul.LC_TabContent li {
                   7243:   color:rgb(47,47,47);
                   7244:   text-decoration:none;
                   7245:   font-size:95%;
                   7246:   font-weight:bold;
1.952     onken    7247:   min-height:20px;
                   7248: }
                   7249: 
1.959     onken    7250: ul.LC_TabContent li a:hover,
                   7251: ul.LC_TabContent li a:focus {
1.952     onken    7252:   color: $button_hover;
1.959     onken    7253:   background:none;
                   7254:   outline:none;
1.952     onken    7255: }
                   7256: 
                   7257: ul.LC_TabContent li:hover {
                   7258:   color: $button_hover;
                   7259:   cursor:pointer;
1.721     harmsja  7260: }
1.795     www      7261: 
1.911     bisitz   7262: ul.LC_TabContent li.active {
1.952     onken    7263:   color: $font;
1.911     bisitz   7264:   background:#FFFFFF url(/adm/lonIcons/open.gif) no-repeat scroll right center;
1.952     onken    7265:   border-bottom:solid 1px #FFFFFF;
                   7266:   cursor: default;
1.744     ehlerst  7267: }
1.795     www      7268: 
1.959     onken    7269: ul.LC_TabContent li.active a {
                   7270:   color:$font;
                   7271:   background:#FFFFFF;
                   7272:   outline: none;
                   7273: }
1.1047    raeburn  7274: 
                   7275: ul.LC_TabContent li.goback {
                   7276:   float: left;
                   7277:   border-left: none;
                   7278: }
                   7279: 
1.870     tempelho 7280: #maincoursedoc {
1.911     bisitz   7281:   clear:both;
1.870     tempelho 7282: }
                   7283: 
                   7284: ul.LC_TabContentBigger {
1.911     bisitz   7285:   display:block;
                   7286:   list-style:none;
                   7287:   padding: 0;
1.870     tempelho 7288: }
                   7289: 
1.795     www      7290: ul.LC_TabContentBigger li {
1.911     bisitz   7291:   vertical-align:bottom;
                   7292:   height: 30px;
                   7293:   font-size:110%;
                   7294:   font-weight:bold;
                   7295:   color: #737373;
1.841     tempelho 7296: }
                   7297: 
1.957     onken    7298: ul.LC_TabContentBigger li.active {
                   7299:   position: relative;
                   7300:   top: 1px;
                   7301: }
                   7302: 
1.870     tempelho 7303: ul.LC_TabContentBigger li a {
1.911     bisitz   7304:   background:url('/adm/lonIcons/tabbgleft.gif') left bottom no-repeat;
                   7305:   height: 30px;
                   7306:   line-height: 30px;
                   7307:   text-align: center;
                   7308:   display: block;
                   7309:   text-decoration: none;
1.958     onken    7310:   outline: none;  
1.741     harmsja  7311: }
1.795     www      7312: 
1.870     tempelho 7313: ul.LC_TabContentBigger li.active a {
1.911     bisitz   7314:   background:url('/adm/lonIcons/tabbgleft.gif') left top no-repeat;
                   7315:   color:$font;
1.744     ehlerst  7316: }
1.795     www      7317: 
1.870     tempelho 7318: ul.LC_TabContentBigger li b {
1.911     bisitz   7319:   background: url('/adm/lonIcons/tabbgright.gif') no-repeat right bottom;
                   7320:   display: block;
                   7321:   float: left;
                   7322:   padding: 0 30px;
1.957     onken    7323:   border-bottom: 1px solid $lg_border_color;
1.870     tempelho 7324: }
                   7325: 
1.956     onken    7326: ul.LC_TabContentBigger li:hover b {
                   7327:   color:$button_hover;
                   7328: }
                   7329: 
1.870     tempelho 7330: ul.LC_TabContentBigger li.active b {
1.911     bisitz   7331:   background:url('/adm/lonIcons/tabbgright.gif') right top no-repeat;
                   7332:   color:$font;
1.957     onken    7333:   border: 0;
1.741     harmsja  7334: }
1.693     droeschl 7335: 
1.870     tempelho 7336: 
1.862     bisitz   7337: ul.LC_CourseBreadcrumbs {
                   7338:   background: $sidebg;
1.1020    raeburn  7339:   height: 2em;
1.862     bisitz   7340:   padding-left: 10px;
1.1020    raeburn  7341:   margin: 0;
1.862     bisitz   7342:   list-style-position: inside;
                   7343: }
                   7344: 
1.911     bisitz   7345: ol#LC_MenuBreadcrumbs,
1.862     bisitz   7346: ol#LC_PathBreadcrumbs {
1.911     bisitz   7347:   padding-left: 10px;
                   7348:   margin: 0;
1.933     droeschl 7349:   height: 2.5em;  /* equal to #LC_breadcrumbs line-height */
1.693     droeschl 7350: }
                   7351: 
1.911     bisitz   7352: ol#LC_MenuBreadcrumbs li,
                   7353: ol#LC_PathBreadcrumbs li,
1.862     bisitz   7354: ul.LC_CourseBreadcrumbs li {
1.911     bisitz   7355:   display: inline;
1.933     droeschl 7356:   white-space: normal;  
1.693     droeschl 7357: }
                   7358: 
1.823     bisitz   7359: ol#LC_MenuBreadcrumbs li a,
1.862     bisitz   7360: ul.LC_CourseBreadcrumbs li a {
1.911     bisitz   7361:   text-decoration: none;
                   7362:   font-size:90%;
1.693     droeschl 7363: }
1.795     www      7364: 
1.969     droeschl 7365: ol#LC_MenuBreadcrumbs h1 {
                   7366:   display: inline;
                   7367:   font-size: 90%;
                   7368:   line-height: 2.5em;
                   7369:   margin: 0;
                   7370:   padding: 0;
                   7371: }
                   7372: 
1.795     www      7373: ol#LC_PathBreadcrumbs li a {
1.911     bisitz   7374:   text-decoration:none;
                   7375:   font-size:100%;
                   7376:   font-weight:bold;
1.693     droeschl 7377: }
1.795     www      7378: 
1.840     bisitz   7379: .LC_Box {
1.911     bisitz   7380:   border: solid 1px $lg_border_color;
                   7381:   padding: 0 10px 10px 10px;
1.746     neumanie 7382: }
1.795     www      7383: 
1.1020    raeburn  7384: .LC_DocsBox {
                   7385:   border: solid 1px $lg_border_color;
                   7386:   padding: 0 0 10px 10px;
                   7387: }
                   7388: 
1.795     www      7389: .LC_AboutMe_Image {
1.911     bisitz   7390:   float:left;
                   7391:   margin-right:10px;
1.747     neumanie 7392: }
1.795     www      7393: 
                   7394: .LC_Clear_AboutMe_Image {
1.911     bisitz   7395:   clear:left;
1.747     neumanie 7396: }
1.795     www      7397: 
1.721     harmsja  7398: dl.LC_ListStyleClean dt {
1.911     bisitz   7399:   padding-right: 5px;
                   7400:   display: table-header-group;
1.693     droeschl 7401: }
                   7402: 
1.721     harmsja  7403: dl.LC_ListStyleClean dd {
1.911     bisitz   7404:   display: table-row;
1.693     droeschl 7405: }
                   7406: 
1.721     harmsja  7407: .LC_ListStyleClean,
                   7408: .LC_ListStyleSimple,
                   7409: .LC_ListStyleNormal,
1.795     www      7410: .LC_ListStyleSpecial {
1.911     bisitz   7411:   /* display:block; */
                   7412:   list-style-position: inside;
                   7413:   list-style-type: none;
                   7414:   overflow: hidden;
                   7415:   padding: 0;
1.693     droeschl 7416: }
                   7417: 
1.721     harmsja  7418: .LC_ListStyleSimple li,
                   7419: .LC_ListStyleSimple dd,
                   7420: .LC_ListStyleNormal li,
                   7421: .LC_ListStyleNormal dd,
                   7422: .LC_ListStyleSpecial li,
1.795     www      7423: .LC_ListStyleSpecial dd {
1.911     bisitz   7424:   margin: 0;
                   7425:   padding: 5px 5px 5px 10px;
                   7426:   clear: both;
1.693     droeschl 7427: }
                   7428: 
1.721     harmsja  7429: .LC_ListStyleClean li,
                   7430: .LC_ListStyleClean dd {
1.911     bisitz   7431:   padding-top: 0;
                   7432:   padding-bottom: 0;
1.693     droeschl 7433: }
                   7434: 
1.721     harmsja  7435: .LC_ListStyleSimple dd,
1.795     www      7436: .LC_ListStyleSimple li {
1.911     bisitz   7437:   border-bottom: solid 1px $lg_border_color;
1.693     droeschl 7438: }
                   7439: 
1.721     harmsja  7440: .LC_ListStyleSpecial li,
                   7441: .LC_ListStyleSpecial dd {
1.911     bisitz   7442:   list-style-type: none;
                   7443:   background-color: RGB(220, 220, 220);
                   7444:   margin-bottom: 4px;
1.693     droeschl 7445: }
                   7446: 
1.721     harmsja  7447: table.LC_SimpleTable {
1.911     bisitz   7448:   margin:5px;
                   7449:   border:solid 1px $lg_border_color;
1.795     www      7450: }
1.693     droeschl 7451: 
1.721     harmsja  7452: table.LC_SimpleTable tr {
1.911     bisitz   7453:   padding: 0;
                   7454:   border:solid 1px $lg_border_color;
1.693     droeschl 7455: }
1.795     www      7456: 
                   7457: table.LC_SimpleTable thead {
1.911     bisitz   7458:   background:rgb(220,220,220);
1.693     droeschl 7459: }
                   7460: 
1.721     harmsja  7461: div.LC_columnSection {
1.911     bisitz   7462:   display: block;
                   7463:   clear: both;
                   7464:   overflow: hidden;
                   7465:   margin: 0;
1.693     droeschl 7466: }
                   7467: 
1.721     harmsja  7468: div.LC_columnSection>* {
1.911     bisitz   7469:   float: left;
                   7470:   margin: 10px 20px 10px 0;
                   7471:   overflow:hidden;
1.693     droeschl 7472: }
1.721     harmsja  7473: 
1.795     www      7474: table em {
1.911     bisitz   7475:   font-weight: bold;
                   7476:   font-style: normal;
1.748     schulted 7477: }
1.795     www      7478: 
1.779     bisitz   7479: table.LC_tableBrowseRes,
1.795     www      7480: table.LC_tableOfContent {
1.911     bisitz   7481:   border:none;
                   7482:   border-spacing: 1px;
                   7483:   padding: 3px;
                   7484:   background-color: #FFFFFF;
                   7485:   font-size: 90%;
1.753     droeschl 7486: }
1.789     droeschl 7487: 
1.911     bisitz   7488: table.LC_tableOfContent {
                   7489:   border-collapse: collapse;
1.789     droeschl 7490: }
                   7491: 
1.771     droeschl 7492: table.LC_tableBrowseRes a,
1.768     schulted 7493: table.LC_tableOfContent a {
1.911     bisitz   7494:   background-color: transparent;
                   7495:   text-decoration: none;
1.753     droeschl 7496: }
                   7497: 
1.795     www      7498: table.LC_tableOfContent img {
1.911     bisitz   7499:   border: none;
                   7500:   height: 1.3em;
                   7501:   vertical-align: text-bottom;
                   7502:   margin-right: 0.3em;
1.753     droeschl 7503: }
1.757     schulted 7504: 
1.795     www      7505: a#LC_content_toolbar_firsthomework {
1.911     bisitz   7506:   background-image:url(/res/adm/pages/open-first-problem.gif);
1.774     ehlerst  7507: }
                   7508: 
1.795     www      7509: a#LC_content_toolbar_everything {
1.911     bisitz   7510:   background-image:url(/res/adm/pages/show-all.gif);
1.774     ehlerst  7511: }
                   7512: 
1.795     www      7513: a#LC_content_toolbar_uncompleted {
1.911     bisitz   7514:   background-image:url(/res/adm/pages/show-incomplete-problems.gif);
1.774     ehlerst  7515: }
                   7516: 
1.795     www      7517: #LC_content_toolbar_clearbubbles {
1.911     bisitz   7518:   background-image:url(/res/adm/pages/mark-discussionentries-read.gif);
1.774     ehlerst  7519: }
                   7520: 
1.795     www      7521: a#LC_content_toolbar_changefolder {
1.911     bisitz   7522:   background : url(/res/adm/pages/close-all-folders.gif) top center ;
1.757     schulted 7523: }
                   7524: 
1.795     www      7525: a#LC_content_toolbar_changefolder_toggled {
1.911     bisitz   7526:   background-image:url(/res/adm/pages/open-all-folders.gif);
1.757     schulted 7527: }
                   7528: 
1.1043    raeburn  7529: a#LC_content_toolbar_edittoplevel {
                   7530:   background-image:url(/res/adm/pages/edittoplevel.gif);
                   7531: }
                   7532: 
1.795     www      7533: ul#LC_toolbar li a:hover {
1.911     bisitz   7534:   background-position: bottom center;
1.757     schulted 7535: }
                   7536: 
1.795     www      7537: ul#LC_toolbar {
1.911     bisitz   7538:   padding: 0;
                   7539:   margin: 2px;
                   7540:   list-style:none;
                   7541:   position:relative;
                   7542:   background-color:white;
1.1082    raeburn  7543:   overflow: auto;
1.757     schulted 7544: }
                   7545: 
1.795     www      7546: ul#LC_toolbar li {
1.911     bisitz   7547:   border:1px solid white;
                   7548:   padding: 0;
                   7549:   margin: 0;
                   7550:   float: left;
                   7551:   display:inline;
                   7552:   vertical-align:middle;
1.1082    raeburn  7553:   white-space: nowrap;
1.911     bisitz   7554: }
1.757     schulted 7555: 
1.783     amueller 7556: 
1.795     www      7557: a.LC_toolbarItem {
1.911     bisitz   7558:   display:block;
                   7559:   padding: 0;
                   7560:   margin: 0;
                   7561:   height: 32px;
                   7562:   width: 32px;
                   7563:   color:white;
                   7564:   border: none;
                   7565:   background-repeat:no-repeat;
                   7566:   background-color:transparent;
1.757     schulted 7567: }
                   7568: 
1.915     droeschl 7569: ul.LC_funclist {
                   7570:     margin: 0;
                   7571:     padding: 0.5em 1em 0.5em 0;
                   7572: }
                   7573: 
1.933     droeschl 7574: ul.LC_funclist > li:first-child {
                   7575:     font-weight:bold; 
                   7576:     margin-left:0.8em;
                   7577: }
                   7578: 
1.915     droeschl 7579: ul.LC_funclist + ul.LC_funclist {
                   7580:     /* 
                   7581:        left border as a seperator if we have more than
                   7582:        one list 
                   7583:     */
                   7584:     border-left: 1px solid $sidebg;
                   7585:     /* 
                   7586:        this hides the left border behind the border of the 
                   7587:        outer box if element is wrapped to the next 'line' 
                   7588:     */
                   7589:     margin-left: -1px;
                   7590: }
                   7591: 
1.843     bisitz   7592: ul.LC_funclist li {
1.915     droeschl 7593:   display: inline;
1.782     bisitz   7594:   white-space: nowrap;
1.915     droeschl 7595:   margin: 0 0 0 25px;
                   7596:   line-height: 150%;
1.782     bisitz   7597: }
                   7598: 
1.974     wenzelju 7599: .LC_hidden {
                   7600:   display: none;
                   7601: }
                   7602: 
1.1030    www      7603: .LCmodal-overlay {
                   7604: 		position:fixed;
                   7605: 		top:0;
                   7606: 		right:0;
                   7607: 		bottom:0;
                   7608: 		left:0;
                   7609: 		height:100%;
                   7610: 		width:100%;
                   7611: 		margin:0;
                   7612: 		padding:0;
                   7613: 		background:#999;
                   7614: 		opacity:.75;
                   7615: 		filter: alpha(opacity=75);
                   7616: 		-moz-opacity: 0.75;
                   7617: 		z-index:101;
                   7618: }
                   7619: 
                   7620: * html .LCmodal-overlay {   
                   7621: 		position: absolute;
                   7622: 		height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
                   7623: }
                   7624: 
                   7625: .LCmodal-window {
                   7626: 		position:fixed;
                   7627: 		top:50%;
                   7628: 		left:50%;
                   7629: 		margin:0;
                   7630: 		padding:0;
                   7631: 		z-index:102;
                   7632: 	}
                   7633: 
                   7634: * html .LCmodal-window {
                   7635: 		position:absolute;
                   7636: }
                   7637: 
                   7638: .LCclose-window {
                   7639: 		position:absolute;
                   7640: 		width:32px;
                   7641: 		height:32px;
                   7642: 		right:8px;
                   7643: 		top:8px;
                   7644: 		background:transparent url('/res/adm/pages/process-stop.png') no-repeat scroll right top;
                   7645: 		text-indent:-99999px;
                   7646: 		overflow:hidden;
                   7647: 		cursor:pointer;
                   7648: }
                   7649: 
1.1100    raeburn  7650: /*
1.1231    damieng  7651:   styles used for response display
                   7652: */
                   7653: div.LC_radiofoil, div.LC_rankfoil {
                   7654:   margin: .5em 0em .5em 0em;
                   7655: }
                   7656: table.LC_itemgroup {
                   7657:   margin-top: 1em;
                   7658: }
                   7659: 
                   7660: /*
1.1100    raeburn  7661:   styles used by TTH when "Default set of options to pass to tth/m
                   7662:   when converting TeX" in course settings has been set
                   7663: 
                   7664:   option passed: -t
                   7665: 
                   7666: */
                   7667: 
                   7668: td div.comp { margin-top: -0.6ex; margin-bottom: -1ex;}
                   7669: td div.comb { margin-top: -0.6ex; margin-bottom: -.6ex;}
                   7670: td div.hrcomp { line-height: 0.9; margin-top: -0.8ex; margin-bottom: -1ex;}
                   7671: td div.norm {line-height:normal;}
                   7672: 
                   7673: /*
                   7674:   option passed -y3
                   7675: */
                   7676: 
                   7677: span.roman {font-family: serif; font-style: normal; font-weight: normal;}
                   7678: span.overacc2 {position: relative;  left: .8em; top: -1.2ex;}
                   7679: span.overacc1 {position: relative;  left: .6em; top: -1.2ex;}
                   7680: 
1.1230    damieng  7681: /*
                   7682:   sections with roles, for content only
                   7683: */
                   7684: section[class^="role-"] {
                   7685:   padding-left: 10px;
                   7686:   padding-right: 5px;
                   7687:   margin-top: 8px;
                   7688:   margin-bottom: 8px;
                   7689:   border: 1px solid #2A4;
                   7690:   border-radius: 5px;
                   7691:   box-shadow: 0px 1px 1px #BBB;
                   7692: }
                   7693: section[class^="role-"]>h1 {
                   7694:   position: relative;
                   7695:   margin: 0px;
                   7696:   padding-top: 10px;
                   7697:   padding-left: 40px;
                   7698: }
                   7699: section[class^="role-"]>h1:before {
                   7700:   position: absolute;
                   7701:   left: -5px;
                   7702:   top: 5px;
                   7703: }
                   7704: section.role-activity>h1:before {
                   7705:   content:url('/adm/daxe/images/section_icons/activity.png');
                   7706: }
                   7707: section.role-advice>h1:before {
                   7708:   content:url('/adm/daxe/images/section_icons/advice.png');
                   7709: }
                   7710: section.role-bibliography>h1:before {
                   7711:   content:url('/adm/daxe/images/section_icons/bibliography.png');
                   7712: }
                   7713: section.role-citation>h1:before {
                   7714:   content:url('/adm/daxe/images/section_icons/citation.png');
                   7715: }
                   7716: section.role-conclusion>h1:before {
                   7717:   content:url('/adm/daxe/images/section_icons/conclusion.png');
                   7718: }
                   7719: section.role-definition>h1:before {
                   7720:   content:url('/adm/daxe/images/section_icons/definition.png');
                   7721: }
                   7722: section.role-demonstration>h1:before {
                   7723:   content:url('/adm/daxe/images/section_icons/demonstration.png');
                   7724: }
                   7725: section.role-example>h1:before {
                   7726:   content:url('/adm/daxe/images/section_icons/example.png');
                   7727: }
                   7728: section.role-explanation>h1:before {
                   7729:   content:url('/adm/daxe/images/section_icons/explanation.png');
                   7730: }
                   7731: section.role-introduction>h1:before {
                   7732:   content:url('/adm/daxe/images/section_icons/introduction.png');
                   7733: }
                   7734: section.role-method>h1:before {
                   7735:   content:url('/adm/daxe/images/section_icons/method.png');
                   7736: }
                   7737: section.role-more_information>h1:before {
                   7738:   content:url('/adm/daxe/images/section_icons/more_information.png');
                   7739: }
                   7740: section.role-objectives>h1:before {
                   7741:   content:url('/adm/daxe/images/section_icons/objectives.png');
                   7742: }
                   7743: section.role-prerequisites>h1:before {
                   7744:   content:url('/adm/daxe/images/section_icons/prerequisites.png');
                   7745: }
                   7746: section.role-remark>h1:before {
                   7747:   content:url('/adm/daxe/images/section_icons/remark.png');
                   7748: }
                   7749: section.role-reminder>h1:before {
                   7750:   content:url('/adm/daxe/images/section_icons/reminder.png');
                   7751: }
                   7752: section.role-summary>h1:before {
                   7753:   content:url('/adm/daxe/images/section_icons/summary.png');
                   7754: }
                   7755: section.role-syntax>h1:before {
                   7756:   content:url('/adm/daxe/images/section_icons/syntax.png');
                   7757: }
                   7758: section.role-warning>h1:before {
                   7759:   content:url('/adm/daxe/images/section_icons/warning.png');
                   7760: }
                   7761: 
1.343     albertel 7762: END
                   7763: }
                   7764: 
1.306     albertel 7765: =pod
                   7766: 
                   7767: =item * &headtag()
                   7768: 
                   7769: Returns a uniform footer for LON-CAPA web pages.
                   7770: 
1.307     albertel 7771: Inputs: $title - optional title for the head
                   7772:         $head_extra - optional extra HTML to put inside the <head>
1.315     albertel 7773:         $args - optional arguments
1.319     albertel 7774:             force_register - if is true call registerurl so the remote is 
                   7775:                              informed
1.415     albertel 7776:             redirect       -> array ref of
                   7777:                                    1- seconds before redirect occurs
                   7778:                                    2- url to redirect to
                   7779:                                    3- whether the side effect should occur
1.315     albertel 7780:                            (side effect of setting 
                   7781:                                $env{'internal.head.redirect'} to the url 
                   7782:                                redirected too)
1.352     albertel 7783:             domain         -> force to color decorate a page for a specific
                   7784:                                domain
                   7785:             function       -> force usage of a specific rolish color scheme
                   7786:             bgcolor        -> override the default page bgcolor
1.460     albertel 7787:             no_auto_mt_title
                   7788:                            -> prevent &mt()ing the title arg
1.464     albertel 7789: 
1.306     albertel 7790: =cut
                   7791: 
                   7792: sub headtag {
1.313     albertel 7793:     my ($title,$head_extra,$args) = @_;
1.306     albertel 7794:     
1.363     albertel 7795:     my $function = $args->{'function'} || &get_users_function();
                   7796:     my $domain   = $args->{'domain'}   || &determinedomain();
                   7797:     my $bgcolor  = $args->{'bgcolor'}  || &designparm($function.'.pgbg',$domain);
1.1154    raeburn  7798:     my $httphost = $args->{'use_absolute'};
1.418     albertel 7799:     my $url = join(':',$env{'user.name'},$env{'user.domain'},
1.458     albertel 7800: 		   $Apache::lonnet::perlvar{'lonVersion'},
1.531     albertel 7801: 		   #time(),
1.418     albertel 7802: 		   $env{'environment.color.timestamp'},
1.363     albertel 7803: 		   $function,$domain,$bgcolor);
                   7804: 
1.369     www      7805:     $url = '/adm/css/'.&escape($url).'.css';
1.363     albertel 7806: 
1.308     albertel 7807:     my $result =
                   7808: 	'<head>'.
1.1160    raeburn  7809: 	&font_settings($args);
1.319     albertel 7810: 
1.1188    raeburn  7811:     my $inhibitprint;
                   7812:     if ($args->{'print_suppress'}) {
                   7813:         $inhibitprint = &print_suppression();
                   7814:     }
1.1064    raeburn  7815: 
1.461     albertel 7816:     if (!$args->{'frameset'}) {
                   7817: 	$result .= &Apache::lonhtmlcommon::htmlareaheaders();
                   7818:     }
1.962     droeschl 7819:     if ($args->{'force_register'} && $env{'request.noversionuri'} !~ m{^/res/adm/pages/}) {
                   7820:         $result .= Apache::lonxml::display_title();
1.319     albertel 7821:     }
1.436     albertel 7822:     if (!$args->{'no_nav_bar'} 
                   7823: 	&& !$args->{'only_body'}
                   7824: 	&& !$args->{'frameset'}) {
1.1154    raeburn  7825: 	$result .= &help_menu_js($httphost);
1.1032    www      7826:         $result.=&modal_window();
1.1038    www      7827:         $result.=&togglebox_script();
1.1034    www      7828:         $result.=&wishlist_window();
1.1041    www      7829:         $result.=&LCprogressbarUpdate_script();
1.1034    www      7830:     } else {
                   7831:         if ($args->{'add_modal'}) {
                   7832:            $result.=&modal_window();
                   7833:         }
                   7834:         if ($args->{'add_wishlist'}) {
                   7835:            $result.=&wishlist_window();
                   7836:         }
1.1038    www      7837:         if ($args->{'add_togglebox'}) {
                   7838:            $result.=&togglebox_script();
                   7839:         }
1.1041    www      7840:         if ($args->{'add_progressbar'}) {
                   7841:            $result.=&LCprogressbarUpdate_script();
                   7842:         }
1.436     albertel 7843:     }
1.314     albertel 7844:     if (ref($args->{'redirect'})) {
1.414     albertel 7845: 	my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
1.315     albertel 7846: 	$url = &Apache::lonenc::check_encrypt($url);
1.414     albertel 7847: 	if (!$inhibit_continue) {
                   7848: 	    $env{'internal.head.redirect'} = $url;
                   7849: 	}
1.313     albertel 7850: 	$result.=<<ADDMETA
                   7851: <meta http-equiv="pragma" content="no-cache" />
1.344     albertel 7852: <meta http-equiv="Refresh" content="$time; url=$url" />
1.313     albertel 7853: ADDMETA
1.1210    raeburn  7854:     } else {
                   7855:         unless (($args->{'frameset'}) || ($args->{'js_ready'}) || ($args->{'only_body'}) || ($args->{'no_nav_bar'})) {
                   7856:             my $requrl = $env{'request.uri'};
                   7857:             if ($requrl eq '') {
                   7858:                 $requrl = $ENV{'REQUEST_URI'};
                   7859:                 $requrl =~ s/\?.+$//;
                   7860:             }
                   7861:             unless (($requrl =~ m{^/adm/(?:switchserver|login|authenticate|logout|groupsort|cleanup|helper|slotrequest|grades)(\?|$)}) ||
                   7862:                     (($requrl =~ m{^/res/}) && (($env{'form.submitted'} eq 'scantron') ||
                   7863:                      ($env{'form.grade_symb'}) || ($Apache::lonhomework::scantronmode)))) {
                   7864:                 my $dom_in_use = $Apache::lonnet::perlvar{'lonDefDomain'};
                   7865:                 unless (&Apache::lonnet::allowed('mau',$dom_in_use)) {
                   7866:                     my %domdefs = &Apache::lonnet::get_domain_defaults($dom_in_use);
                   7867:                     if (ref($domdefs{'offloadnow'}) eq 'HASH') {
                   7868:                         my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
                   7869:                         if ($domdefs{'offloadnow'}{$lonhost}) {
                   7870:                             my $newserver = &Apache::lonnet::spareserver(30000,undef,1,$dom_in_use);
                   7871:                             if (($newserver) && ($newserver ne $lonhost)) {
                   7872:                                 my $numsec = 5;
                   7873:                                 my $timeout = $numsec * 1000;
                   7874:                                 my ($newurl,$locknum,%locks,$msg);
                   7875:                                 if ($env{'request.role.adv'}) {
                   7876:                                     ($locknum,%locks) = &Apache::lonnet::get_locks();
                   7877:                                 }
                   7878:                                 my $disable_submit = 0;
                   7879:                                 if ($requrl =~ /$LONCAPA::assess_re/) {
                   7880:                                     $disable_submit = 1;
                   7881:                                 }
                   7882:                                 if ($locknum) {
                   7883:                                     my @lockinfo = sort(values(%locks));
                   7884:                                     $msg = &mt('Once the following tasks are complete: ')."\\n".
                   7885:                                            join(", ",sort(values(%locks)))."\\n".
                   7886:                                            &mt('your session will be transferred to a different server, after you click "Roles".');
                   7887:                                 } else {
                   7888:                                     if (($requrl =~ m{^/res/}) && ($env{'form.submitted'} =~ /^part_/)) {
                   7889:                                         $msg = &mt('Your LON-CAPA submission has been recorded')."\\n";
                   7890:                                     }
                   7891:                                     $msg .= &mt('Your current LON-CAPA session will be transferred to a different server in [quant,_1,second].',$numsec);
                   7892:                                     $newurl = '/adm/switchserver?otherserver='.$newserver;
                   7893:                                     if (($env{'request.role'}) && ($env{'request.role'} ne 'cm')) {
                   7894:                                         $newurl .= '&role='.$env{'request.role'};
                   7895:                                     }
                   7896:                                     if ($env{'request.symb'}) {
                   7897:                                         $newurl .= '&symb='.$env{'request.symb'};
                   7898:                                     } else {
                   7899:                                         $newurl .= '&origurl='.$requrl;
                   7900:                                     }
                   7901:                                 }
1.1222    damieng  7902:                                 &js_escape(\$msg);
1.1210    raeburn  7903:                                 $result.=<<OFFLOAD
                   7904: <meta http-equiv="pragma" content="no-cache" />
                   7905: <script type="text/javascript">
1.1215    raeburn  7906: // <![CDATA[
1.1210    raeburn  7907: function LC_Offload_Now() {
                   7908:     var dest = "$newurl";
                   7909:     if (dest != '') {
                   7910:         window.location.href="$newurl";
                   7911:     }
                   7912: }
1.1214    raeburn  7913: \$(document).ready(function () {
                   7914:     window.alert('$msg');
                   7915:     if ($disable_submit) {
1.1210    raeburn  7916:         \$(".LC_hwk_submit").prop("disabled", true);
                   7917:         \$( ".LC_textline" ).prop( "readonly", "readonly");
1.1214    raeburn  7918:     }
                   7919:     setTimeout('LC_Offload_Now()', $timeout);
                   7920: });
1.1215    raeburn  7921: // ]]>
1.1210    raeburn  7922: </script>
                   7923: OFFLOAD
                   7924:                             }
                   7925:                         }
                   7926:                     }
                   7927:                 }
                   7928:             }
                   7929:         }
1.313     albertel 7930:     }
1.306     albertel 7931:     if (!defined($title)) {
                   7932: 	$title = 'The LearningOnline Network with CAPA';
                   7933:     }
1.460     albertel 7934:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
                   7935:     $result .= '<title> LON-CAPA '.$title.'</title>'
1.1168    raeburn  7936: 	.'<link rel="stylesheet" type="text/css" href="'.$url.'"';
                   7937:     if (!$args->{'frameset'}) {
                   7938:         $result .= ' /';
                   7939:     }
                   7940:     $result .= '>' 
1.1064    raeburn  7941:         .$inhibitprint
1.414     albertel 7942: 	.$head_extra;
1.1137    raeburn  7943:     if ($env{'browser.mobile'}) {
                   7944:         $result .= '
                   7945: <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
                   7946: <meta name="apple-mobile-web-app-capable" content="yes" />';
                   7947:     }
1.962     droeschl 7948:     return $result.'</head>';
1.306     albertel 7949: }
                   7950: 
                   7951: =pod
                   7952: 
1.340     albertel 7953: =item * &font_settings()
                   7954: 
                   7955: Returns neccessary <meta> to set the proper encoding
                   7956: 
1.1160    raeburn  7957: Inputs: optional reference to HASH -- $args passed to &headtag()
1.340     albertel 7958: 
                   7959: =cut
                   7960: 
                   7961: sub font_settings {
1.1160    raeburn  7962:     my ($args) = @_;
1.340     albertel 7963:     my $headerstring='';
1.1160    raeburn  7964:     if ((!$env{'browser.mathml'} && $env{'browser.unicode'}) ||
                   7965:         ((ref($args) eq 'HASH') && ($args->{'browser.unicode'}))) {
1.1168    raeburn  7966:         $headerstring.=
                   7967:             '<meta http-equiv="Content-Type" content="text/html; charset=utf-8"';
                   7968:         if (!$args->{'frameset'}) {
                   7969: 	    $headerstring.= ' /';
                   7970:         }
                   7971: 	$headerstring .= '>'."\n";
1.340     albertel 7972:     }
                   7973:     return $headerstring;
                   7974: }
                   7975: 
1.341     albertel 7976: =pod
                   7977: 
1.1064    raeburn  7978: =item * &print_suppression()
                   7979: 
                   7980: In course context returns css which causes the body to be blank when media="print",
                   7981: if printout generation is unavailable for the current resource.
                   7982: 
                   7983: This could be because:
                   7984: 
                   7985: (a) printstartdate is in the future
                   7986: 
                   7987: (b) printenddate is in the past
                   7988: 
                   7989: (c) there is an active exam block with "printout"
                   7990: functionality blocked
                   7991: 
                   7992: Users with pav, pfo or evb privileges are exempt.
                   7993: 
                   7994: Inputs: none
                   7995: 
                   7996: =cut
                   7997: 
                   7998: 
                   7999: sub print_suppression {
                   8000:     my $noprint;
                   8001:     if ($env{'request.course.id'}) {
                   8002:         my $scope = $env{'request.course.id'};
                   8003:         if ((&Apache::lonnet::allowed('pav',$scope)) ||
                   8004:             (&Apache::lonnet::allowed('pfo',$scope))) {
                   8005:             return;
                   8006:         }
                   8007:         if ($env{'request.course.sec'} ne '') {
                   8008:             $scope .= "/$env{'request.course.sec'}";
                   8009:             if ((&Apache::lonnet::allowed('pav',$scope)) ||
                   8010:                 (&Apache::lonnet::allowed('pfo',$scope))) {
1.1065    raeburn  8011:                 return;
1.1064    raeburn  8012:             }
                   8013:         }
                   8014:         my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   8015:         my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1189    raeburn  8016:         my $blocked = &blocking_status('printout',$cnum,$cdom,undef,1);
1.1064    raeburn  8017:         if ($blocked) {
                   8018:             my $checkrole = "cm./$cdom/$cnum";
                   8019:             if ($env{'request.course.sec'} ne '') {
                   8020:                 $checkrole .= "/$env{'request.course.sec'}";
                   8021:             }
                   8022:             unless ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
                   8023:                     ($env{'request.role'} !~ m{^st\./$cdom/$cnum})) {
                   8024:                 $noprint = 1;
                   8025:             }
                   8026:         }
                   8027:         unless ($noprint) {
                   8028:             my $symb = &Apache::lonnet::symbread();
                   8029:             if ($symb ne '') {
                   8030:                 my $navmap = Apache::lonnavmaps::navmap->new();
                   8031:                 if (ref($navmap)) {
                   8032:                     my $res = $navmap->getBySymb($symb);
                   8033:                     if (ref($res)) {
                   8034:                         if (!$res->resprintable()) {
                   8035:                             $noprint = 1;
                   8036:                         }
                   8037:                     }
                   8038:                 }
                   8039:             }
                   8040:         }
                   8041:         if ($noprint) {
                   8042:             return <<"ENDSTYLE";
                   8043: <style type="text/css" media="print">
                   8044:     body { display:none }
                   8045: </style>
                   8046: ENDSTYLE
                   8047:         }
                   8048:     }
                   8049:     return;
                   8050: }
                   8051: 
                   8052: =pod
                   8053: 
1.341     albertel 8054: =item * &xml_begin()
                   8055: 
                   8056: Returns the needed doctype and <html>
                   8057: 
                   8058: Inputs: none
                   8059: 
                   8060: =cut
                   8061: 
                   8062: sub xml_begin {
1.1168    raeburn  8063:     my ($is_frameset) = @_;
1.341     albertel 8064:     my $output='';
                   8065: 
                   8066:     if ($env{'browser.mathml'}) {
                   8067: 	$output='<?xml version="1.0"?>'
                   8068:             #.'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'."\n"
                   8069: #            .'<!DOCTYPE html SYSTEM "/adm/MathML/mathml.dtd" '
                   8070:             
                   8071: #	    .'<!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">] >'
                   8072: 	    .'<!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">'
                   8073:             .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" ' 
                   8074: 	    .'xmlns="http://www.w3.org/1999/xhtml">';
1.1168    raeburn  8075:     } elsif ($is_frameset) {
                   8076:         $output='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'."\n".
                   8077:                 '<html>'."\n";
1.341     albertel 8078:     } else {
1.1168    raeburn  8079: 	$output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n".
                   8080:                 '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
1.341     albertel 8081:     }
                   8082:     return $output;
                   8083: }
1.340     albertel 8084: 
                   8085: =pod
                   8086: 
1.306     albertel 8087: =item * &start_page()
                   8088: 
                   8089: Returns a complete <html> .. <body> section for LON-CAPA web pages.
                   8090: 
1.648     raeburn  8091: Inputs:
                   8092: 
                   8093: =over 4
                   8094: 
                   8095: $title - optional title for the page
                   8096: 
                   8097: $head_extra - optional extra HTML to incude inside the <head>
                   8098: 
                   8099: $args - additional optional args supported are:
                   8100: 
                   8101: =over 8
                   8102: 
                   8103:              only_body      -> is true will set &bodytag() onlybodytag
1.317     albertel 8104:                                     arg on
1.814     bisitz   8105:              no_nav_bar     -> is true will set &bodytag() no_nav_bar arg on
1.648     raeburn  8106:              add_entries    -> additional attributes to add to the  <body>
                   8107:              domain         -> force to color decorate a page for a 
1.317     albertel 8108:                                     specific domain
1.648     raeburn  8109:              function       -> force usage of a specific rolish color
1.317     albertel 8110:                                     scheme
1.648     raeburn  8111:              redirect       -> see &headtag()
                   8112:              bgcolor        -> override the default page bg color
                   8113:              js_ready       -> return a string ready for being used in 
1.317     albertel 8114:                                     a javascript writeln
1.648     raeburn  8115:              html_encode    -> return a string ready for being used in 
1.320     albertel 8116:                                     a html attribute
1.648     raeburn  8117:              force_register -> if is true will turn on the &bodytag()
1.317     albertel 8118:                                     $forcereg arg
1.648     raeburn  8119:              frameset       -> if true will start with a <frameset>
1.330     albertel 8120:                                     rather than <body>
1.648     raeburn  8121:              skip_phases    -> hash ref of 
1.338     albertel 8122:                                     head -> skip the <html><head> generation
                   8123:                                     body -> skip all <body> generation
1.648     raeburn  8124:              no_auto_mt_title -> prevent &mt()ing the title arg
1.867     kalberla 8125:              bread_crumbs ->             Array containing breadcrumbs
1.983     raeburn  8126:              bread_crumbs_component ->  if exists show it as headline else show only the breadcrumbs
1.1096    raeburn  8127:              group          -> includes the current group, if page is for a 
                   8128:                                specific group  
1.361     albertel 8129: 
1.648     raeburn  8130: =back
1.460     albertel 8131: 
1.648     raeburn  8132: =back
1.562     albertel 8133: 
1.306     albertel 8134: =cut
                   8135: 
                   8136: sub start_page {
1.309     albertel 8137:     my ($title,$head_extra,$args) = @_;
1.318     albertel 8138:     #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
1.319     albertel 8139: 
1.315     albertel 8140:     $env{'internal.start_page'}++;
1.1096    raeburn  8141:     my ($result,@advtools);
1.964     droeschl 8142: 
1.338     albertel 8143:     if (! exists($args->{'skip_phases'}{'head'}) ) {
1.1168    raeburn  8144:         $result .= &xml_begin($args->{'frameset'}) . &headtag($title, $head_extra, $args);
1.338     albertel 8145:     }
                   8146:     
                   8147:     if (! exists($args->{'skip_phases'}{'body'}) ) {
                   8148: 	if ($args->{'frameset'}) {
                   8149: 	    my $attr_string = &make_attr_string($args->{'force_register'},
                   8150: 						$args->{'add_entries'});
                   8151: 	    $result .= "\n<frameset $attr_string>\n";
1.831     bisitz   8152:         } else {
                   8153:             $result .=
                   8154:                 &bodytag($title, 
                   8155:                          $args->{'function'},       $args->{'add_entries'},
                   8156:                          $args->{'only_body'},      $args->{'domain'},
                   8157:                          $args->{'force_register'}, $args->{'no_nav_bar'},
1.1096    raeburn  8158:                          $args->{'bgcolor'},        $args,
                   8159:                          \@advtools);
1.831     bisitz   8160:         }
1.330     albertel 8161:     }
1.338     albertel 8162: 
1.315     albertel 8163:     if ($args->{'js_ready'}) {
1.713     kaisler  8164: 		$result = &js_ready($result);
1.315     albertel 8165:     }
1.320     albertel 8166:     if ($args->{'html_encode'}) {
1.713     kaisler  8167: 		$result = &html_encode($result);
                   8168:     }
                   8169: 
1.813     bisitz   8170:     # Preparation for new and consistent functionlist at top of screen
                   8171:     # if ($args->{'functionlist'}) {
                   8172:     #            $result .= &build_functionlist();
                   8173:     #}
                   8174: 
1.964     droeschl 8175:     # Don't add anything more if only_body wanted or in const space
                   8176:     return $result if    $args->{'only_body'} 
                   8177:                       || $env{'request.state'} eq 'construct';
1.813     bisitz   8178: 
                   8179:     #Breadcrumbs
1.758     kaisler  8180:     if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) {
                   8181: 		&Apache::lonhtmlcommon::clear_breadcrumbs();
                   8182: 		#if any br links exists, add them to the breadcrumbs
                   8183: 		if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') {         
                   8184: 			foreach my $crumb (@{$args->{'bread_crumbs'}}){
                   8185: 				&Apache::lonhtmlcommon::add_breadcrumb($crumb);
                   8186: 			}
                   8187: 		}
1.1096    raeburn  8188:                 # if @advtools array contains items add then to the breadcrumbs
                   8189:                 if (@advtools > 0) {
                   8190:                     &Apache::lonmenu::advtools_crumbs(@advtools);
                   8191:                 }
1.758     kaisler  8192: 
                   8193: 		#if bread_crumbs_component exists show it as headline else show only the breadcrumbs
                   8194: 		if(exists($args->{'bread_crumbs_component'})){
                   8195: 			$result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'});
                   8196: 		}else{
                   8197: 			$result .= &Apache::lonhtmlcommon::breadcrumbs();
                   8198: 		}
1.320     albertel 8199:     }
1.315     albertel 8200:     return $result;
1.306     albertel 8201: }
                   8202: 
                   8203: sub end_page {
1.315     albertel 8204:     my ($args) = @_;
                   8205:     $env{'internal.end_page'}++;
1.330     albertel 8206:     my $result;
1.335     albertel 8207:     if ($args->{'discussion'}) {
                   8208: 	my ($target,$parser);
                   8209: 	if (ref($args->{'discussion'})) {
                   8210: 	    ($target,$parser) =($args->{'discussion'}{'target'},
                   8211: 				$args->{'discussion'}{'parser'});
                   8212: 	}
                   8213: 	$result .= &Apache::lonxml::xmlend($target,$parser);
                   8214:     }
1.330     albertel 8215:     if ($args->{'frameset'}) {
                   8216: 	$result .= '</frameset>';
                   8217:     } else {
1.635     raeburn  8218: 	$result .= &endbodytag($args);
1.330     albertel 8219:     }
1.1080    raeburn  8220:     unless ($args->{'notbody'}) {
                   8221:         $result .= "\n</html>";
                   8222:     }
1.330     albertel 8223: 
1.315     albertel 8224:     if ($args->{'js_ready'}) {
1.317     albertel 8225: 	$result = &js_ready($result);
1.315     albertel 8226:     }
1.335     albertel 8227: 
1.320     albertel 8228:     if ($args->{'html_encode'}) {
                   8229: 	$result = &html_encode($result);
                   8230:     }
1.335     albertel 8231: 
1.315     albertel 8232:     return $result;
                   8233: }
                   8234: 
1.1034    www      8235: sub wishlist_window {
                   8236:     return(<<'ENDWISHLIST');
1.1046    raeburn  8237: <script type="text/javascript">
1.1034    www      8238: // <![CDATA[
                   8239: // <!-- BEGIN LON-CAPA Internal
                   8240: function set_wishlistlink(title, path) {
                   8241:     if (!title) {
                   8242:         title = document.title;
                   8243:         title = title.replace(/^LON-CAPA /,'');
                   8244:     }
1.1175    raeburn  8245:     title = encodeURIComponent(title);
1.1203    raeburn  8246:     title = title.replace("'","\\\'");
1.1034    www      8247:     if (!path) {
                   8248:         path = location.pathname;
                   8249:     }
1.1175    raeburn  8250:     path = encodeURIComponent(path);
1.1203    raeburn  8251:     path = path.replace("'","\\\'");
1.1034    www      8252:     Win = window.open('/adm/wishlist?mode=newLink&setTitle='+title+'&setPath='+path,
                   8253:                       'wishlistNewLink','width=560,height=350,scrollbars=0');
                   8254: }
                   8255: // END LON-CAPA Internal -->
                   8256: // ]]>
                   8257: </script>
                   8258: ENDWISHLIST
                   8259: }
                   8260: 
1.1030    www      8261: sub modal_window {
                   8262:     return(<<'ENDMODAL');
1.1046    raeburn  8263: <script type="text/javascript">
1.1030    www      8264: // <![CDATA[
                   8265: // <!-- BEGIN LON-CAPA Internal
                   8266: var modalWindow = {
                   8267: 	parent:"body",
                   8268: 	windowId:null,
                   8269: 	content:null,
                   8270: 	width:null,
                   8271: 	height:null,
                   8272: 	close:function()
                   8273: 	{
                   8274: 	        $(".LCmodal-window").remove();
                   8275: 	        $(".LCmodal-overlay").remove();
                   8276: 	},
                   8277: 	open:function()
                   8278: 	{
                   8279: 		var modal = "";
                   8280: 		modal += "<div class=\"LCmodal-overlay\"></div>";
                   8281: 		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;\">";
                   8282: 		modal += this.content;
                   8283: 		modal += "</div>";	
                   8284: 
                   8285: 		$(this.parent).append(modal);
                   8286: 
                   8287: 		$(".LCmodal-window").append("<a class=\"LCclose-window\"></a>");
                   8288: 		$(".LCclose-window").click(function(){modalWindow.close();});
                   8289: 		$(".LCmodal-overlay").click(function(){modalWindow.close();});
                   8290: 	}
                   8291: };
1.1140    raeburn  8292: 	var openMyModal = function(source,width,height,scrolling,transparency,style)
1.1030    www      8293: 	{
1.1203    raeburn  8294:                 source = source.replace("'","&#39;");
1.1030    www      8295: 		modalWindow.windowId = "myModal";
                   8296: 		modalWindow.width = width;
                   8297: 		modalWindow.height = height;
1.1196    raeburn  8298: 		modalWindow.content = "<iframe width='"+width+"' height='"+height+"' frameborder='0' scrolling='"+scrolling+"' allowtransparency='"+transparency+"' src='" + source + "' style='"+style+"'></iframe>";
1.1030    www      8299: 		modalWindow.open();
1.1208    raeburn  8300: 	};
1.1030    www      8301: // END LON-CAPA Internal -->
                   8302: // ]]>
                   8303: </script>
                   8304: ENDMODAL
                   8305: }
                   8306: 
                   8307: sub modal_link {
1.1140    raeburn  8308:     my ($link,$linktext,$width,$height,$target,$scrolling,$title,$transparency,$style)=@_;
1.1030    www      8309:     unless ($width) { $width=480; }
                   8310:     unless ($height) { $height=400; }
1.1031    www      8311:     unless ($scrolling) { $scrolling='yes'; }
1.1140    raeburn  8312:     unless ($transparency) { $transparency='true'; }
                   8313: 
1.1074    raeburn  8314:     my $target_attr;
                   8315:     if (defined($target)) {
                   8316:         $target_attr = 'target="'.$target.'"';
                   8317:     }
                   8318:     return <<"ENDLINK";
1.1140    raeburn  8319: <a href="$link" $target_attr title="$title" onclick="javascript:openMyModal('$link',$width,$height,'$scrolling','$transparency','$style'); return false;">
1.1074    raeburn  8320:            $linktext</a>
                   8321: ENDLINK
1.1030    www      8322: }
                   8323: 
1.1032    www      8324: sub modal_adhoc_script {
                   8325:     my ($funcname,$width,$height,$content)=@_;
                   8326:     return (<<ENDADHOC);
1.1046    raeburn  8327: <script type="text/javascript">
1.1032    www      8328: // <![CDATA[
                   8329:         var $funcname = function()
                   8330:         {
                   8331:                 modalWindow.windowId = "myModal";
                   8332:                 modalWindow.width = $width;
                   8333:                 modalWindow.height = $height;
                   8334:                 modalWindow.content = '$content';
                   8335:                 modalWindow.open();
                   8336:         };  
                   8337: // ]]>
                   8338: </script>
                   8339: ENDADHOC
                   8340: }
                   8341: 
1.1041    www      8342: sub modal_adhoc_inner {
                   8343:     my ($funcname,$width,$height,$content)=@_;
                   8344:     my $innerwidth=$width-20;
                   8345:     $content=&js_ready(
1.1140    raeburn  8346:                  &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}).
                   8347:                  &start_scrollbox($width.'px',$innerwidth.'px',$height.'px','myModal','#FFFFFF',undef,1).
                   8348:                  $content.
1.1041    www      8349:                  &end_scrollbox().
1.1140    raeburn  8350:                  &end_page()
1.1041    www      8351:              );
                   8352:     return &modal_adhoc_script($funcname,$width,$height,$content);
                   8353: }
                   8354: 
                   8355: sub modal_adhoc_window {
                   8356:     my ($funcname,$width,$height,$content,$linktext)=@_;
                   8357:     return &modal_adhoc_inner($funcname,$width,$height,$content).
                   8358:            "<a href=\"javascript:$funcname();void(0);\">".$linktext."</a>";
                   8359: }
                   8360: 
                   8361: sub modal_adhoc_launch {
                   8362:     my ($funcname,$width,$height,$content)=@_;
                   8363:     return &modal_adhoc_inner($funcname,$width,$height,$content).(<<ENDLAUNCH);
                   8364: <script type="text/javascript">
                   8365: // <![CDATA[
                   8366: $funcname();
                   8367: // ]]>
                   8368: </script>
                   8369: ENDLAUNCH
                   8370: }
                   8371: 
                   8372: sub modal_adhoc_close {
                   8373:     return (<<ENDCLOSE);
                   8374: <script type="text/javascript">
                   8375: // <![CDATA[
                   8376: modalWindow.close();
                   8377: // ]]>
                   8378: </script>
                   8379: ENDCLOSE
                   8380: }
                   8381: 
1.1038    www      8382: sub togglebox_script {
                   8383:    return(<<ENDTOGGLE);
                   8384: <script type="text/javascript"> 
                   8385: // <![CDATA[
                   8386: function LCtoggleDisplay(id,hidetext,showtext) {
                   8387:    link = document.getElementById(id + "link").childNodes[0];
                   8388:    with (document.getElementById(id).style) {
                   8389:       if (display == "none" ) {
                   8390:           display = "inline";
                   8391:           link.nodeValue = hidetext;
                   8392:         } else {
                   8393:           display = "none";
                   8394:           link.nodeValue = showtext;
                   8395:        }
                   8396:    }
                   8397: }
                   8398: // ]]>
                   8399: </script>
                   8400: ENDTOGGLE
                   8401: }
                   8402: 
1.1039    www      8403: sub start_togglebox {
                   8404:     my ($id,$heading,$headerbg,$hidetext,$showtext)=@_;
                   8405:     unless ($heading) { $heading=''; } else { $heading.=' '; }
                   8406:     unless ($showtext) { $showtext=&mt('show'); }
                   8407:     unless ($hidetext) { $hidetext=&mt('hide'); }
                   8408:     unless ($headerbg) { $headerbg='#FFFFFF'; }
                   8409:     return &start_data_table().
                   8410:            &start_data_table_header_row().
                   8411:            '<td bgcolor="'.$headerbg.'">'.$heading.
                   8412:            '[<a id="'.$id.'link" href="javascript:LCtoggleDisplay(\''.$id.'\',\''.$hidetext.'\',\''.
                   8413:            $showtext.'\')">'.$showtext.'</a>]</td>'.
                   8414:            &end_data_table_header_row().
                   8415:            '<tr id="'.$id.'" style="display:none""><td>';
                   8416: }
                   8417: 
                   8418: sub end_togglebox {
                   8419:     return '</td></tr>'.&end_data_table();
                   8420: }
                   8421: 
1.1041    www      8422: sub LCprogressbar_script {
1.1045    www      8423:    my ($id)=@_;
1.1041    www      8424:    return(<<ENDPROGRESS);
                   8425: <script type="text/javascript">
                   8426: // <![CDATA[
1.1045    www      8427: \$('#progressbar$id').progressbar({
1.1041    www      8428:   value: 0,
                   8429:   change: function(event, ui) {
                   8430:     var newVal = \$(this).progressbar('option', 'value');
                   8431:     \$('.pblabel', this).text(LCprogressTxt);
                   8432:   }
                   8433: });
                   8434: // ]]>
                   8435: </script>
                   8436: ENDPROGRESS
                   8437: }
                   8438: 
                   8439: sub LCprogressbarUpdate_script {
                   8440:    return(<<ENDPROGRESSUPDATE);
                   8441: <style type="text/css">
                   8442: .ui-progressbar { position:relative; }
                   8443: .pblabel { position: absolute; width: 100%; text-align: center; line-height: 1.9em; }
                   8444: </style>
                   8445: <script type="text/javascript">
                   8446: // <![CDATA[
1.1045    www      8447: var LCprogressTxt='---';
                   8448: 
                   8449: function LCupdateProgress(percent,progresstext,id) {
1.1041    www      8450:    LCprogressTxt=progresstext;
1.1045    www      8451:    \$('#progressbar'+id).progressbar('value',percent);
1.1041    www      8452: }
                   8453: // ]]>
                   8454: </script>
                   8455: ENDPROGRESSUPDATE
                   8456: }
                   8457: 
1.1042    www      8458: my $LClastpercent;
1.1045    www      8459: my $LCidcnt;
                   8460: my $LCcurrentid;
1.1042    www      8461: 
1.1041    www      8462: sub LCprogressbar {
1.1042    www      8463:     my ($r)=(@_);
                   8464:     $LClastpercent=0;
1.1045    www      8465:     $LCidcnt++;
                   8466:     $LCcurrentid=$$.'_'.$LCidcnt;
1.1041    www      8467:     my $starting=&mt('Starting');
                   8468:     my $content=(<<ENDPROGBAR);
1.1045    www      8469:   <div id="progressbar$LCcurrentid">
1.1041    www      8470:     <span class="pblabel">$starting</span>
                   8471:   </div>
                   8472: ENDPROGBAR
1.1045    www      8473:     &r_print($r,$content.&LCprogressbar_script($LCcurrentid));
1.1041    www      8474: }
                   8475: 
                   8476: sub LCprogressbarUpdate {
1.1042    www      8477:     my ($r,$val,$text)=@_;
                   8478:     unless ($val) { 
                   8479:        if ($LClastpercent) {
                   8480:            $val=$LClastpercent;
                   8481:        } else {
                   8482:            $val=0;
                   8483:        }
                   8484:     }
1.1041    www      8485:     if ($val<0) { $val=0; }
                   8486:     if ($val>100) { $val=0; }
1.1042    www      8487:     $LClastpercent=$val;
1.1041    www      8488:     unless ($text) { $text=$val.'%'; }
                   8489:     $text=&js_ready($text);
1.1044    www      8490:     &r_print($r,<<ENDUPDATE);
1.1041    www      8491: <script type="text/javascript">
                   8492: // <![CDATA[
1.1045    www      8493: LCupdateProgress($val,'$text','$LCcurrentid');
1.1041    www      8494: // ]]>
                   8495: </script>
                   8496: ENDUPDATE
1.1035    www      8497: }
                   8498: 
1.1042    www      8499: sub LCprogressbarClose {
                   8500:     my ($r)=@_;
                   8501:     $LClastpercent=0;
1.1044    www      8502:     &r_print($r,<<ENDCLOSE);
1.1042    www      8503: <script type="text/javascript">
                   8504: // <![CDATA[
1.1045    www      8505: \$("#progressbar$LCcurrentid").hide('slow'); 
1.1042    www      8506: // ]]>
                   8507: </script>
                   8508: ENDCLOSE
1.1044    www      8509: }
                   8510: 
                   8511: sub r_print {
                   8512:     my ($r,$to_print)=@_;
                   8513:     if ($r) {
                   8514:       $r->print($to_print);
                   8515:       $r->rflush();
                   8516:     } else {
                   8517:       print($to_print);
                   8518:     }
1.1042    www      8519: }
                   8520: 
1.320     albertel 8521: sub html_encode {
                   8522:     my ($result) = @_;
                   8523: 
1.322     albertel 8524:     $result = &HTML::Entities::encode($result,'<>&"');
1.320     albertel 8525:     
                   8526:     return $result;
                   8527: }
1.1044    www      8528: 
1.317     albertel 8529: sub js_ready {
                   8530:     my ($result) = @_;
                   8531: 
1.323     albertel 8532:     $result =~ s/[\n\r]/ /xmsg;
                   8533:     $result =~ s/\\/\\\\/xmsg;
                   8534:     $result =~ s/'/\\'/xmsg;
1.372     albertel 8535:     $result =~ s{</}{<\\/}xmsg;
1.317     albertel 8536:     
                   8537:     return $result;
                   8538: }
                   8539: 
1.315     albertel 8540: sub validate_page {
                   8541:     if (  exists($env{'internal.start_page'})
1.316     albertel 8542: 	  &&     $env{'internal.start_page'} > 1) {
                   8543: 	&Apache::lonnet::logthis('start_page called multiple times '.
1.318     albertel 8544: 				 $env{'internal.start_page'}.' '.
1.316     albertel 8545: 				 $ENV{'request.filename'});
1.315     albertel 8546:     }
                   8547:     if (  exists($env{'internal.end_page'})
1.316     albertel 8548: 	  &&     $env{'internal.end_page'} > 1) {
                   8549: 	&Apache::lonnet::logthis('end_page called multiple times '.
1.318     albertel 8550: 				 $env{'internal.end_page'}.' '.
1.316     albertel 8551: 				 $env{'request.filename'});
1.315     albertel 8552:     }
                   8553:     if (     exists($env{'internal.start_page'})
                   8554: 	&& ! exists($env{'internal.end_page'})) {
1.316     albertel 8555: 	&Apache::lonnet::logthis('start_page called without end_page '.
                   8556: 				 $env{'request.filename'});
1.315     albertel 8557:     }
                   8558:     if (   ! exists($env{'internal.start_page'})
                   8559: 	&&   exists($env{'internal.end_page'})) {
1.316     albertel 8560: 	&Apache::lonnet::logthis('end_page called without start_page'.
                   8561: 				 $env{'request.filename'});
1.315     albertel 8562:     }
1.306     albertel 8563: }
1.315     albertel 8564: 
1.996     www      8565: 
                   8566: sub start_scrollbox {
1.1140    raeburn  8567:     my ($outerwidth,$width,$height,$id,$bgcolor,$cursor,$needjsready) = @_;
1.998     raeburn  8568:     unless ($outerwidth) { $outerwidth='520px'; }
                   8569:     unless ($width) { $width='500px'; }
                   8570:     unless ($height) { $height='200px'; }
1.1075    raeburn  8571:     my ($table_id,$div_id,$tdcol);
1.1018    raeburn  8572:     if ($id ne '') {
1.1140    raeburn  8573:         $table_id = ' id="table_'.$id.'"';
1.1137    raeburn  8574:         $div_id = ' id="div_'.$id.'"';
1.1018    raeburn  8575:     }
1.1075    raeburn  8576:     if ($bgcolor ne '') {
                   8577:         $tdcol = "background-color: $bgcolor;";
                   8578:     }
1.1137    raeburn  8579:     my $nicescroll_js;
                   8580:     if ($env{'browser.mobile'}) {
1.1140    raeburn  8581:         $nicescroll_js = &nicescroll_javascript('div_'.$id,$cursor,$needjsready);
                   8582:     }
                   8583:     return <<"END";
                   8584: $nicescroll_js
                   8585: 
                   8586: <table style="width: $outerwidth; border: 1px solid none;"$table_id><tr><td style="width: $width;$tdcol">
                   8587: <div style="overflow:auto; width:$width; height:$height;"$div_id>
                   8588: END
                   8589: }
                   8590: 
                   8591: sub end_scrollbox {
                   8592:     return '</div></td></tr></table>';
                   8593: }
                   8594: 
                   8595: sub nicescroll_javascript {
                   8596:     my ($id,$cursor,$needjsready,$framecheck,$location) = @_;
                   8597:     my %options;
                   8598:     if (ref($cursor) eq 'HASH') {
                   8599:         %options = %{$cursor};
                   8600:     }
                   8601:     unless ($options{'railalign'} =~ /^left|right$/) {
                   8602:         $options{'railalign'} = 'left';
                   8603:     }
                   8604:     unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
                   8605:         my $function  = &get_users_function();
                   8606:         $options{'cursorcolor'} = &designparm($function.'.sidebg',$env{'request.role.domain'});
1.1138    raeburn  8607:         unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
1.1140    raeburn  8608:             $options{'cursorcolor'} = '#00F';
1.1138    raeburn  8609:         }
1.1140    raeburn  8610:     }
                   8611:     if ($options{'cursoropacity'} =~ /^[\d.]+$/) {
                   8612:         unless ($options{'cursoropacity'} >= 0.0 && $options{'cursoropacity'} <=1.0) {
1.1138    raeburn  8613:             $options{'cursoropacity'}='1.0';
                   8614:         }
1.1140    raeburn  8615:     } else {
                   8616:         $options{'cursoropacity'}='1.0';
                   8617:     }
                   8618:     if ($options{'cursorfixedheight'} eq 'none') {
                   8619:         delete($options{'cursorfixedheight'});
                   8620:     } else {
                   8621:         unless ($options{'cursorfixedheight'} =~ /^\d+$/) { $options{'cursorfixedheight'}='50'; }
                   8622:     }
                   8623:     unless ($options{'railoffset'} =~ /^{[\w\:\d\-,]+}$/) {
                   8624:         delete($options{'railoffset'});
                   8625:     }
                   8626:     my @niceoptions;
                   8627:     while (my($key,$value) = each(%options)) {
                   8628:         if ($value =~ /^\{.+\}$/) {
                   8629:             push(@niceoptions,$key.':'.$value);
1.1138    raeburn  8630:         } else {
1.1140    raeburn  8631:             push(@niceoptions,$key.':"'.$value.'"');
1.1138    raeburn  8632:         }
1.1140    raeburn  8633:     }
                   8634:     my $nicescroll_js = '
1.1137    raeburn  8635: $(document).ready(
1.1140    raeburn  8636:       function() {
                   8637:           $("#'.$id.'").niceScroll({'.join(',',@niceoptions).'});
                   8638:       }
1.1137    raeburn  8639: );
                   8640: ';
1.1140    raeburn  8641:     if ($framecheck) {
                   8642:         $nicescroll_js .= '
                   8643: function expand_div(caller) {
                   8644:     if (top === self) {
                   8645:         document.getElementById("'.$id.'").style.width = "auto";
                   8646:         document.getElementById("'.$id.'").style.height = "auto";
                   8647:     } else {
                   8648:         try {
                   8649:             if (parent.frames) {
                   8650:                 if (parent.frames.length > 1) {
                   8651:                     var framesrc = parent.frames[1].location.href;
                   8652:                     var currsrc = framesrc.replace(/\#.*$/,"");
                   8653:                     if ((caller == "search") || (currsrc == "'.$location.'")) {
                   8654:                         document.getElementById("'.$id.'").style.width = "auto";
                   8655:                         document.getElementById("'.$id.'").style.height = "auto";
                   8656:                     }
                   8657:                 }
                   8658:             }
                   8659:         } catch (e) {
                   8660:             return;
                   8661:         }
1.1137    raeburn  8662:     }
1.1140    raeburn  8663:     return;
1.996     www      8664: }
1.1140    raeburn  8665: ';
                   8666:     }
                   8667:     if ($needjsready) {
                   8668:         $nicescroll_js = '
                   8669: <script type="text/javascript">'."\n".$nicescroll_js."\n</script>\n";
                   8670:     } else {
                   8671:         $nicescroll_js = &Apache::lonhtmlcommon::scripttag($nicescroll_js);
                   8672:     }
                   8673:     return $nicescroll_js;
1.996     www      8674: }
                   8675: 
1.318     albertel 8676: sub simple_error_page {
1.1150    bisitz   8677:     my ($r,$title,$msg,$args) = @_;
1.1151    raeburn  8678:     if (ref($args) eq 'HASH') {
                   8679:         if (!$args->{'no_auto_mt_msg'}) { $msg = &mt($msg); }
                   8680:     } else {
                   8681:         $msg = &mt($msg);
                   8682:     }
1.1150    bisitz   8683: 
1.318     albertel 8684:     my $page =
                   8685: 	&Apache::loncommon::start_page($title).
1.1150    bisitz   8686: 	'<p class="LC_error">'.$msg.'</p>'.
1.318     albertel 8687: 	&Apache::loncommon::end_page();
                   8688:     if (ref($r)) {
                   8689: 	$r->print($page);
1.327     albertel 8690: 	return;
1.318     albertel 8691:     }
                   8692:     return $page;
                   8693: }
1.347     albertel 8694: 
                   8695: {
1.610     albertel 8696:     my @row_count;
1.961     onken    8697: 
                   8698:     sub start_data_table_count {
                   8699:         unshift(@row_count, 0);
                   8700:         return;
                   8701:     }
                   8702: 
                   8703:     sub end_data_table_count {
                   8704:         shift(@row_count);
                   8705:         return;
                   8706:     }
                   8707: 
1.347     albertel 8708:     sub start_data_table {
1.1018    raeburn  8709: 	my ($add_class,$id) = @_;
1.422     albertel 8710: 	my $css_class = (join(' ','LC_data_table',$add_class));
1.1018    raeburn  8711:         my $table_id;
                   8712:         if (defined($id)) {
                   8713:             $table_id = ' id="'.$id.'"';
                   8714:         }
1.961     onken    8715: 	&start_data_table_count();
1.1018    raeburn  8716: 	return '<table class="'.$css_class.'"'.$table_id.'>'."\n";
1.347     albertel 8717:     }
                   8718: 
                   8719:     sub end_data_table {
1.961     onken    8720: 	&end_data_table_count();
1.389     albertel 8721: 	return '</table>'."\n";;
1.347     albertel 8722:     }
                   8723: 
                   8724:     sub start_data_table_row {
1.974     wenzelju 8725: 	my ($add_class, $id) = @_;
1.610     albertel 8726: 	$row_count[0]++;
                   8727: 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.900     bisitz   8728: 	$css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
1.974     wenzelju 8729:         $id = (' id="'.$id.'"') unless ($id eq '');
                   8730:         return  '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.347     albertel 8731:     }
1.471     banghart 8732:     
                   8733:     sub continue_data_table_row {
1.974     wenzelju 8734: 	my ($add_class, $id) = @_;
1.610     albertel 8735: 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.974     wenzelju 8736: 	$css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
                   8737:         $id = (' id="'.$id.'"') unless ($id eq '');
                   8738:         return  '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.471     banghart 8739:     }
1.347     albertel 8740: 
                   8741:     sub end_data_table_row {
1.389     albertel 8742: 	return '</tr>'."\n";;
1.347     albertel 8743:     }
1.367     www      8744: 
1.421     albertel 8745:     sub start_data_table_empty_row {
1.707     bisitz   8746: #	$row_count[0]++;
1.421     albertel 8747: 	return  '<tr class="LC_empty_row" >'."\n";;
                   8748:     }
                   8749: 
                   8750:     sub end_data_table_empty_row {
                   8751: 	return '</tr>'."\n";;
                   8752:     }
                   8753: 
1.367     www      8754:     sub start_data_table_header_row {
1.389     albertel 8755: 	return  '<tr class="LC_header_row">'."\n";;
1.367     www      8756:     }
                   8757: 
                   8758:     sub end_data_table_header_row {
1.389     albertel 8759: 	return '</tr>'."\n";;
1.367     www      8760:     }
1.890     droeschl 8761: 
                   8762:     sub data_table_caption {
                   8763:         my $caption = shift;
                   8764:         return "<caption class=\"LC_caption\">$caption</caption>";
                   8765:     }
1.347     albertel 8766: }
                   8767: 
1.548     albertel 8768: =pod
                   8769: 
                   8770: =item * &inhibit_menu_check($arg)
                   8771: 
                   8772: Checks for a inhibitmenu state and generates output to preserve it
                   8773: 
                   8774: Inputs:         $arg - can be any of
                   8775:                      - undef - in which case the return value is a string 
                   8776:                                to add  into arguments list of a uri
                   8777:                      - 'input' - in which case the return value is a HTML
                   8778:                                  <form> <input> field of type hidden to
                   8779:                                  preserve the value
                   8780:                      - a url - in which case the return value is the url with
                   8781:                                the neccesary cgi args added to preserve the
                   8782:                                inhibitmenu state
                   8783:                      - a ref to a url - no return value, but the string is
                   8784:                                         updated to include the neccessary cgi
                   8785:                                         args to preserve the inhibitmenu state
                   8786: 
                   8787: =cut
                   8788: 
                   8789: sub inhibit_menu_check {
                   8790:     my ($arg) = @_;
                   8791:     &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
                   8792:     if ($arg eq 'input') {
                   8793: 	if ($env{'form.inhibitmenu'}) {
                   8794: 	    return '<input type="hidden" name="inhibitmenu" value="'.$env{'form.inhibitmenu'}.'" />';
                   8795: 	} else {
                   8796: 	    return
                   8797: 	}
                   8798:     }
                   8799:     if ($env{'form.inhibitmenu'}) {
                   8800: 	if (ref($arg)) {
                   8801: 	    $$arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   8802: 	} elsif ($arg eq '') {
                   8803: 	    $arg .= 'inhibitmenu='.$env{'form.inhibitmenu'};
                   8804: 	} else {
                   8805: 	    $arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   8806: 	}
                   8807:     }
                   8808:     if (!ref($arg)) {
                   8809: 	return $arg;
                   8810:     }
                   8811: }
                   8812: 
1.251     albertel 8813: ###############################################
1.182     matthew  8814: 
                   8815: =pod
                   8816: 
1.549     albertel 8817: =back
                   8818: 
                   8819: =head1 User Information Routines
                   8820: 
                   8821: =over 4
                   8822: 
1.405     albertel 8823: =item * &get_users_function()
1.182     matthew  8824: 
                   8825: Used by &bodytag to determine the current users primary role.
                   8826: Returns either 'student','coordinator','admin', or 'author'.
                   8827: 
                   8828: =cut
                   8829: 
                   8830: ###############################################
                   8831: sub get_users_function {
1.815     tempelho 8832:     my $function = 'norole';
1.818     tempelho 8833:     if ($env{'request.role'}=~/^(st)/) {
                   8834:         $function='student';
                   8835:     }
1.907     raeburn  8836:     if ($env{'request.role'}=~/^(cc|co|in|ta|ep)/) {
1.182     matthew  8837:         $function='coordinator';
                   8838:     }
1.258     albertel 8839:     if ($env{'request.role'}=~/^(su|dc|ad|li)/) {
1.182     matthew  8840:         $function='admin';
                   8841:     }
1.826     bisitz   8842:     if (($env{'request.role'}=~/^(au|ca|aa)/) ||
1.1025    raeburn  8843:         ($ENV{'REQUEST_URI'}=~ m{/^(/priv)})) {
1.182     matthew  8844:         $function='author';
                   8845:     }
                   8846:     return $function;
1.54      www      8847: }
1.99      www      8848: 
                   8849: ###############################################
                   8850: 
1.233     raeburn  8851: =pod
                   8852: 
1.821     raeburn  8853: =item * &show_course()
                   8854: 
                   8855: Used by lonmenu.pm and lonroles.pm to determine whether to use the word
                   8856: 'Courses' or 'Roles' in inline navigation and on screen displaying user's roles.
                   8857: 
                   8858: Inputs:
                   8859: None
                   8860: 
                   8861: Outputs:
                   8862: Scalar: 1 if 'Course' to be used, 0 otherwise.
                   8863: 
                   8864: =cut
                   8865: 
                   8866: ###############################################
                   8867: sub show_course {
                   8868:     my $course = !$env{'user.adv'};
                   8869:     if (!$env{'user.adv'}) {
                   8870:         foreach my $env (keys(%env)) {
                   8871:             next if ($env !~ m/^user\.priv\./);
                   8872:             if ($env !~ m/^user\.priv\.(?:st|cm)/) {
                   8873:                 $course = 0;
                   8874:                 last;
                   8875:             }
                   8876:         }
                   8877:     }
                   8878:     return $course;
                   8879: }
                   8880: 
                   8881: ###############################################
                   8882: 
                   8883: =pod
                   8884: 
1.542     raeburn  8885: =item * &check_user_status()
1.274     raeburn  8886: 
                   8887: Determines current status of supplied role for a
                   8888: specific user. Roles can be active, previous or future.
                   8889: 
                   8890: Inputs: 
                   8891: user's domain, user's username, course's domain,
1.375     raeburn  8892: course's number, optional section ID.
1.274     raeburn  8893: 
                   8894: Outputs:
                   8895: role status: active, previous or future. 
                   8896: 
                   8897: =cut
                   8898: 
                   8899: sub check_user_status {
1.412     raeburn  8900:     my ($udom,$uname,$cdom,$crs,$role,$sec) = @_;
1.1073    raeburn  8901:     my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname);
1.1202    raeburn  8902:     my @uroles = keys(%userinfo);
1.274     raeburn  8903:     my $srchstr;
                   8904:     my $active_chk = 'none';
1.412     raeburn  8905:     my $now = time;
1.274     raeburn  8906:     if (@uroles > 0) {
1.908     raeburn  8907:         if (($role eq 'cc') || ($role eq 'co') || ($sec eq '') || (!defined($sec))) {
1.274     raeburn  8908:             $srchstr = '/'.$cdom.'/'.$crs.'_'.$role;
                   8909:         } else {
1.412     raeburn  8910:             $srchstr = '/'.$cdom.'/'.$crs.'/'.$sec.'_'.$role;
                   8911:         }
                   8912:         if (grep/^\Q$srchstr\E$/,@uroles) {
1.274     raeburn  8913:             my $role_end = 0;
                   8914:             my $role_start = 0;
                   8915:             $active_chk = 'active';
1.412     raeburn  8916:             if ($userinfo{$srchstr} =~ m/^\Q$role\E_(\d+)/) {
                   8917:                 $role_end = $1;
                   8918:                 if ($userinfo{$srchstr} =~ m/^\Q$role\E_\Q$role_end\E_(\d+)$/) {
                   8919:                     $role_start = $1;
1.274     raeburn  8920:                 }
                   8921:             }
                   8922:             if ($role_start > 0) {
1.412     raeburn  8923:                 if ($now < $role_start) {
1.274     raeburn  8924:                     $active_chk = 'future';
                   8925:                 }
                   8926:             }
                   8927:             if ($role_end > 0) {
1.412     raeburn  8928:                 if ($now > $role_end) {
1.274     raeburn  8929:                     $active_chk = 'previous';
                   8930:                 }
                   8931:             }
                   8932:         }
                   8933:     }
                   8934:     return $active_chk;
                   8935: }
                   8936: 
                   8937: ###############################################
                   8938: 
                   8939: =pod
                   8940: 
1.405     albertel 8941: =item * &get_sections()
1.233     raeburn  8942: 
                   8943: Determines all the sections for a course including
                   8944: sections with students and sections containing other roles.
1.419     raeburn  8945: Incoming parameters: 
                   8946: 
                   8947: 1. domain
                   8948: 2. course number 
                   8949: 3. reference to array containing roles for which sections should 
                   8950: be gathered (optional).
                   8951: 4. reference to array containing status types for which sections 
                   8952: should be gathered (optional).
                   8953: 
                   8954: If the third argument is undefined, sections are gathered for any role. 
                   8955: If the fourth argument is undefined, sections are gathered for any status.
                   8956: Permissible values are 'active' or 'future' or 'previous'.
1.233     raeburn  8957:  
1.374     raeburn  8958: Returns section hash (keys are section IDs, values are
                   8959: number of users in each section), subject to the
1.419     raeburn  8960: optional roles filter, optional status filter 
1.233     raeburn  8961: 
                   8962: =cut
                   8963: 
                   8964: ###############################################
                   8965: sub get_sections {
1.419     raeburn  8966:     my ($cdom,$cnum,$possible_roles,$possible_status) = @_;
1.366     albertel 8967:     if (!defined($cdom) || !defined($cnum)) {
                   8968:         my $cid =  $env{'request.course.id'};
                   8969: 
                   8970: 	return if (!defined($cid));
                   8971: 
                   8972:         $cdom = $env{'course.'.$cid.'.domain'};
                   8973:         $cnum = $env{'course.'.$cid.'.num'};
                   8974:     }
                   8975: 
                   8976:     my %sectioncount;
1.419     raeburn  8977:     my $now = time;
1.240     albertel 8978: 
1.1118    raeburn  8979:     my $check_students = 1;
                   8980:     my $only_students = 0;
                   8981:     if (ref($possible_roles) eq 'ARRAY') {
                   8982:         if (grep(/^st$/,@{$possible_roles})) {
                   8983:             if (@{$possible_roles} == 1) {
                   8984:                 $only_students = 1;
                   8985:             }
                   8986:         } else {
                   8987:             $check_students = 0;
                   8988:         }
                   8989:     }
                   8990: 
                   8991:     if ($check_students) { 
1.276     albertel 8992: 	my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
1.240     albertel 8993: 	my $sec_index = &Apache::loncoursedata::CL_SECTION();
                   8994: 	my $status_index = &Apache::loncoursedata::CL_STATUS();
1.419     raeburn  8995:         my $start_index = &Apache::loncoursedata::CL_START();
                   8996:         my $end_index = &Apache::loncoursedata::CL_END();
                   8997:         my $status;
1.366     albertel 8998: 	while (my ($student,$data) = each(%$classlist)) {
1.419     raeburn  8999: 	    my ($section,$stu_status,$start,$end) = ($data->[$sec_index],
                   9000: 				                     $data->[$status_index],
                   9001:                                                      $data->[$start_index],
                   9002:                                                      $data->[$end_index]);
                   9003:             if ($stu_status eq 'Active') {
                   9004:                 $status = 'active';
                   9005:             } elsif ($end < $now) {
                   9006:                 $status = 'previous';
                   9007:             } elsif ($start > $now) {
                   9008:                 $status = 'future';
                   9009:             } 
                   9010: 	    if ($section ne '-1' && $section !~ /^\s*$/) {
                   9011:                 if ((!defined($possible_status)) || (($status ne '') && 
                   9012:                     (grep/^\Q$status\E$/,@{$possible_status}))) { 
                   9013: 		    $sectioncount{$section}++;
                   9014:                 }
1.240     albertel 9015: 	    }
                   9016: 	}
                   9017:     }
1.1118    raeburn  9018:     if ($only_students) {
                   9019:         return %sectioncount;
                   9020:     }
1.240     albertel 9021:     my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   9022:     foreach my $user (sort(keys(%courseroles))) {
                   9023: 	if ($user !~ /^(\w{2})/) { next; }
                   9024: 	my ($role) = ($user =~ /^(\w{2})/);
                   9025: 	if ($possible_roles && !(grep(/^$role$/,@$possible_roles))) { next; }
1.419     raeburn  9026: 	my ($section,$status);
1.240     albertel 9027: 	if ($role eq 'cr' &&
                   9028: 	    $user =~ m-^$role/[^/]*/[^/]*/[^/]*:[^:]*:[^:]*:(\w+)-) {
                   9029: 	    $section=$1;
                   9030: 	}
                   9031: 	if ($user =~ /^$role:[^:]*:[^:]*:(\w+)/) { $section=$1; }
                   9032: 	if (!defined($section) || $section eq '-1') { next; }
1.419     raeburn  9033:         my ($end,$start) = ($courseroles{$user} =~ /^([^:]*):([^:]*)$/);
                   9034:         if ($end == -1 && $start == -1) {
                   9035:             next; #deleted role
                   9036:         }
                   9037:         if (!defined($possible_status)) { 
                   9038:             $sectioncount{$section}++;
                   9039:         } else {
                   9040:             if ((!$end || $end >= $now) && (!$start || $start <= $now)) {
                   9041:                 $status = 'active';
                   9042:             } elsif ($end < $now) {
                   9043:                 $status = 'future';
                   9044:             } elsif ($start > $now) {
                   9045:                 $status = 'previous';
                   9046:             }
                   9047:             if (($status ne '') && (grep/^\Q$status\E$/,@{$possible_status})) {
                   9048:                 $sectioncount{$section}++;
                   9049:             }
                   9050:         }
1.233     raeburn  9051:     }
1.366     albertel 9052:     return %sectioncount;
1.233     raeburn  9053: }
                   9054: 
1.274     raeburn  9055: ###############################################
1.294     raeburn  9056: 
                   9057: =pod
1.405     albertel 9058: 
                   9059: =item * &get_course_users()
                   9060: 
1.275     raeburn  9061: Retrieves usernames:domains for users in the specified course
                   9062: with specific role(s), and access status. 
                   9063: 
                   9064: Incoming parameters:
1.277     albertel 9065: 1. course domain
                   9066: 2. course number
                   9067: 3. access status: users must have - either active, 
1.275     raeburn  9068: previous, future, or all.
1.277     albertel 9069: 4. reference to array of permissible roles
1.288     raeburn  9070: 5. reference to array of section restrictions (optional)
                   9071: 6. reference to results object (hash of hashes).
                   9072: 7. reference to optional userdata hash
1.609     raeburn  9073: 8. reference to optional statushash
1.630     raeburn  9074: 9. flag if privileged users (except those set to unhide in
                   9075:    course settings) should be excluded    
1.609     raeburn  9076: Keys of top level results hash are roles.
1.275     raeburn  9077: Keys of inner hashes are username:domain, with 
                   9078: values set to access type.
1.288     raeburn  9079: Optional userdata hash returns an array with arguments in the 
                   9080: same order as loncoursedata::get_classlist() for student data.
                   9081: 
1.609     raeburn  9082: Optional statushash returns
                   9083: 
1.288     raeburn  9084: Entries for end, start, section and status are blank because
                   9085: of the possibility of multiple values for non-student roles.
                   9086: 
1.275     raeburn  9087: =cut
1.405     albertel 9088: 
1.275     raeburn  9089: ###############################################
1.405     albertel 9090: 
1.275     raeburn  9091: sub get_course_users {
1.630     raeburn  9092:     my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata,$statushash,$hidepriv) = @_;
1.288     raeburn  9093:     my %idx = ();
1.419     raeburn  9094:     my %seclists;
1.288     raeburn  9095: 
                   9096:     $idx{udom} = &Apache::loncoursedata::CL_SDOM();
                   9097:     $idx{uname} =  &Apache::loncoursedata::CL_SNAME();
                   9098:     $idx{end} = &Apache::loncoursedata::CL_END();
                   9099:     $idx{start} = &Apache::loncoursedata::CL_START();
                   9100:     $idx{id} = &Apache::loncoursedata::CL_ID();
                   9101:     $idx{section} = &Apache::loncoursedata::CL_SECTION();
                   9102:     $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME();
                   9103:     $idx{status} = &Apache::loncoursedata::CL_STATUS();
                   9104: 
1.290     albertel 9105:     if (grep(/^st$/,@{$roles})) {
1.276     albertel 9106:         my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist($cdom,$cnum);
1.278     raeburn  9107:         my $now = time;
1.277     albertel 9108:         foreach my $student (keys(%{$classlist})) {
1.288     raeburn  9109:             my $match = 0;
1.412     raeburn  9110:             my $secmatch = 0;
1.419     raeburn  9111:             my $section = $$classlist{$student}[$idx{section}];
1.609     raeburn  9112:             my $status = $$classlist{$student}[$idx{status}];
1.419     raeburn  9113:             if ($section eq '') {
                   9114:                 $section = 'none';
                   9115:             }
1.291     albertel 9116:             if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 9117:                 if (grep(/^all$/,@{$sections})) {
1.412     raeburn  9118:                     $secmatch = 1;
                   9119:                 } elsif ($$classlist{$student}[$idx{section}] eq '') {
1.420     albertel 9120:                     if (grep(/^none$/,@{$sections})) {
1.412     raeburn  9121:                         $secmatch = 1;
                   9122:                     }
                   9123:                 } else {  
1.419     raeburn  9124: 		    if (grep(/^\Q$section\E$/,@{$sections})) {
1.412     raeburn  9125: 		        $secmatch = 1;
                   9126:                     }
1.290     albertel 9127: 		}
1.412     raeburn  9128:                 if (!$secmatch) {
                   9129:                     next;
                   9130:                 }
1.419     raeburn  9131:             }
1.275     raeburn  9132:             if (defined($$types{'active'})) {
1.288     raeburn  9133:                 if ($$classlist{$student}[$idx{status}] eq 'Active') {
1.275     raeburn  9134:                     push(@{$$users{st}{$student}},'active');
1.288     raeburn  9135:                     $match = 1;
1.275     raeburn  9136:                 }
                   9137:             }
                   9138:             if (defined($$types{'previous'})) {
1.609     raeburn  9139:                 if ($$classlist{$student}[$idx{status}] eq 'Expired') {
1.275     raeburn  9140:                     push(@{$$users{st}{$student}},'previous');
1.288     raeburn  9141:                     $match = 1;
1.275     raeburn  9142:                 }
                   9143:             }
                   9144:             if (defined($$types{'future'})) {
1.609     raeburn  9145:                 if ($$classlist{$student}[$idx{status}] eq 'Future') {
1.275     raeburn  9146:                     push(@{$$users{st}{$student}},'future');
1.288     raeburn  9147:                     $match = 1;
1.275     raeburn  9148:                 }
                   9149:             }
1.609     raeburn  9150:             if ($match) {
                   9151:                 push(@{$seclists{$student}},$section);
                   9152:                 if (ref($userdata) eq 'HASH') {
                   9153:                     $$userdata{$student} = $$classlist{$student};
                   9154:                 }
                   9155:                 if (ref($statushash) eq 'HASH') {
                   9156:                     $statushash->{$student}{'st'}{$section} = $status;
                   9157:                 }
1.288     raeburn  9158:             }
1.275     raeburn  9159:         }
                   9160:     }
1.412     raeburn  9161:     if ((@{$roles} > 1) || ((@{$roles} == 1) && ($$roles[0] ne "st"))) {
1.439     raeburn  9162:         my %coursepersonnel = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   9163:         my $now = time;
1.609     raeburn  9164:         my %displaystatus = ( previous => 'Expired',
                   9165:                               active   => 'Active',
                   9166:                               future   => 'Future',
                   9167:                             );
1.1121    raeburn  9168:         my (%nothide,@possdoms);
1.630     raeburn  9169:         if ($hidepriv) {
                   9170:             my %coursehash=&Apache::lonnet::coursedescription($cdom.'_'.$cnum);
                   9171:             foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
                   9172:                 if ($user !~ /:/) {
                   9173:                     $nothide{join(':',split(/[\@]/,$user))}=1;
                   9174:                 } else {
                   9175:                     $nothide{$user} = 1;
                   9176:                 }
                   9177:             }
1.1121    raeburn  9178:             my @possdoms = ($cdom);
                   9179:             if ($coursehash{'checkforpriv'}) {
                   9180:                 push(@possdoms,split(/,/,$coursehash{'checkforpriv'}));
                   9181:             }
1.630     raeburn  9182:         }
1.439     raeburn  9183:         foreach my $person (sort(keys(%coursepersonnel))) {
1.288     raeburn  9184:             my $match = 0;
1.412     raeburn  9185:             my $secmatch = 0;
1.439     raeburn  9186:             my $status;
1.412     raeburn  9187:             my ($role,$user,$usec) = ($person =~ /^([^:]*):([^:]+:[^:]+):([^:]*)/);
1.275     raeburn  9188:             $user =~ s/:$//;
1.439     raeburn  9189:             my ($end,$start) = split(/:/,$coursepersonnel{$person});
                   9190:             if ($end == -1 || $start == -1) {
                   9191:                 next;
                   9192:             }
                   9193:             if (($role) && ((grep(/^\Q$role\E$/,@{$roles})) ||
                   9194:                 (grep(/^cr$/,@{$roles}) && $role =~ /^cr\//))) {
1.412     raeburn  9195:                 my ($uname,$udom) = split(/:/,$user);
                   9196:                 if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 9197:                     if (grep(/^all$/,@{$sections})) {
1.412     raeburn  9198:                         $secmatch = 1;
                   9199:                     } elsif ($usec eq '') {
1.420     albertel 9200:                         if (grep(/^none$/,@{$sections})) {
1.412     raeburn  9201:                             $secmatch = 1;
                   9202:                         }
                   9203:                     } else {
                   9204:                         if (grep(/^\Q$usec\E$/,@{$sections})) {
                   9205:                             $secmatch = 1;
                   9206:                         }
                   9207:                     }
                   9208:                     if (!$secmatch) {
                   9209:                         next;
                   9210:                     }
1.288     raeburn  9211:                 }
1.419     raeburn  9212:                 if ($usec eq '') {
                   9213:                     $usec = 'none';
                   9214:                 }
1.275     raeburn  9215:                 if ($uname ne '' && $udom ne '') {
1.630     raeburn  9216:                     if ($hidepriv) {
1.1121    raeburn  9217:                         if ((&Apache::lonnet::privileged($uname,$udom,\@possdoms)) &&
1.630     raeburn  9218:                             (!$nothide{$uname.':'.$udom})) {
                   9219:                             next;
                   9220:                         }
                   9221:                     }
1.503     raeburn  9222:                     if ($end > 0 && $end < $now) {
1.439     raeburn  9223:                         $status = 'previous';
                   9224:                     } elsif ($start > $now) {
                   9225:                         $status = 'future';
                   9226:                     } else {
                   9227:                         $status = 'active';
                   9228:                     }
1.277     albertel 9229:                     foreach my $type (keys(%{$types})) { 
1.275     raeburn  9230:                         if ($status eq $type) {
1.420     albertel 9231:                             if (!grep(/^\Q$type\E$/,@{$$users{$role}{$user}})) {
1.419     raeburn  9232:                                 push(@{$$users{$role}{$user}},$type);
                   9233:                             }
1.288     raeburn  9234:                             $match = 1;
                   9235:                         }
                   9236:                     }
1.419     raeburn  9237:                     if (($match) && (ref($userdata) eq 'HASH')) {
                   9238:                         if (!exists($$userdata{$uname.':'.$udom})) {
                   9239: 			    &get_user_info($udom,$uname,\%idx,$userdata);
                   9240:                         }
1.420     albertel 9241:                         if (!grep(/^\Q$usec\E$/,@{$seclists{$uname.':'.$udom}})) {
1.419     raeburn  9242:                             push(@{$seclists{$uname.':'.$udom}},$usec);
                   9243:                         }
1.609     raeburn  9244:                         if (ref($statushash) eq 'HASH') {
                   9245:                             $statushash->{$uname.':'.$udom}{$role}{$usec} = $displaystatus{$status};
                   9246:                         }
1.275     raeburn  9247:                     }
                   9248:                 }
                   9249:             }
                   9250:         }
1.290     albertel 9251:         if (grep(/^ow$/,@{$roles})) {
1.279     raeburn  9252:             if ((defined($cdom)) && (defined($cnum))) {
                   9253:                 my %csettings = &Apache::lonnet::get('environment',['internal.courseowner'],$cdom,$cnum);
                   9254:                 if ( defined($csettings{'internal.courseowner'}) ) {
                   9255:                     my $owner = $csettings{'internal.courseowner'};
1.609     raeburn  9256:                     next if ($owner eq '');
                   9257:                     my ($ownername,$ownerdom);
                   9258:                     if ($owner =~ /^([^:]+):([^:]+)$/) {
                   9259:                         $ownername = $1;
                   9260:                         $ownerdom = $2;
                   9261:                     } else {
                   9262:                         $ownername = $owner;
                   9263:                         $ownerdom = $cdom;
                   9264:                         $owner = $ownername.':'.$ownerdom;
1.439     raeburn  9265:                     }
                   9266:                     @{$$users{'ow'}{$owner}} = 'any';
1.290     albertel 9267:                     if (defined($userdata) && 
1.609     raeburn  9268: 			!exists($$userdata{$owner})) {
                   9269: 			&get_user_info($ownerdom,$ownername,\%idx,$userdata);
                   9270:                         if (!grep(/^none$/,@{$seclists{$owner}})) {
                   9271:                             push(@{$seclists{$owner}},'none');
                   9272:                         }
                   9273:                         if (ref($statushash) eq 'HASH') {
                   9274:                             $statushash->{$owner}{'ow'}{'none'} = 'Any';
1.419     raeburn  9275:                         }
1.290     albertel 9276: 		    }
1.279     raeburn  9277:                 }
                   9278:             }
                   9279:         }
1.419     raeburn  9280:         foreach my $user (keys(%seclists)) {
                   9281:             @{$seclists{$user}} = (sort {$a <=> $b} @{$seclists{$user}});
                   9282:             $$userdata{$user}[$idx{section}] = join(',',@{$seclists{$user}});
                   9283:         }
1.275     raeburn  9284:     }
                   9285:     return;
                   9286: }
                   9287: 
1.288     raeburn  9288: sub get_user_info {
                   9289:     my ($udom,$uname,$idx,$userdata) = @_;
1.289     albertel 9290:     $$userdata{$uname.':'.$udom}[$$idx{fullname}] = 
                   9291: 	&plainname($uname,$udom,'lastname');
1.291     albertel 9292:     $$userdata{$uname.':'.$udom}[$$idx{uname}] = $uname;
1.297     raeburn  9293:     $$userdata{$uname.':'.$udom}[$$idx{udom}] = $udom;
1.609     raeburn  9294:     my %idhash =  &Apache::lonnet::idrget($udom,($uname));
                   9295:     $$userdata{$uname.':'.$udom}[$$idx{id}] = $idhash{$uname}; 
1.288     raeburn  9296:     return;
                   9297: }
1.275     raeburn  9298: 
1.472     raeburn  9299: ###############################################
                   9300: 
                   9301: =pod
                   9302: 
                   9303: =item * &get_user_quota()
                   9304: 
1.1134    raeburn  9305: Retrieves quota assigned for storage of user files.
                   9306: Default is to report quota for portfolio files.
1.472     raeburn  9307: 
                   9308: Incoming parameters:
                   9309: 1. user's username
                   9310: 2. user's domain
1.1134    raeburn  9311: 3. quota name - portfolio, author, or course
1.1136    raeburn  9312:    (if no quota name provided, defaults to portfolio).
1.1165    raeburn  9313: 4. crstype - official, unofficial, textbook or community, if quota name is
1.1136    raeburn  9314:    course
1.472     raeburn  9315: 
                   9316: Returns:
1.1163    raeburn  9317: 1. Disk quota (in MB) assigned to student.
1.536     raeburn  9318: 2. (Optional) Type of setting: custom or default
                   9319:    (individually assigned or default for user's 
                   9320:    institutional status).
                   9321: 3. (Optional) - User's institutional status (e.g., faculty, staff
                   9322:    or student - types as defined in localenroll::inst_usertypes 
                   9323:    for user's domain, which determines default quota for user.
                   9324: 4. (Optional) - Default quota which would apply to the user.
1.472     raeburn  9325: 
                   9326: If a value has been stored in the user's environment, 
1.536     raeburn  9327: it will return that, otherwise it returns the maximal default
1.1134    raeburn  9328: defined for the user's institutional status(es) in the domain.
1.472     raeburn  9329: 
                   9330: =cut
                   9331: 
                   9332: ###############################################
                   9333: 
                   9334: 
                   9335: sub get_user_quota {
1.1136    raeburn  9336:     my ($uname,$udom,$quotaname,$crstype) = @_;
1.536     raeburn  9337:     my ($quota,$quotatype,$settingstatus,$defquota);
1.472     raeburn  9338:     if (!defined($udom)) {
                   9339:         $udom = $env{'user.domain'};
                   9340:     }
                   9341:     if (!defined($uname)) {
                   9342:         $uname = $env{'user.name'};
                   9343:     }
                   9344:     if (($udom eq '' || $uname eq '') ||
                   9345:         ($udom eq 'public') && ($uname eq 'public')) {
                   9346:         $quota = 0;
1.536     raeburn  9347:         $quotatype = 'default';
                   9348:         $defquota = 0; 
1.472     raeburn  9349:     } else {
1.536     raeburn  9350:         my $inststatus;
1.1134    raeburn  9351:         if ($quotaname eq 'course') {
                   9352:             if (($env{'course.'.$udom.'_'.$uname.'.num'} eq $uname) &&
                   9353:                 ($env{'course.'.$udom.'_'.$uname.'.domain'} eq $udom)) {
                   9354:                 $quota = $env{'course.'.$udom.'_'.$uname.'.internal.uploadquota'};
                   9355:             } else {
                   9356:                 my %cenv = &Apache::lonnet::coursedescription("$udom/$uname");
                   9357:                 $quota = $cenv{'internal.uploadquota'};
                   9358:             }
1.536     raeburn  9359:         } else {
1.1134    raeburn  9360:             if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
                   9361:                 if ($quotaname eq 'author') {
                   9362:                     $quota = $env{'environment.authorquota'};
                   9363:                 } else {
                   9364:                     $quota = $env{'environment.portfolioquota'};
                   9365:                 }
                   9366:                 $inststatus = $env{'environment.inststatus'};
                   9367:             } else {
                   9368:                 my %userenv = 
                   9369:                     &Apache::lonnet::get('environment',['portfolioquota',
                   9370:                                          'authorquota','inststatus'],$udom,$uname);
                   9371:                 my ($tmp) = keys(%userenv);
                   9372:                 if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   9373:                     if ($quotaname eq 'author') {
                   9374:                         $quota = $userenv{'authorquota'};
                   9375:                     } else {
                   9376:                         $quota = $userenv{'portfolioquota'};
                   9377:                     }
                   9378:                     $inststatus = $userenv{'inststatus'};
                   9379:                 } else {
                   9380:                     undef(%userenv);
                   9381:                 }
                   9382:             }
                   9383:         }
                   9384:         if ($quota eq '' || wantarray) {
                   9385:             if ($quotaname eq 'course') {
                   9386:                 my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
1.1165    raeburn  9387:                 if (($crstype eq 'official') || ($crstype eq 'unofficial') || 
                   9388:                     ($crstype eq 'community') || ($crstype eq 'textbook')) { 
1.1136    raeburn  9389:                     $defquota = $domdefs{$crstype.'quota'};
                   9390:                 }
                   9391:                 if ($defquota eq '') {
                   9392:                     $defquota = 500;
                   9393:                 }
1.1134    raeburn  9394:             } else {
                   9395:                 ($defquota,$settingstatus) = &default_quota($udom,$inststatus,$quotaname);
                   9396:             }
                   9397:             if ($quota eq '') {
                   9398:                 $quota = $defquota;
                   9399:                 $quotatype = 'default';
                   9400:             } else {
                   9401:                 $quotatype = 'custom';
                   9402:             }
1.472     raeburn  9403:         }
                   9404:     }
1.536     raeburn  9405:     if (wantarray) {
                   9406:         return ($quota,$quotatype,$settingstatus,$defquota);
                   9407:     } else {
                   9408:         return $quota;
                   9409:     }
1.472     raeburn  9410: }
                   9411: 
                   9412: ###############################################
                   9413: 
                   9414: =pod
                   9415: 
                   9416: =item * &default_quota()
                   9417: 
1.536     raeburn  9418: Retrieves default quota assigned for storage of user portfolio files,
                   9419: given an (optional) user's institutional status.
1.472     raeburn  9420: 
                   9421: Incoming parameters:
1.1142    raeburn  9422: 
1.472     raeburn  9423: 1. domain
1.536     raeburn  9424: 2. (Optional) institutional status(es).  This is a : separated list of 
                   9425:    status types (e.g., faculty, staff, student etc.)
                   9426:    which apply to the user for whom the default is being retrieved.
                   9427:    If the institutional status string in undefined, the domain
1.1134    raeburn  9428:    default quota will be returned.
                   9429: 3.  quota name - portfolio, author, or course
                   9430:    (if no quota name provided, defaults to portfolio).
1.472     raeburn  9431: 
                   9432: Returns:
1.1142    raeburn  9433: 
1.1163    raeburn  9434: 1. Default disk quota (in MB) for user portfolios in the domain.
1.536     raeburn  9435: 2. (Optional) institutional type which determined the value of the
                   9436:    default quota.
1.472     raeburn  9437: 
                   9438: If a value has been stored in the domain's configuration db,
                   9439: it will return that, otherwise it returns 20 (for backwards 
                   9440: compatibility with domains which have not set up a configuration
1.1163    raeburn  9441: db file; the original statically defined portfolio quota was 20 MB). 
1.472     raeburn  9442: 
1.536     raeburn  9443: If the user's status includes multiple types (e.g., staff and student),
                   9444: the largest default quota which applies to the user determines the
                   9445: default quota returned.
                   9446: 
1.472     raeburn  9447: =cut
                   9448: 
                   9449: ###############################################
                   9450: 
                   9451: 
                   9452: sub default_quota {
1.1134    raeburn  9453:     my ($udom,$inststatus,$quotaname) = @_;
1.536     raeburn  9454:     my ($defquota,$settingstatus);
                   9455:     my %quotahash = &Apache::lonnet::get_dom('configuration',
1.622     raeburn  9456:                                             ['quotas'],$udom);
1.1134    raeburn  9457:     my $key = 'defaultquota';
                   9458:     if ($quotaname eq 'author') {
                   9459:         $key = 'authorquota';
                   9460:     }
1.622     raeburn  9461:     if (ref($quotahash{'quotas'}) eq 'HASH') {
1.536     raeburn  9462:         if ($inststatus ne '') {
1.765     raeburn  9463:             my @statuses = map { &unescape($_); } split(/:/,$inststatus);
1.536     raeburn  9464:             foreach my $item (@statuses) {
1.1134    raeburn  9465:                 if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
                   9466:                     if ($quotahash{'quotas'}{$key}{$item} ne '') {
1.711     raeburn  9467:                         if ($defquota eq '') {
1.1134    raeburn  9468:                             $defquota = $quotahash{'quotas'}{$key}{$item};
1.711     raeburn  9469:                             $settingstatus = $item;
1.1134    raeburn  9470:                         } elsif ($quotahash{'quotas'}{$key}{$item} > $defquota) {
                   9471:                             $defquota = $quotahash{'quotas'}{$key}{$item};
1.711     raeburn  9472:                             $settingstatus = $item;
                   9473:                         }
                   9474:                     }
1.1134    raeburn  9475:                 } elsif ($key eq 'defaultquota') {
1.711     raeburn  9476:                     if ($quotahash{'quotas'}{$item} ne '') {
                   9477:                         if ($defquota eq '') {
                   9478:                             $defquota = $quotahash{'quotas'}{$item};
                   9479:                             $settingstatus = $item;
                   9480:                         } elsif ($quotahash{'quotas'}{$item} > $defquota) {
                   9481:                             $defquota = $quotahash{'quotas'}{$item};
                   9482:                             $settingstatus = $item;
                   9483:                         }
1.536     raeburn  9484:                     }
                   9485:                 }
                   9486:             }
                   9487:         }
                   9488:         if ($defquota eq '') {
1.1134    raeburn  9489:             if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
                   9490:                 $defquota = $quotahash{'quotas'}{$key}{'default'};
                   9491:             } elsif ($key eq 'defaultquota') {
1.711     raeburn  9492:                 $defquota = $quotahash{'quotas'}{'default'};
                   9493:             }
1.536     raeburn  9494:             $settingstatus = 'default';
1.1139    raeburn  9495:             if ($defquota eq '') {
                   9496:                 if ($quotaname eq 'author') {
                   9497:                     $defquota = 500;
                   9498:                 }
                   9499:             }
1.536     raeburn  9500:         }
                   9501:     } else {
                   9502:         $settingstatus = 'default';
1.1134    raeburn  9503:         if ($quotaname eq 'author') {
                   9504:             $defquota = 500;
                   9505:         } else {
                   9506:             $defquota = 20;
                   9507:         }
1.536     raeburn  9508:     }
                   9509:     if (wantarray) {
                   9510:         return ($defquota,$settingstatus);
1.472     raeburn  9511:     } else {
1.536     raeburn  9512:         return $defquota;
1.472     raeburn  9513:     }
                   9514: }
                   9515: 
1.1135    raeburn  9516: ###############################################
                   9517: 
                   9518: =pod
                   9519: 
1.1136    raeburn  9520: =item * &excess_filesize_warning()
1.1135    raeburn  9521: 
                   9522: Returns warning message if upload of file to authoring space, or copying
1.1136    raeburn  9523: of existing file within authoring space will cause quota for the authoring
1.1146    raeburn  9524: space to be exceeded.
1.1136    raeburn  9525: 
                   9526: Same, if upload of a file directly to a course/community via Course Editor
1.1137    raeburn  9527: will cause quota for uploaded content for the course to be exceeded.
1.1135    raeburn  9528: 
1.1165    raeburn  9529: Inputs: 7 
1.1136    raeburn  9530: 1. username or coursenum
1.1135    raeburn  9531: 2. domain
1.1136    raeburn  9532: 3. context ('author' or 'course')
1.1135    raeburn  9533: 4. filename of file for which action is being requested
                   9534: 5. filesize (kB) of file
                   9535: 6. action being taken: copy or upload.
1.1165    raeburn  9536: 7. quotatype (in course context -- official, unofficial, community or textbook).
1.1135    raeburn  9537: 
                   9538: Returns: 1 scalar: HTML to display containing warning if quota would be exceeded,
1.1142    raeburn  9539:          otherwise return null.
                   9540: 
                   9541: =back
1.1135    raeburn  9542: 
                   9543: =cut
                   9544: 
1.1136    raeburn  9545: sub excess_filesize_warning {
1.1165    raeburn  9546:     my ($uname,$udom,$context,$filename,$filesize,$action,$quotatype) = @_;
1.1136    raeburn  9547:     my $current_disk_usage = 0;
1.1165    raeburn  9548:     my $disk_quota = &get_user_quota($uname,$udom,$context,$quotatype); #expressed in MB
1.1136    raeburn  9549:     if ($context eq 'author') {
                   9550:         my $authorspace = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname";
                   9551:         $current_disk_usage = &Apache::lonnet::diskusage($udom,$uname,$authorspace);
                   9552:     } else {
                   9553:         foreach my $subdir ('docs','supplemental') {
                   9554:             $current_disk_usage += &Apache::lonnet::diskusage($udom,$uname,"userfiles/$subdir",1);
                   9555:         }
                   9556:     }
1.1135    raeburn  9557:     $disk_quota = int($disk_quota * 1000);
                   9558:     if (($current_disk_usage + $filesize) > $disk_quota) {
1.1179    bisitz   9559:         return '<p class="LC_warning">'.
1.1135    raeburn  9560:                 &mt("Unable to $action [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.",
1.1179    bisitz   9561:                     '<span class="LC_filename">'.$filename.'</span>',$filesize).'</p>'.
                   9562:                '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
1.1135    raeburn  9563:                             $disk_quota,$current_disk_usage).
                   9564:                '</p>';
                   9565:     }
                   9566:     return;
                   9567: }
                   9568: 
                   9569: ###############################################
                   9570: 
                   9571: 
1.1136    raeburn  9572: 
                   9573: 
1.384     raeburn  9574: sub get_secgrprole_info {
                   9575:     my ($cdom,$cnum,$needroles,$type)  = @_;
                   9576:     my %sections_count = &get_sections($cdom,$cnum);
                   9577:     my @sections =  (sort {$a <=> $b} keys(%sections_count));
                   9578:     my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
                   9579:     my @groups = sort(keys(%curr_groups));
                   9580:     my $allroles = [];
                   9581:     my $rolehash;
                   9582:     my $accesshash = {
                   9583:                      active => 'Currently has access',
                   9584:                      future => 'Will have future access',
                   9585:                      previous => 'Previously had access',
                   9586:                   };
                   9587:     if ($needroles) {
                   9588:         $rolehash = {'all' => 'all'};
1.385     albertel 9589:         my %user_roles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   9590: 	if (&Apache::lonnet::error(%user_roles)) {
                   9591: 	    undef(%user_roles);
                   9592: 	}
                   9593:         foreach my $item (keys(%user_roles)) {
1.384     raeburn  9594:             my ($role)=split(/\:/,$item,2);
                   9595:             if ($role eq 'cr') { next; }
                   9596:             if ($role =~ /^cr/) {
                   9597:                 $$rolehash{$role} = (split('/',$role))[3];
                   9598:             } else {
                   9599:                 $$rolehash{$role} = &Apache::lonnet::plaintext($role,$type);
                   9600:             }
                   9601:         }
                   9602:         foreach my $key (sort(keys(%{$rolehash}))) {
                   9603:             push(@{$allroles},$key);
                   9604:         }
                   9605:         push (@{$allroles},'st');
                   9606:         $$rolehash{'st'} = &Apache::lonnet::plaintext('st',$type);
                   9607:     }
                   9608:     return (\@sections,\@groups,$allroles,$rolehash,$accesshash);
                   9609: }
                   9610: 
1.555     raeburn  9611: sub user_picker {
1.994     raeburn  9612:     my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype,$context) = @_;
1.555     raeburn  9613:     my $currdom = $dom;
                   9614:     my %curr_selected = (
                   9615:                         srchin => 'dom',
1.580     raeburn  9616:                         srchby => 'lastname',
1.555     raeburn  9617:                       );
                   9618:     my $srchterm;
1.625     raeburn  9619:     if ((ref($srch) eq 'HASH') && ($env{'form.origform'} ne 'crtusername')) {
1.555     raeburn  9620:         if ($srch->{'srchby'} ne '') {
                   9621:             $curr_selected{'srchby'} = $srch->{'srchby'};
                   9622:         }
                   9623:         if ($srch->{'srchin'} ne '') {
                   9624:             $curr_selected{'srchin'} = $srch->{'srchin'};
                   9625:         }
                   9626:         if ($srch->{'srchtype'} ne '') {
                   9627:             $curr_selected{'srchtype'} = $srch->{'srchtype'};
                   9628:         }
                   9629:         if ($srch->{'srchdomain'} ne '') {
                   9630:             $currdom = $srch->{'srchdomain'};
                   9631:         }
                   9632:         $srchterm = $srch->{'srchterm'};
                   9633:     }
1.1222    damieng  9634:     my %html_lt=&Apache::lonlocal::texthash(
1.573     raeburn  9635:                     'usr'       => 'Search criteria',
1.563     raeburn  9636:                     'doma'      => 'Domain/institution to search',
1.558     albertel 9637:                     'uname'     => 'username',
                   9638:                     'lastname'  => 'last name',
1.555     raeburn  9639:                     'lastfirst' => 'last name, first name',
1.558     albertel 9640:                     'crs'       => 'in this course',
1.576     raeburn  9641:                     'dom'       => 'in selected LON-CAPA domain', 
1.558     albertel 9642:                     'alc'       => 'all LON-CAPA',
1.573     raeburn  9643:                     'instd'     => 'in institutional directory for selected domain',
1.558     albertel 9644:                     'exact'     => 'is',
                   9645:                     'contains'  => 'contains',
1.569     raeburn  9646:                     'begins'    => 'begins with',
1.1222    damieng  9647:                                        );
                   9648:     my %js_lt=&Apache::lonlocal::texthash(
1.571     raeburn  9649:                     'youm'      => "You must include some text to search for.",
                   9650:                     'thte'      => "The text you are searching for must contain at least two characters when using a 'begins' type search.",
                   9651:                     'thet'      => "The text you are searching for must contain at least three characters when using a 'contains' type search.",
                   9652:                     'yomc'      => "You must choose a domain when using an institutional directory search.",
                   9653:                     'ymcd'      => "You must choose a domain when using a domain search.",
                   9654:                     'whus'      => "When using searching by last,first you must include a comma as separator between last name and first name.",
                   9655:                     'whse'      => "When searching by last,first you must include at least one character in the first name.",
                   9656:                      'thfo'     => "The following need to be corrected before the search can be run:",
1.555     raeburn  9657:                                        );
1.1222    damieng  9658:     &html_escape(\%html_lt);
                   9659:     &js_escape(\%js_lt);
1.563     raeburn  9660:     my $domform = &select_dom_form($currdom,'srchdomain',1,1);
                   9661:     my $srchinsel = ' <select name="srchin">';
1.555     raeburn  9662: 
                   9663:     my @srchins = ('crs','dom','alc','instd');
                   9664: 
                   9665:     foreach my $option (@srchins) {
                   9666:         # FIXME 'alc' option unavailable until 
                   9667:         #       loncreateuser::print_user_query_page()
                   9668:         #       has been completed.
                   9669:         next if ($option eq 'alc');
1.880     raeburn  9670:         next if (($option eq 'crs') && ($env{'form.form'} eq 'requestcrs'));  
1.555     raeburn  9671:         next if ($option eq 'crs' && !$env{'request.course.id'});
1.563     raeburn  9672:         if ($curr_selected{'srchin'} eq $option) {
                   9673:             $srchinsel .= ' 
1.1222    damieng  9674:    <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.563     raeburn  9675:         } else {
                   9676:             $srchinsel .= '
1.1222    damieng  9677:    <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.563     raeburn  9678:         }
1.555     raeburn  9679:     }
1.563     raeburn  9680:     $srchinsel .= "\n  </select>\n";
1.555     raeburn  9681: 
                   9682:     my $srchbysel =  ' <select name="srchby">';
1.580     raeburn  9683:     foreach my $option ('lastname','lastfirst','uname') {
1.555     raeburn  9684:         if ($curr_selected{'srchby'} eq $option) {
                   9685:             $srchbysel .= '
1.1222    damieng  9686:    <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.555     raeburn  9687:         } else {
                   9688:             $srchbysel .= '
1.1222    damieng  9689:    <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.555     raeburn  9690:          }
                   9691:     }
                   9692:     $srchbysel .= "\n  </select>\n";
                   9693: 
                   9694:     my $srchtypesel = ' <select name="srchtype">';
1.580     raeburn  9695:     foreach my $option ('begins','contains','exact') {
1.555     raeburn  9696:         if ($curr_selected{'srchtype'} eq $option) {
                   9697:             $srchtypesel .= '
1.1222    damieng  9698:    <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.555     raeburn  9699:         } else {
                   9700:             $srchtypesel .= '
1.1222    damieng  9701:    <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.555     raeburn  9702:         }
                   9703:     }
                   9704:     $srchtypesel .= "\n  </select>\n";
                   9705: 
1.558     albertel 9706:     my ($newuserscript,$new_user_create);
1.994     raeburn  9707:     my $context_dom = $env{'request.role.domain'};
                   9708:     if ($context eq 'requestcrs') {
                   9709:         if ($env{'form.coursedom'} ne '') { 
                   9710:             $context_dom = $env{'form.coursedom'};
                   9711:         }
                   9712:     }
1.556     raeburn  9713:     if ($forcenewuser) {
1.576     raeburn  9714:         if (ref($srch) eq 'HASH') {
1.994     raeburn  9715:             if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $context_dom) {
1.627     raeburn  9716:                 if ($cancreate) {
                   9717:                     $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>';
                   9718:                 } else {
1.799     bisitz   9719:                     my $helplink = 'javascript:helpMenu('."'display'".')';
1.627     raeburn  9720:                     my %usertypetext = (
                   9721:                         official   => 'institutional',
                   9722:                         unofficial => 'non-institutional',
                   9723:                     );
1.799     bisitz   9724:                     $new_user_create = '<p class="LC_warning">'
                   9725:                                       .&mt("You are not authorized to create new $usertypetext{$usertype} users in this domain.")
                   9726:                                       .' '
                   9727:                                       .&mt('Please contact the [_1]helpdesk[_2] for assistance.'
                   9728:                                           ,'<a href="'.$helplink.'">','</a>')
                   9729:                                       .'</p><br />';
1.627     raeburn  9730:                 }
1.576     raeburn  9731:             }
                   9732:         }
                   9733: 
1.556     raeburn  9734:         $newuserscript = <<"ENDSCRIPT";
                   9735: 
1.570     raeburn  9736: function setSearch(createnew,callingForm) {
1.556     raeburn  9737:     if (createnew == 1) {
1.570     raeburn  9738:         for (var i=0; i<callingForm.srchby.length; i++) {
                   9739:             if (callingForm.srchby.options[i].value == 'uname') {
                   9740:                 callingForm.srchby.selectedIndex = i;
1.556     raeburn  9741:             }
                   9742:         }
1.570     raeburn  9743:         for (var i=0; i<callingForm.srchin.length; i++) {
                   9744:             if ( callingForm.srchin.options[i].value == 'dom') {
                   9745: 		callingForm.srchin.selectedIndex = i;
1.556     raeburn  9746:             }
                   9747:         }
1.570     raeburn  9748:         for (var i=0; i<callingForm.srchtype.length; i++) {
                   9749:             if (callingForm.srchtype.options[i].value == 'exact') {
                   9750:                 callingForm.srchtype.selectedIndex = i;
1.556     raeburn  9751:             }
                   9752:         }
1.570     raeburn  9753:         for (var i=0; i<callingForm.srchdomain.length; i++) {
1.994     raeburn  9754:             if (callingForm.srchdomain.options[i].value == '$context_dom') {
1.570     raeburn  9755:                 callingForm.srchdomain.selectedIndex = i;
1.556     raeburn  9756:             }
                   9757:         }
                   9758:     }
                   9759: }
                   9760: ENDSCRIPT
1.558     albertel 9761: 
1.556     raeburn  9762:     }
                   9763: 
1.555     raeburn  9764:     my $output = <<"END_BLOCK";
1.556     raeburn  9765: <script type="text/javascript">
1.824     bisitz   9766: // <![CDATA[
1.570     raeburn  9767: function validateEntry(callingForm) {
1.558     albertel 9768: 
1.556     raeburn  9769:     var checkok = 1;
1.558     albertel 9770:     var srchin;
1.570     raeburn  9771:     for (var i=0; i<callingForm.srchin.length; i++) {
                   9772: 	if ( callingForm.srchin[i].checked ) {
                   9773: 	    srchin = callingForm.srchin[i].value;
1.558     albertel 9774: 	}
                   9775:     }
                   9776: 
1.570     raeburn  9777:     var srchtype = callingForm.srchtype.options[callingForm.srchtype.selectedIndex].value;
                   9778:     var srchby = callingForm.srchby.options[callingForm.srchby.selectedIndex].value;
                   9779:     var srchdomain = callingForm.srchdomain.options[callingForm.srchdomain.selectedIndex].value;
                   9780:     var srchterm =  callingForm.srchterm.value;
                   9781:     var srchin = callingForm.srchin.options[callingForm.srchin.selectedIndex].value;
1.556     raeburn  9782:     var msg = "";
                   9783: 
                   9784:     if (srchterm == "") {
                   9785:         checkok = 0;
1.1222    damieng  9786:         msg += "$js_lt{'youm'}\\n";
1.556     raeburn  9787:     }
                   9788: 
1.569     raeburn  9789:     if (srchtype== 'begins') {
                   9790:         if (srchterm.length < 2) {
                   9791:             checkok = 0;
1.1222    damieng  9792:             msg += "$js_lt{'thte'}\\n";
1.569     raeburn  9793:         }
                   9794:     }
                   9795: 
1.556     raeburn  9796:     if (srchtype== 'contains') {
                   9797:         if (srchterm.length < 3) {
                   9798:             checkok = 0;
1.1222    damieng  9799:             msg += "$js_lt{'thet'}\\n";
1.556     raeburn  9800:         }
                   9801:     }
                   9802:     if (srchin == 'instd') {
                   9803:         if (srchdomain == '') {
                   9804:             checkok = 0;
1.1222    damieng  9805:             msg += "$js_lt{'yomc'}\\n";
1.556     raeburn  9806:         }
                   9807:     }
                   9808:     if (srchin == 'dom') {
                   9809:         if (srchdomain == '') {
                   9810:             checkok = 0;
1.1222    damieng  9811:             msg += "$js_lt{'ymcd'}\\n";
1.556     raeburn  9812:         }
                   9813:     }
                   9814:     if (srchby == 'lastfirst') {
                   9815:         if (srchterm.indexOf(",") == -1) {
                   9816:             checkok = 0;
1.1222    damieng  9817:             msg += "$js_lt{'whus'}\\n";
1.556     raeburn  9818:         }
                   9819:         if (srchterm.indexOf(",") == srchterm.length -1) {
                   9820:             checkok = 0;
1.1222    damieng  9821:             msg += "$js_lt{'whse'}\\n";
1.556     raeburn  9822:         }
                   9823:     }
                   9824:     if (checkok == 0) {
1.1222    damieng  9825:         alert("$js_lt{'thfo'}\\n"+msg);
1.556     raeburn  9826:         return;
                   9827:     }
                   9828:     if (checkok == 1) {
1.570     raeburn  9829:         callingForm.submit();
1.556     raeburn  9830:     }
                   9831: }
                   9832: 
                   9833: $newuserscript
                   9834: 
1.824     bisitz   9835: // ]]>
1.556     raeburn  9836: </script>
1.558     albertel 9837: 
                   9838: $new_user_create
                   9839: 
1.555     raeburn  9840: END_BLOCK
1.558     albertel 9841: 
1.876     raeburn  9842:     $output .= &Apache::lonhtmlcommon::start_pick_box().
1.1222    damieng  9843:                &Apache::lonhtmlcommon::row_title($html_lt{'doma'}).
1.876     raeburn  9844:                $domform.
                   9845:                &Apache::lonhtmlcommon::row_closure().
1.1222    damieng  9846:                &Apache::lonhtmlcommon::row_title($html_lt{'usr'}).
1.876     raeburn  9847:                $srchbysel.
                   9848:                $srchtypesel. 
                   9849:                '<input type="text" size="15" name="srchterm" value="'.$srchterm.'" />'.
                   9850:                $srchinsel.
                   9851:                &Apache::lonhtmlcommon::row_closure(1). 
                   9852:                &Apache::lonhtmlcommon::end_pick_box().
                   9853:                '<br />';
1.555     raeburn  9854:     return $output;
                   9855: }
                   9856: 
1.612     raeburn  9857: sub user_rule_check {
1.615     raeburn  9858:     my ($usershash,$checks,$alerts,$rulematch,$inst_results,$curr_rules,$got_rules) = @_;
1.1226    raeburn  9859:     my ($response,%inst_response);
1.612     raeburn  9860:     if (ref($usershash) eq 'HASH') {
1.1226    raeburn  9861:         if (keys(%{$usershash}) > 1) {
                   9862:             my (%by_username,%by_id,%userdoms);
                   9863:             my $checkid; 
                   9864:             if (ref($checks) eq 'HASH') {
                   9865:                 if ((!defined($checks->{'username'})) && (defined($checks->{'id'}))) {
                   9866:                     $checkid = 1;
                   9867:                 }
                   9868:             }
                   9869:             foreach my $user (keys(%{$usershash})) {
                   9870:                 my ($uname,$udom) = split(/:/,$user);
                   9871:                 if ($checkid) {
                   9872:                     if (ref($usershash->{$user}) eq 'HASH') {
                   9873:                         if ($usershash->{$user}->{'id'} ne '') {
1.1227    raeburn  9874:                             $by_id{$udom}{$usershash->{$user}->{'id'}} = $uname; 
1.1226    raeburn  9875:                             $userdoms{$udom} = 1;
1.1227    raeburn  9876:                             if (ref($inst_results) eq 'HASH') {
                   9877:                                 $inst_results->{$uname.':'.$udom} = {};
                   9878:                             }
1.1226    raeburn  9879:                         }
                   9880:                     }
                   9881:                 } else {
                   9882:                     $by_username{$udom}{$uname} = 1;
                   9883:                     $userdoms{$udom} = 1;
1.1227    raeburn  9884:                     if (ref($inst_results) eq 'HASH') {
                   9885:                         $inst_results->{$uname.':'.$udom} = {};
                   9886:                     }
1.1226    raeburn  9887:                 }
                   9888:             }
                   9889:             foreach my $udom (keys(%userdoms)) {
                   9890:                 if (!$got_rules->{$udom}) {
                   9891:                     my %domconfig = &Apache::lonnet::get_dom('configuration',
                   9892:                                                              ['usercreation'],$udom);
                   9893:                     if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   9894:                         foreach my $item ('username','id') {
                   9895:                             if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
1.1227    raeburn  9896:                                 $$curr_rules{$udom}{$item} =
                   9897:                                     $domconfig{'usercreation'}{$item.'_rule'};
1.1226    raeburn  9898:                             }
                   9899:                         }
                   9900:                     }
                   9901:                     $got_rules->{$udom} = 1;
                   9902:                 }
1.612     raeburn  9903:             }
1.1226    raeburn  9904:             if ($checkid) {
                   9905:                 foreach my $udom (keys(%by_id)) {
                   9906:                     my ($outcome,$results) = &Apache::lonnet::get_multiple_instusers($udom,$by_id{$udom},'id');
                   9907:                     if ($outcome eq 'ok') {
1.1227    raeburn  9908:                         foreach my $id (keys(%{$by_id{$udom}})) {
                   9909:                             my $uname = $by_id{$udom}{$id};
                   9910:                             $inst_response{$uname.':'.$udom} = $outcome;
                   9911:                         }
1.1226    raeburn  9912:                         if (ref($results) eq 'HASH') {
                   9913:                             foreach my $uname (keys(%{$results})) {
1.1227    raeburn  9914:                                 if (exists($inst_response{$uname.':'.$udom})) {
                   9915:                                     $inst_response{$uname.':'.$udom} = $outcome;
                   9916:                                     $inst_results->{$uname.':'.$udom} = $results->{$uname};
                   9917:                                 }
1.1226    raeburn  9918:                             }
                   9919:                         }
                   9920:                     }
1.612     raeburn  9921:                 }
1.615     raeburn  9922:             } else {
1.1226    raeburn  9923:                 foreach my $udom (keys(%by_username)) {
                   9924:                     my ($outcome,$results) = &Apache::lonnet::get_multiple_instusers($udom,$by_username{$udom});
                   9925:                     if ($outcome eq 'ok') {
1.1227    raeburn  9926:                         foreach my $uname (keys(%{$by_username{$udom}})) {
                   9927:                             $inst_response{$uname.':'.$udom} = $outcome;
                   9928:                         }
1.1226    raeburn  9929:                         if (ref($results) eq 'HASH') {
                   9930:                             foreach my $uname (keys(%{$results})) {
                   9931:                                 $inst_results->{$uname.':'.$udom} = $results->{$uname};
                   9932:                             }
                   9933:                         }
                   9934:                     }
                   9935:                 }
1.612     raeburn  9936:             }
1.1226    raeburn  9937:         } elsif (keys(%{$usershash}) == 1) {
                   9938:             my $user = (keys(%{$usershash}))[0];
                   9939:             my ($uname,$udom) = split(/:/,$user);
                   9940:             if (($udom ne '') && ($uname ne '')) {
                   9941:                 if (ref($usershash->{$user}) eq 'HASH') {
                   9942:                     if (ref($checks) eq 'HASH') {
                   9943:                         if (defined($checks->{'username'})) {
                   9944:                             ($inst_response{$user},%{$inst_results->{$user}}) = 
                   9945:                                 &Apache::lonnet::get_instuser($udom,$uname);
                   9946:                         } elsif (defined($checks->{'id'})) {
                   9947:                             if ($usershash->{$user}->{'id'} ne '') {
                   9948:                                 ($inst_response{$user},%{$inst_results->{$user}}) =
                   9949:                                     &Apache::lonnet::get_instuser($udom,undef,
                   9950:                                                                   $usershash->{$user}->{'id'});
                   9951:                             } else {
                   9952:                                 ($inst_response{$user},%{$inst_results->{$user}}) =
                   9953:                                     &Apache::lonnet::get_instuser($udom,$uname);
                   9954:                             }
1.585     raeburn  9955:                         }
1.1226    raeburn  9956:                     } else {
                   9957:                        ($inst_response{$user},%{$inst_results->{$user}}) =
                   9958:                             &Apache::lonnet::get_instuser($udom,$uname);
                   9959:                        return;
                   9960:                     }
                   9961:                     if (!$got_rules->{$udom}) {
                   9962:                         my %domconfig = &Apache::lonnet::get_dom('configuration',
                   9963:                                                                  ['usercreation'],$udom);
                   9964:                         if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   9965:                             foreach my $item ('username','id') {
                   9966:                                 if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
                   9967:                                    $$curr_rules{$udom}{$item} = 
                   9968:                                        $domconfig{'usercreation'}{$item.'_rule'};
                   9969:                                 }
                   9970:                             }
                   9971:                         }
                   9972:                         $got_rules->{$udom} = 1;
1.585     raeburn  9973:                     }
                   9974:                 }
1.1226    raeburn  9975:             } else {
                   9976:                 return;
                   9977:             }
                   9978:         } else {
                   9979:             return;
                   9980:         }
                   9981:         foreach my $user (keys(%{$usershash})) {
                   9982:             my ($uname,$udom) = split(/:/,$user);
                   9983:             next if (($udom eq '') || ($uname eq ''));
                   9984:             my $id;
1.1227    raeburn  9985:             if (ref($inst_results) eq 'HASH') {
                   9986:                 if (ref($inst_results->{$user}) eq 'HASH') {
                   9987:                     $id = $inst_results->{$user}->{'id'};
                   9988:                 }
                   9989:             }
                   9990:             if ($id eq '') { 
                   9991:                 if (ref($usershash->{$user})) {
                   9992:                     $id = $usershash->{$user}->{'id'};
                   9993:                 }
1.585     raeburn  9994:             }
1.612     raeburn  9995:             foreach my $item (keys(%{$checks})) {
                   9996:                 if (ref($$curr_rules{$udom}) eq 'HASH') {
                   9997:                     if (ref($$curr_rules{$udom}{$item}) eq 'ARRAY') {
                   9998:                         if (@{$$curr_rules{$udom}{$item}} > 0) {
1.1226    raeburn  9999:                             my %rule_check = &Apache::lonnet::inst_rulecheck($udom,$uname,$id,$item,
                   10000:                                                                              $$curr_rules{$udom}{$item});
1.612     raeburn  10001:                             foreach my $rule (@{$$curr_rules{$udom}{$item}}) {
                   10002:                                 if ($rule_check{$rule}) {
                   10003:                                     $$rulematch{$user}{$item} = $rule;
1.1226    raeburn  10004:                                     if ($inst_response{$user} eq 'ok') {
1.615     raeburn  10005:                                         if (ref($inst_results) eq 'HASH') {
                   10006:                                             if (ref($inst_results->{$user}) eq 'HASH') {
                   10007:                                                 if (keys(%{$inst_results->{$user}}) == 0) {
                   10008:                                                     $$alerts{$item}{$udom}{$uname} = 1;
1.1227    raeburn  10009:                                                 } elsif ($item eq 'id') {
                   10010:                                                     if ($inst_results->{$user}->{'id'} eq '') {
                   10011:                                                         $$alerts{$item}{$udom}{$uname} = 1;
                   10012:                                                     }
1.615     raeburn  10013:                                                 }
1.612     raeburn  10014:                                             }
                   10015:                                         }
1.615     raeburn  10016:                                     }
                   10017:                                     last;
1.585     raeburn  10018:                                 }
                   10019:                             }
                   10020:                         }
                   10021:                     }
                   10022:                 }
                   10023:             }
                   10024:         }
                   10025:     }
1.612     raeburn  10026:     return;
                   10027: }
                   10028: 
                   10029: sub user_rule_formats {
                   10030:     my ($domain,$domdesc,$curr_rules,$check) = @_;
                   10031:     my %text = ( 
                   10032:                  'username' => 'Usernames',
                   10033:                  'id'       => 'IDs',
                   10034:                );
                   10035:     my $output;
                   10036:     my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($domain,$check);
                   10037:     if ((ref($rules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
                   10038:         if (@{$ruleorder} > 0) {
1.1102    raeburn  10039:             $output = '<br />'.
                   10040:                       &mt($text{$check}.' with the following format(s) may [_1]only[_2] be used for verified users at [_3]:',
                   10041:                           '<span class="LC_cusr_emph">','</span>',$domdesc).
                   10042:                       ' <ul>';
1.612     raeburn  10043:             foreach my $rule (@{$ruleorder}) {
                   10044:                 if (ref($curr_rules) eq 'ARRAY') {
                   10045:                     if (grep(/^\Q$rule\E$/,@{$curr_rules})) {
                   10046:                         if (ref($rules->{$rule}) eq 'HASH') {
                   10047:                             $output .= '<li>'.$rules->{$rule}{'name'}.': '.
                   10048:                                         $rules->{$rule}{'desc'}.'</li>';
                   10049:                         }
                   10050:                     }
                   10051:                 }
                   10052:             }
                   10053:             $output .= '</ul>';
                   10054:         }
                   10055:     }
                   10056:     return $output;
                   10057: }
                   10058: 
                   10059: sub instrule_disallow_msg {
1.615     raeburn  10060:     my ($checkitem,$domdesc,$count,$mode) = @_;
1.612     raeburn  10061:     my $response;
                   10062:     my %text = (
                   10063:                   item   => 'username',
                   10064:                   items  => 'usernames',
                   10065:                   match  => 'matches',
                   10066:                   do     => 'does',
                   10067:                   action => 'a username',
                   10068:                   one    => 'one',
                   10069:                );
                   10070:     if ($count > 1) {
                   10071:         $text{'item'} = 'usernames';
                   10072:         $text{'match'} ='match';
                   10073:         $text{'do'} = 'do';
                   10074:         $text{'action'} = 'usernames',
                   10075:         $text{'one'} = 'ones';
                   10076:     }
                   10077:     if ($checkitem eq 'id') {
                   10078:         $text{'items'} = 'IDs';
                   10079:         $text{'item'} = 'ID';
                   10080:         $text{'action'} = 'an ID';
1.615     raeburn  10081:         if ($count > 1) {
                   10082:             $text{'item'} = 'IDs';
                   10083:             $text{'action'} = 'IDs';
                   10084:         }
1.612     raeburn  10085:     }
1.674     bisitz   10086:     $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  10087:     if ($mode eq 'upload') {
                   10088:         if ($checkitem eq 'username') {
                   10089:             $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'}.");
                   10090:         } elsif ($checkitem eq 'id') {
1.674     bisitz   10091:             $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  10092:         }
1.669     raeburn  10093:     } elsif ($mode eq 'selfcreate') {
                   10094:         if ($checkitem eq 'id') {
                   10095:             $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.");
                   10096:         }
1.615     raeburn  10097:     } else {
                   10098:         if ($checkitem eq 'username') {
                   10099:             $response .= &mt("You must choose $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
                   10100:         } elsif ($checkitem eq 'id') {
                   10101:             $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.");
                   10102:         }
1.612     raeburn  10103:     }
                   10104:     return $response;
1.585     raeburn  10105: }
                   10106: 
1.624     raeburn  10107: sub personal_data_fieldtitles {
                   10108:     my %fieldtitles = &Apache::lonlocal::texthash (
                   10109:                         id => 'Student/Employee ID',
                   10110:                         permanentemail => 'E-mail address',
                   10111:                         lastname => 'Last Name',
                   10112:                         firstname => 'First Name',
                   10113:                         middlename => 'Middle Name',
                   10114:                         generation => 'Generation',
                   10115:                         gen => 'Generation',
1.765     raeburn  10116:                         inststatus => 'Affiliation',
1.624     raeburn  10117:                    );
                   10118:     return %fieldtitles;
                   10119: }
                   10120: 
1.642     raeburn  10121: sub sorted_inst_types {
                   10122:     my ($dom) = @_;
1.1185    raeburn  10123:     my ($usertypes,$order);
                   10124:     my %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
                   10125:     if (ref($domdefaults{'inststatus'}) eq 'HASH') {
                   10126:         $usertypes = $domdefaults{'inststatus'}{'inststatustypes'};
                   10127:         $order = $domdefaults{'inststatus'}{'inststatusorder'};
                   10128:     } else {
                   10129:         ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom);
                   10130:     }
1.642     raeburn  10131:     my $othertitle = &mt('All users');
                   10132:     if ($env{'request.course.id'}) {
1.668     raeburn  10133:         $othertitle  = &mt('Any users');
1.642     raeburn  10134:     }
                   10135:     my @types;
                   10136:     if (ref($order) eq 'ARRAY') {
                   10137:         @types = @{$order};
                   10138:     }
                   10139:     if (@types == 0) {
                   10140:         if (ref($usertypes) eq 'HASH') {
                   10141:             @types = sort(keys(%{$usertypes}));
                   10142:         }
                   10143:     }
                   10144:     if (keys(%{$usertypes}) > 0) {
                   10145:         $othertitle = &mt('Other users');
                   10146:     }
                   10147:     return ($othertitle,$usertypes,\@types);
                   10148: }
                   10149: 
1.645     raeburn  10150: sub get_institutional_codes {
                   10151:     my ($settings,$allcourses,$LC_code) = @_;
                   10152: # Get complete list of course sections to update
                   10153:     my @currsections = ();
                   10154:     my @currxlists = ();
                   10155:     my $coursecode = $$settings{'internal.coursecode'};
                   10156: 
                   10157:     if ($$settings{'internal.sectionnums'} ne '') {
                   10158:         @currsections = split(/,/,$$settings{'internal.sectionnums'});
                   10159:     }
                   10160: 
                   10161:     if ($$settings{'internal.crosslistings'} ne '') {
                   10162:         @currxlists = split(/,/,$$settings{'internal.crosslistings'});
                   10163:     }
                   10164: 
                   10165:     if (@currxlists > 0) {
                   10166:         foreach (@currxlists) {
                   10167:             if (m/^([^:]+):(\w*)$/) {
                   10168:                 unless (grep/^$1$/,@{$allcourses}) {
                   10169:                     push @{$allcourses},$1;
                   10170:                     $$LC_code{$1} = $2;
                   10171:                 }
                   10172:             }
                   10173:         }
                   10174:     }
                   10175:  
                   10176:     if (@currsections > 0) {
                   10177:         foreach (@currsections) {
                   10178:             if (m/^(\w+):(\w*)$/) {
                   10179:                 my $sec = $coursecode.$1;
                   10180:                 my $lc_sec = $2;
                   10181:                 unless (grep/^$sec$/,@{$allcourses}) {
                   10182:                     push @{$allcourses},$sec;
                   10183:                     $$LC_code{$sec} = $lc_sec;
                   10184:                 }
                   10185:             }
                   10186:         }
                   10187:     }
                   10188:     return;
                   10189: }
                   10190: 
1.971     raeburn  10191: sub get_standard_codeitems {
                   10192:     return ('Year','Semester','Department','Number','Section');
                   10193: }
                   10194: 
1.112     bowersj2 10195: =pod
                   10196: 
1.780     raeburn  10197: =head1 Slot Helpers
                   10198: 
                   10199: =over 4
                   10200: 
                   10201: =item * sorted_slots()
                   10202: 
1.1040    raeburn  10203: Sorts an array of slot names in order of an optional sort key,
                   10204: default sort is by slot start time (earliest first). 
1.780     raeburn  10205: 
                   10206: Inputs:
                   10207: 
                   10208: =over 4
                   10209: 
                   10210: slotsarr  - Reference to array of unsorted slot names.
                   10211: 
                   10212: slots     - Reference to hash of hash, where outer hash keys are slot names.
                   10213: 
1.1040    raeburn  10214: sortkey   - Name of key in inner hash to be sorted on (e.g., starttime).
                   10215: 
1.549     albertel 10216: =back
                   10217: 
1.780     raeburn  10218: Returns:
                   10219: 
                   10220: =over 4
                   10221: 
1.1040    raeburn  10222: sorted   - An array of slot names sorted by a specified sort key 
                   10223:            (default sort key is start time of the slot).
1.780     raeburn  10224: 
                   10225: =back
                   10226: 
                   10227: =cut
                   10228: 
                   10229: 
                   10230: sub sorted_slots {
1.1040    raeburn  10231:     my ($slotsarr,$slots,$sortkey) = @_;
                   10232:     if ($sortkey eq '') {
                   10233:         $sortkey = 'starttime';
                   10234:     }
1.780     raeburn  10235:     my @sorted;
                   10236:     if ((ref($slotsarr) eq 'ARRAY') && (ref($slots) eq 'HASH')) {
                   10237:         @sorted =
                   10238:             sort {
                   10239:                      if (ref($slots->{$a}) && ref($slots->{$b})) {
1.1040    raeburn  10240:                          return $slots->{$a}{$sortkey} <=> $slots->{$b}{$sortkey}
1.780     raeburn  10241:                      }
                   10242:                      if (ref($slots->{$a})) { return -1;}
                   10243:                      if (ref($slots->{$b})) { return 1;}
                   10244:                      return 0;
                   10245:                  } @{$slotsarr};
                   10246:     }
                   10247:     return @sorted;
                   10248: }
                   10249: 
1.1040    raeburn  10250: =pod
                   10251: 
                   10252: =item * get_future_slots()
                   10253: 
                   10254: Inputs:
                   10255: 
                   10256: =over 4
                   10257: 
                   10258: cnum - course number
                   10259: 
                   10260: cdom - course domain
                   10261: 
                   10262: now - current UNIX time
                   10263: 
                   10264: symb - optional symb
                   10265: 
                   10266: =back
                   10267: 
                   10268: Returns:
                   10269: 
                   10270: =over 4
                   10271: 
                   10272: sorted_reservable - ref to array of student_schedulable slots currently 
                   10273:                     reservable, ordered by end date of reservation period.
                   10274: 
                   10275: reservable_now - ref to hash of student_schedulable slots currently
                   10276:                  reservable.
                   10277: 
                   10278:     Keys in inner hash are:
                   10279:     (a) symb: either blank or symb to which slot use is restricted.
                   10280:     (b) endreserve: end date of reservation period. 
                   10281: 
                   10282: sorted_future - ref to array of student_schedulable slots reservable in
                   10283:                 the future, ordered by start date of reservation period.
                   10284: 
                   10285: future_reservable - ref to hash of student_schedulable slots reservable
                   10286:                     in the future.
                   10287: 
                   10288:     Keys in inner hash are:
                   10289:     (a) symb: either blank or symb to which slot use is restricted.
                   10290:     (b) startreserve:  start date of reservation period.
                   10291: 
                   10292: =back
                   10293: 
                   10294: =cut
                   10295: 
                   10296: sub get_future_slots {
                   10297:     my ($cnum,$cdom,$now,$symb) = @_;
1.1229    raeburn  10298:     my $map;
                   10299:     if ($symb) {
                   10300:         ($map) = &Apache::lonnet::decode_symb($symb);
                   10301:     }
1.1040    raeburn  10302:     my (%reservable_now,%future_reservable,@sorted_reservable,@sorted_future);
                   10303:     my %slots = &Apache::lonnet::get_course_slots($cnum,$cdom);
                   10304:     foreach my $slot (keys(%slots)) {
                   10305:         next unless($slots{$slot}->{'type'} eq 'schedulable_student');
                   10306:         if ($symb) {
1.1229    raeburn  10307:             if ($slots{$slot}->{'symb'} ne '') {
                   10308:                 my $canuse;
                   10309:                 my %oksymbs;
                   10310:                 my @slotsymbs = split(/\s*,\s*/,$slots{$slot}->{'symb'});
                   10311:                 map { $oksymbs{$_} = 1; } @slotsymbs;
                   10312:                 if ($oksymbs{$symb}) {
                   10313:                     $canuse = 1;
                   10314:                 } else {
                   10315:                     foreach my $item (@slotsymbs) {
                   10316:                         if ($item =~ /\.(page|sequence)$/) {
                   10317:                             (undef,undef,my $sloturl) = &Apache::lonnet::decode_symb($item);
                   10318:                             if (($map ne '') && ($map eq $sloturl)) {
                   10319:                                 $canuse = 1;
                   10320:                                 last;
                   10321:                             }
                   10322:                         }
                   10323:                     }
                   10324:                 }
                   10325:                 next unless ($canuse);
                   10326:             }
1.1040    raeburn  10327:         }
                   10328:         if (($slots{$slot}->{'starttime'} > $now) &&
                   10329:             ($slots{$slot}->{'endtime'} > $now)) {
                   10330:             if (($slots{$slot}->{'allowedsections'}) || ($slots{$slot}->{'allowedusers'})) {
                   10331:                 my $userallowed = 0;
                   10332:                 if ($slots{$slot}->{'allowedsections'}) {
                   10333:                     my @allowed_sec = split(',',$slots{$slot}->{'allowedsections'});
                   10334:                     if (!defined($env{'request.role.sec'})
                   10335:                         && grep(/^No section assigned$/,@allowed_sec)) {
                   10336:                         $userallowed=1;
                   10337:                     } else {
                   10338:                         if (grep(/^\Q$env{'request.role.sec'}\E$/,@allowed_sec)) {
                   10339:                             $userallowed=1;
                   10340:                         }
                   10341:                     }
                   10342:                     unless ($userallowed) {
                   10343:                         if (defined($env{'request.course.groups'})) {
                   10344:                             my @groups = split(/:/,$env{'request.course.groups'});
                   10345:                             foreach my $group (@groups) {
                   10346:                                 if (grep(/^\Q$group\E$/,@allowed_sec)) {
                   10347:                                     $userallowed=1;
                   10348:                                     last;
                   10349:                                 }
                   10350:                             }
                   10351:                         }
                   10352:                     }
                   10353:                 }
                   10354:                 if ($slots{$slot}->{'allowedusers'}) {
                   10355:                     my @allowed_users = split(',',$slots{$slot}->{'allowedusers'});
                   10356:                     my $user = $env{'user.name'}.':'.$env{'user.domain'};
                   10357:                     if (grep(/^\Q$user\E$/,@allowed_users)) {
                   10358:                         $userallowed = 1;
                   10359:                     }
                   10360:                 }
                   10361:                 next unless($userallowed);
                   10362:             }
                   10363:             my $startreserve = $slots{$slot}->{'startreserve'};
                   10364:             my $endreserve = $slots{$slot}->{'endreserve'};
                   10365:             my $symb = $slots{$slot}->{'symb'};
                   10366:             if (($startreserve < $now) &&
                   10367:                 (!$endreserve || $endreserve > $now)) {
                   10368:                 my $lastres = $endreserve;
                   10369:                 if (!$lastres) {
                   10370:                     $lastres = $slots{$slot}->{'starttime'};
                   10371:                 }
                   10372:                 $reservable_now{$slot} = {
                   10373:                                            symb       => $symb,
                   10374:                                            endreserve => $lastres
                   10375:                                          };
                   10376:             } elsif (($startreserve > $now) &&
                   10377:                      (!$endreserve || $endreserve > $startreserve)) {
                   10378:                 $future_reservable{$slot} = {
                   10379:                                               symb         => $symb,
                   10380:                                               startreserve => $startreserve
                   10381:                                             };
                   10382:             }
                   10383:         }
                   10384:     }
                   10385:     my @unsorted_reservable = keys(%reservable_now);
                   10386:     if (@unsorted_reservable > 0) {
                   10387:         @sorted_reservable = 
                   10388:             &sorted_slots(\@unsorted_reservable,\%reservable_now,'endreserve');
                   10389:     }
                   10390:     my @unsorted_future = keys(%future_reservable);
                   10391:     if (@unsorted_future > 0) {
                   10392:         @sorted_future =
                   10393:             &sorted_slots(\@unsorted_future,\%future_reservable,'startreserve');
                   10394:     }
                   10395:     return (\@sorted_reservable,\%reservable_now,\@sorted_future,\%future_reservable);
                   10396: }
1.780     raeburn  10397: 
                   10398: =pod
                   10399: 
1.1057    foxr     10400: =back
                   10401: 
1.549     albertel 10402: =head1 HTTP Helpers
                   10403: 
                   10404: =over 4
                   10405: 
1.648     raeburn  10406: =item * &get_unprocessed_cgi($query,$possible_names)
1.112     bowersj2 10407: 
1.258     albertel 10408: Modify the %env hash to contain unprocessed CGI form parameters held in
1.112     bowersj2 10409: $query.  The parameters listed in $possible_names (an array reference),
1.258     albertel 10410: will be set in $env{'form.name'} if they do not already exist.
1.112     bowersj2 10411: 
                   10412: Typically called with $ENV{'QUERY_STRING'} as the first parameter.  
                   10413: $possible_names is an ref to an array of form element names.  As an example:
                   10414: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']);
1.258     albertel 10415: will result in $env{'form.uname'} and $env{'form.udom'} being set.
1.112     bowersj2 10416: 
                   10417: =cut
1.1       albertel 10418: 
1.6       albertel 10419: sub get_unprocessed_cgi {
1.25      albertel 10420:   my ($query,$possible_names)= @_;
1.26      matthew  10421:   # $Apache::lonxml::debug=1;
1.356     albertel 10422:   foreach my $pair (split(/&/,$query)) {
                   10423:     my ($name, $value) = split(/=/,$pair);
1.369     www      10424:     $name = &unescape($name);
1.25      albertel 10425:     if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
                   10426:       $value =~ tr/+/ /;
                   10427:       $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.258     albertel 10428:       unless (defined($env{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25      albertel 10429:     }
1.16      harris41 10430:   }
1.6       albertel 10431: }
                   10432: 
1.112     bowersj2 10433: =pod
                   10434: 
1.648     raeburn  10435: =item * &cacheheader() 
1.112     bowersj2 10436: 
                   10437: returns cache-controlling header code
                   10438: 
                   10439: =cut
                   10440: 
1.7       albertel 10441: sub cacheheader {
1.258     albertel 10442:     unless ($env{'request.method'} eq 'GET') { return ''; }
1.216     albertel 10443:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
                   10444:     my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
1.7       albertel 10445:                 <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
                   10446:                 <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
1.216     albertel 10447:     return $output;
1.7       albertel 10448: }
                   10449: 
1.112     bowersj2 10450: =pod
                   10451: 
1.648     raeburn  10452: =item * &no_cache($r) 
1.112     bowersj2 10453: 
                   10454: specifies header code to not have cache
                   10455: 
                   10456: =cut
                   10457: 
1.9       albertel 10458: sub no_cache {
1.216     albertel 10459:     my ($r) = @_;
                   10460:     if ($ENV{'REQUEST_METHOD'} ne 'GET' &&
1.258     albertel 10461: 	$env{'request.method'} ne 'GET') { return ''; }
1.216     albertel 10462:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime(time));
                   10463:     $r->no_cache(1);
                   10464:     $r->header_out("Expires" => $date);
                   10465:     $r->header_out("Pragma" => "no-cache");
1.123     www      10466: }
                   10467: 
                   10468: sub content_type {
1.181     albertel 10469:     my ($r,$type,$charset) = @_;
1.299     foxr     10470:     if ($r) {
                   10471: 	#  Note that printout.pl calls this with undef for $r.
                   10472: 	&no_cache($r);
                   10473:     }
1.258     albertel 10474:     if ($env{'browser.mathml'} && $type eq 'text/html') { $type='text/xml'; }
1.181     albertel 10475:     unless ($charset) {
                   10476: 	$charset=&Apache::lonlocal::current_encoding;
                   10477:     }
                   10478:     if ($charset) { $type.='; charset='.$charset; }
                   10479:     if ($r) {
                   10480: 	$r->content_type($type);
                   10481:     } else {
                   10482: 	print("Content-type: $type\n\n");
                   10483:     }
1.9       albertel 10484: }
1.25      albertel 10485: 
1.112     bowersj2 10486: =pod
                   10487: 
1.648     raeburn  10488: =item * &add_to_env($name,$value) 
1.112     bowersj2 10489: 
1.258     albertel 10490: adds $name to the %env hash with value
1.112     bowersj2 10491: $value, if $name already exists, the entry is converted to an array
                   10492: reference and $value is added to the array.
                   10493: 
                   10494: =cut
                   10495: 
1.25      albertel 10496: sub add_to_env {
                   10497:   my ($name,$value)=@_;
1.258     albertel 10498:   if (defined($env{$name})) {
                   10499:     if (ref($env{$name})) {
1.25      albertel 10500:       #already have multiple values
1.258     albertel 10501:       push(@{ $env{$name} },$value);
1.25      albertel 10502:     } else {
                   10503:       #first time seeing multiple values, convert hash entry to an arrayref
1.258     albertel 10504:       my $first=$env{$name};
                   10505:       undef($env{$name});
                   10506:       push(@{ $env{$name} },$first,$value);
1.25      albertel 10507:     }
                   10508:   } else {
1.258     albertel 10509:     $env{$name}=$value;
1.25      albertel 10510:   }
1.31      albertel 10511: }
1.149     albertel 10512: 
                   10513: =pod
                   10514: 
1.648     raeburn  10515: =item * &get_env_multiple($name) 
1.149     albertel 10516: 
1.258     albertel 10517: gets $name from the %env hash, it seemlessly handles the cases where multiple
1.149     albertel 10518: values may be defined and end up as an array ref.
                   10519: 
                   10520: returns an array of values
                   10521: 
                   10522: =cut
                   10523: 
                   10524: sub get_env_multiple {
                   10525:     my ($name) = @_;
                   10526:     my @values;
1.258     albertel 10527:     if (defined($env{$name})) {
1.149     albertel 10528:         # exists is it an array
1.258     albertel 10529:         if (ref($env{$name})) {
                   10530:             @values=@{ $env{$name} };
1.149     albertel 10531:         } else {
1.258     albertel 10532:             $values[0]=$env{$name};
1.149     albertel 10533:         }
                   10534:     }
                   10535:     return(@values);
                   10536: }
                   10537: 
1.660     raeburn  10538: sub ask_for_embedded_content {
                   10539:     my ($actionurl,$state,$allfiles,$codebase,$args)=@_;
1.1071    raeburn  10540:     my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges,
1.1085    raeburn  10541:         %currsubfile,%unused,$rem);
1.1071    raeburn  10542:     my $counter = 0;
                   10543:     my $numnew = 0;
1.987     raeburn  10544:     my $numremref = 0;
                   10545:     my $numinvalid = 0;
                   10546:     my $numpathchg = 0;
                   10547:     my $numexisting = 0;
1.1071    raeburn  10548:     my $numunused = 0;
                   10549:     my ($output,$upload_output,$toplevel,$url,$udom,$uname,$getpropath,$cdom,$cnum,
1.1156    raeburn  10550:         $fileloc,$filename,$delete_output,$modify_output,$title,$symb,$path,$navmap);
1.1071    raeburn  10551:     my $heading = &mt('Upload embedded files');
                   10552:     my $buttontext = &mt('Upload');
                   10553: 
1.1085    raeburn  10554:     if ($env{'request.course.id'}) {
1.1123    raeburn  10555:         if ($actionurl eq '/adm/dependencies') {
                   10556:             $navmap = Apache::lonnavmaps::navmap->new();
                   10557:         }
                   10558:         $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   10559:         $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1085    raeburn  10560:     }
1.1123    raeburn  10561:     if (($actionurl eq '/adm/portfolio') || 
                   10562:         ($actionurl eq '/adm/coursegrp_portfolio')) {
1.984     raeburn  10563:         my $current_path='/';
                   10564:         if ($env{'form.currentpath'}) {
                   10565:             $current_path = $env{'form.currentpath'};
                   10566:         }
                   10567:         if ($actionurl eq '/adm/coursegrp_portfolio') {
1.1123    raeburn  10568:             $udom = $cdom;
                   10569:             $uname = $cnum;
1.984     raeburn  10570:             $url = '/userfiles/groups/'.$env{'form.group'}.'/portfolio';
                   10571:         } else {
                   10572:             $udom = $env{'user.domain'};
                   10573:             $uname = $env{'user.name'};
                   10574:             $url = '/userfiles/portfolio';
                   10575:         }
1.987     raeburn  10576:         $toplevel = $url.'/';
1.984     raeburn  10577:         $url .= $current_path;
                   10578:         $getpropath = 1;
1.987     raeburn  10579:     } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
                   10580:              ($actionurl eq '/adm/imsimport')) { 
1.1022    www      10581:         my ($udom,$uname,$rest) = ($args->{'current_path'} =~ m{/priv/($match_domain)/($match_username)/?(.*)$});
1.1026    raeburn  10582:         $url = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname/";
1.987     raeburn  10583:         $toplevel = $url;
1.984     raeburn  10584:         if ($rest ne '') {
1.987     raeburn  10585:             $url .= $rest;
                   10586:         }
                   10587:     } elsif ($actionurl eq '/adm/coursedocs') {
                   10588:         if (ref($args) eq 'HASH') {
1.1071    raeburn  10589:             $url = $args->{'docs_url'};
                   10590:             $toplevel = $url;
1.1084    raeburn  10591:             if ($args->{'context'} eq 'paste') {
                   10592:                 ($cdom,$cnum) = ($url =~ m{^\Q/uploaded/\E($match_domain)/($match_courseid)/});
                   10593:                 ($path) = 
                   10594:                     ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
                   10595:                 $fileloc = &Apache::lonnet::filelocation('',$toplevel);
                   10596:                 $fileloc =~ s{^/}{};
                   10597:             }
1.1071    raeburn  10598:         }
1.1084    raeburn  10599:     } elsif ($actionurl eq '/adm/dependencies')  {
1.1071    raeburn  10600:         if ($env{'request.course.id'} ne '') {
                   10601:             if (ref($args) eq 'HASH') {
                   10602:                 $url = $args->{'docs_url'};
                   10603:                 $title = $args->{'docs_title'};
1.1126    raeburn  10604:                 $toplevel = $url; 
                   10605:                 unless ($toplevel =~ m{^/}) {
                   10606:                     $toplevel = "/$url";
                   10607:                 }
1.1085    raeburn  10608:                 ($rem) = ($toplevel =~ m{^(.+/)[^/]+$});
1.1126    raeburn  10609:                 if ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E)}) {
                   10610:                     $path = $1;
                   10611:                 } else {
                   10612:                     ($path) =
                   10613:                         ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
                   10614:                 }
1.1195    raeburn  10615:                 if ($toplevel=~/^\/*(uploaded|editupload)/) {
                   10616:                     $fileloc = $toplevel;
                   10617:                     $fileloc=~ s/^\s*(\S+)\s*$/$1/;
                   10618:                     my ($udom,$uname,$fname) =
                   10619:                         ($fileloc=~ m{^/+(?:uploaded|editupload)/+($match_domain)/+($match_name)/+(.*)$});
                   10620:                     $fileloc = propath($udom,$uname).'/userfiles/'.$fname;
                   10621:                 } else {
                   10622:                     $fileloc = &Apache::lonnet::filelocation('',$toplevel);
                   10623:                 }
1.1071    raeburn  10624:                 $fileloc =~ s{^/}{};
                   10625:                 ($filename) = ($fileloc =~ m{.+/([^/]+)$});
                   10626:                 $heading = &mt('Status of dependencies in [_1]',"$title ($filename)");
                   10627:             }
1.987     raeburn  10628:         }
1.1123    raeburn  10629:     } elsif ($actionurl eq "/public/$cdom/$cnum/syllabus") {
                   10630:         $udom = $cdom;
                   10631:         $uname = $cnum;
                   10632:         $url = "/uploaded/$cdom/$cnum/portfolio/syllabus";
                   10633:         $toplevel = $url;
                   10634:         $path = $url;
                   10635:         $fileloc = &Apache::lonnet::filelocation('',$toplevel).'/';
                   10636:         $fileloc =~ s{^/}{};
1.987     raeburn  10637:     }
1.1126    raeburn  10638:     foreach my $file (keys(%{$allfiles})) {
                   10639:         my $embed_file;
                   10640:         if (($path eq "/uploaded/$cdom/$cnum/portfolio/syllabus") && ($file =~ m{^\Q$path/\E(.+)$})) {
                   10641:             $embed_file = $1;
                   10642:         } else {
                   10643:             $embed_file = $file;
                   10644:         }
1.1158    raeburn  10645:         my ($absolutepath,$cleaned_file);
                   10646:         if ($embed_file =~ m{^\w+://}) {
                   10647:             $cleaned_file = $embed_file;
1.1147    raeburn  10648:             $newfiles{$cleaned_file} = 1;
                   10649:             $mapping{$cleaned_file} = $embed_file;
1.987     raeburn  10650:         } else {
1.1158    raeburn  10651:             $cleaned_file = &clean_path($embed_file);
1.987     raeburn  10652:             if ($embed_file =~ m{^/}) {
                   10653:                 $absolutepath = $embed_file;
                   10654:             }
1.1147    raeburn  10655:             if ($cleaned_file =~ m{/}) {
                   10656:                 my ($path,$fname) = ($cleaned_file =~ m{^(.+)/([^/]*)$});
1.987     raeburn  10657:                 $path = &check_for_traversal($path,$url,$toplevel);
                   10658:                 my $item = $fname;
                   10659:                 if ($path ne '') {
                   10660:                     $item = $path.'/'.$fname;
                   10661:                     $subdependencies{$path}{$fname} = 1;
                   10662:                 } else {
                   10663:                     $dependencies{$item} = 1;
                   10664:                 }
                   10665:                 if ($absolutepath) {
                   10666:                     $mapping{$item} = $absolutepath;
                   10667:                 } else {
                   10668:                     $mapping{$item} = $embed_file;
                   10669:                 }
                   10670:             } else {
                   10671:                 $dependencies{$embed_file} = 1;
                   10672:                 if ($absolutepath) {
1.1147    raeburn  10673:                     $mapping{$cleaned_file} = $absolutepath;
1.987     raeburn  10674:                 } else {
1.1147    raeburn  10675:                     $mapping{$cleaned_file} = $embed_file;
1.987     raeburn  10676:                 }
                   10677:             }
1.984     raeburn  10678:         }
                   10679:     }
1.1071    raeburn  10680:     my $dirptr = 16384;
1.984     raeburn  10681:     foreach my $path (keys(%subdependencies)) {
1.1071    raeburn  10682:         $currsubfile{$path} = {};
1.1123    raeburn  10683:         if (($actionurl eq '/adm/portfolio') || 
                   10684:             ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021    raeburn  10685:             my ($sublistref,$listerror) =
                   10686:                 &Apache::lonnet::dirlist($url.$path,$udom,$uname,$getpropath);
                   10687:             if (ref($sublistref) eq 'ARRAY') {
                   10688:                 foreach my $line (@{$sublistref}) {
                   10689:                     my ($file_name,$rest) = split(/\&/,$line,2);
1.1071    raeburn  10690:                     $currsubfile{$path}{$file_name} = 1;
1.1021    raeburn  10691:                 }
1.984     raeburn  10692:             }
1.987     raeburn  10693:         } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984     raeburn  10694:             if (opendir(my $dir,$url.'/'.$path)) {
                   10695:                 my @subdir_list = grep(!/^\./,readdir($dir));
1.1071    raeburn  10696:                 map {$currsubfile{$path}{$_} = 1;} @subdir_list;
                   10697:             }
1.1084    raeburn  10698:         } elsif (($actionurl eq '/adm/dependencies') ||
                   10699:                  (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
1.1123    raeburn  10700:                   ($args->{'context'} eq 'paste')) ||
                   10701:                  ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
1.1071    raeburn  10702:             if ($env{'request.course.id'} ne '') {
1.1123    raeburn  10703:                 my $dir;
                   10704:                 if ($actionurl eq "/public/$cdom/$cnum/syllabus") {
                   10705:                     $dir = $fileloc;
                   10706:                 } else {
                   10707:                     ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
                   10708:                 }
1.1071    raeburn  10709:                 if ($dir ne '') {
                   10710:                     my ($sublistref,$listerror) =
                   10711:                         &Apache::lonnet::dirlist($dir.$path,$cdom,$cnum,$getpropath,undef,'/');
                   10712:                     if (ref($sublistref) eq 'ARRAY') {
                   10713:                         foreach my $line (@{$sublistref}) {
                   10714:                             my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,$size,
                   10715:                                 undef,$mtime)=split(/\&/,$line,12);
                   10716:                             unless (($testdir&$dirptr) ||
                   10717:                                     ($file_name =~ /^\.\.?$/)) {
                   10718:                                 $currsubfile{$path}{$file_name} = [$size,$mtime];
                   10719:                             }
                   10720:                         }
                   10721:                     }
                   10722:                 }
1.984     raeburn  10723:             }
                   10724:         }
                   10725:         foreach my $file (keys(%{$subdependencies{$path}})) {
1.1071    raeburn  10726:             if (exists($currsubfile{$path}{$file})) {
1.987     raeburn  10727:                 my $item = $path.'/'.$file;
                   10728:                 unless ($mapping{$item} eq $item) {
                   10729:                     $pathchanges{$item} = 1;
                   10730:                 }
                   10731:                 $existing{$item} = 1;
                   10732:                 $numexisting ++;
                   10733:             } else {
                   10734:                 $newfiles{$path.'/'.$file} = 1;
1.984     raeburn  10735:             }
                   10736:         }
1.1071    raeburn  10737:         if ($actionurl eq '/adm/dependencies') {
                   10738:             foreach my $path (keys(%currsubfile)) {
                   10739:                 if (ref($currsubfile{$path}) eq 'HASH') {
                   10740:                     foreach my $file (keys(%{$currsubfile{$path}})) {
                   10741:                          unless ($subdependencies{$path}{$file}) {
1.1085    raeburn  10742:                              next if (($rem ne '') &&
                   10743:                                       (($env{"httpref.$rem"."$path/$file"} ne '') ||
                   10744:                                        (ref($navmap) &&
                   10745:                                        (($navmap->getResourceByUrl($rem."$path/$file") ne '') ||
                   10746:                                         (($file =~ /^(.*\.s?html?)\.bak$/i) &&
                   10747:                                          ($navmap->getResourceByUrl($rem."$path/$1")))))));
1.1071    raeburn  10748:                              $unused{$path.'/'.$file} = 1; 
                   10749:                          }
                   10750:                     }
                   10751:                 }
                   10752:             }
                   10753:         }
1.984     raeburn  10754:     }
1.987     raeburn  10755:     my %currfile;
1.1123    raeburn  10756:     if (($actionurl eq '/adm/portfolio') ||
                   10757:         ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021    raeburn  10758:         my ($dirlistref,$listerror) =
                   10759:             &Apache::lonnet::dirlist($url,$udom,$uname,$getpropath);
                   10760:         if (ref($dirlistref) eq 'ARRAY') {
                   10761:             foreach my $line (@{$dirlistref}) {
                   10762:                 my ($file_name,$rest) = split(/\&/,$line,2);
                   10763:                 $currfile{$file_name} = 1;
                   10764:             }
1.984     raeburn  10765:         }
1.987     raeburn  10766:     } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984     raeburn  10767:         if (opendir(my $dir,$url)) {
1.987     raeburn  10768:             my @dir_list = grep(!/^\./,readdir($dir));
1.984     raeburn  10769:             map {$currfile{$_} = 1;} @dir_list;
                   10770:         }
1.1084    raeburn  10771:     } elsif (($actionurl eq '/adm/dependencies') ||
                   10772:              (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
1.1123    raeburn  10773:               ($args->{'context'} eq 'paste')) ||
                   10774:              ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
1.1071    raeburn  10775:         if ($env{'request.course.id'} ne '') {
                   10776:             my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
                   10777:             if ($dir ne '') {
                   10778:                 my ($dirlistref,$listerror) =
                   10779:                     &Apache::lonnet::dirlist($dir,$cdom,$cnum,$getpropath,undef,'/');
                   10780:                 if (ref($dirlistref) eq 'ARRAY') {
                   10781:                     foreach my $line (@{$dirlistref}) {
                   10782:                         my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,
                   10783:                             $size,undef,$mtime)=split(/\&/,$line,12);
                   10784:                         unless (($testdir&$dirptr) ||
                   10785:                                 ($file_name =~ /^\.\.?$/)) {
                   10786:                             $currfile{$file_name} = [$size,$mtime];
                   10787:                         }
                   10788:                     }
                   10789:                 }
                   10790:             }
                   10791:         }
1.984     raeburn  10792:     }
                   10793:     foreach my $file (keys(%dependencies)) {
1.1071    raeburn  10794:         if (exists($currfile{$file})) {
1.987     raeburn  10795:             unless ($mapping{$file} eq $file) {
                   10796:                 $pathchanges{$file} = 1;
                   10797:             }
                   10798:             $existing{$file} = 1;
                   10799:             $numexisting ++;
                   10800:         } else {
1.984     raeburn  10801:             $newfiles{$file} = 1;
                   10802:         }
                   10803:     }
1.1071    raeburn  10804:     foreach my $file (keys(%currfile)) {
                   10805:         unless (($file eq $filename) ||
                   10806:                 ($file eq $filename.'.bak') ||
                   10807:                 ($dependencies{$file})) {
1.1085    raeburn  10808:             if ($actionurl eq '/adm/dependencies') {
1.1126    raeburn  10809:                 unless ($toplevel =~ m{^\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E}) {
                   10810:                     next if (($rem ne '') &&
                   10811:                              (($env{"httpref.$rem".$file} ne '') ||
                   10812:                               (ref($navmap) &&
                   10813:                               (($navmap->getResourceByUrl($rem.$file) ne '') ||
                   10814:                                (($file =~ /^(.*\.s?html?)\.bak$/i) &&
                   10815:                                 ($navmap->getResourceByUrl($rem.$1)))))));
                   10816:                 }
1.1085    raeburn  10817:             }
1.1071    raeburn  10818:             $unused{$file} = 1;
                   10819:         }
                   10820:     }
1.1084    raeburn  10821:     if (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
                   10822:         ($args->{'context'} eq 'paste')) {
                   10823:         $counter = scalar(keys(%existing));
                   10824:         $numpathchg = scalar(keys(%pathchanges));
1.1123    raeburn  10825:         return ($output,$counter,$numpathchg,\%existing);
                   10826:     } elsif (($actionurl eq "/public/$cdom/$cnum/syllabus") && 
                   10827:              (ref($args) eq 'HASH') && ($args->{'context'} eq 'rewrites')) {
                   10828:         $counter = scalar(keys(%existing));
                   10829:         $numpathchg = scalar(keys(%pathchanges));
                   10830:         return ($output,$counter,$numpathchg,\%existing,\%mapping);
1.1084    raeburn  10831:     }
1.984     raeburn  10832:     foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%newfiles)) {
1.1071    raeburn  10833:         if ($actionurl eq '/adm/dependencies') {
                   10834:             next if ($embed_file =~ m{^\w+://});
                   10835:         }
1.660     raeburn  10836:         $upload_output .= &start_data_table_row().
1.1123    raeburn  10837:                           '<td valign="top"><img src="'.&icon($embed_file).'" />&nbsp;'.
1.1071    raeburn  10838:                           '<span class="LC_filename">'.$embed_file.'</span>';
1.987     raeburn  10839:         unless ($mapping{$embed_file} eq $embed_file) {
1.1123    raeburn  10840:             $upload_output .= '<br /><span class="LC_info" style="font-size:smaller;">'.
                   10841:                               &mt('changed from: [_1]',$mapping{$embed_file}).'</span>';
1.987     raeburn  10842:         }
1.1123    raeburn  10843:         $upload_output .= '</td>';
1.1071    raeburn  10844:         if ($args->{'ignore_remote_references'} && $embed_file =~ m{^\w+://}) { 
1.1123    raeburn  10845:             $upload_output.='<td align="right">'.
                   10846:                             '<span class="LC_info LC_fontsize_medium">'.
                   10847:                             &mt("URL points to web address").'</span>';
1.987     raeburn  10848:             $numremref++;
1.660     raeburn  10849:         } elsif ($args->{'error_on_invalid_names'}
                   10850:             && $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) {
1.1123    raeburn  10851:             $upload_output.='<td align="right"><span class="LC_warning">'.
                   10852:                             &mt('Invalid characters').'</span>';
1.987     raeburn  10853:             $numinvalid++;
1.660     raeburn  10854:         } else {
1.1123    raeburn  10855:             $upload_output .= '<td>'.
                   10856:                               &embedded_file_element('upload_embedded',$counter,
1.987     raeburn  10857:                                                      $embed_file,\%mapping,
1.1071    raeburn  10858:                                                      $allfiles,$codebase,'upload');
                   10859:             $counter ++;
                   10860:             $numnew ++;
1.987     raeburn  10861:         }
                   10862:         $upload_output .= '</td>'.&Apache::loncommon::end_data_table_row()."\n";
                   10863:     }
                   10864:     foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%existing)) {
1.1071    raeburn  10865:         if ($actionurl eq '/adm/dependencies') {
                   10866:             my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$embed_file);
                   10867:             $modify_output .= &start_data_table_row().
                   10868:                               '<td><a href="'.$path.'/'.$embed_file.'" style="text-decoration:none;">'.
                   10869:                               '<img src="'.&icon($embed_file).'" border="0" />'.
                   10870:                               '&nbsp;<span class="LC_filename">'.$embed_file.'</span></a></td>'.
                   10871:                               '<td>'.$size.'</td>'.
                   10872:                               '<td>'.$mtime.'</td>'.
                   10873:                               '<td><label><input type="checkbox" name="mod_upload_dep" '.
                   10874:                               'onclick="toggleBrowse('."'$counter'".')" id="mod_upload_dep_'.
                   10875:                               $counter.'" value="'.$counter.'" />'.&mt('Yes').'</label>'.
                   10876:                               '<div id="moduploaddep_'.$counter.'" style="display:none;">'.
                   10877:                               &embedded_file_element('upload_embedded',$counter,
                   10878:                                                      $embed_file,\%mapping,
                   10879:                                                      $allfiles,$codebase,'modify').
                   10880:                               '</div></td>'.
                   10881:                               &end_data_table_row()."\n";
                   10882:             $counter ++;
                   10883:         } else {
                   10884:             $upload_output .= &start_data_table_row().
1.1123    raeburn  10885:                               '<td valign="top"><img src="'.&icon($embed_file).'" />&nbsp;'.
                   10886:                               '<span class="LC_filename">'.$embed_file.'</span></td>'.
                   10887:                               '<td align="right"><span class="LC_info LC_fontsize_medium">'.&mt('Already exists').'</span></td>'.
1.1071    raeburn  10888:                               &Apache::loncommon::end_data_table_row()."\n";
                   10889:         }
                   10890:     }
                   10891:     my $delidx = $counter;
                   10892:     foreach my $oldfile (sort {lc($a) cmp lc($b)} keys(%unused)) {
                   10893:         my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$oldfile);
                   10894:         $delete_output .= &start_data_table_row().
                   10895:                           '<td><img src="'.&icon($oldfile).'" />'.
                   10896:                           '&nbsp;<span class="LC_filename">'.$oldfile.'</span></td>'.
                   10897:                           '<td>'.$size.'</td>'.
                   10898:                           '<td>'.$mtime.'</td>'.
                   10899:                           '<td><label><input type="checkbox" name="del_upload_dep" '.
                   10900:                           ' value="'.$delidx.'" />'.&mt('Yes').'</label>'.
                   10901:                           &embedded_file_element('upload_embedded',$delidx,
                   10902:                                                  $oldfile,\%mapping,$allfiles,
                   10903:                                                  $codebase,'delete').'</td>'.
                   10904:                           &end_data_table_row()."\n"; 
                   10905:         $numunused ++;
                   10906:         $delidx ++;
1.987     raeburn  10907:     }
                   10908:     if ($upload_output) {
                   10909:         $upload_output = &start_data_table().
                   10910:                          $upload_output.
                   10911:                          &end_data_table()."\n";
                   10912:     }
1.1071    raeburn  10913:     if ($modify_output) {
                   10914:         $modify_output = &start_data_table().
                   10915:                          &start_data_table_header_row().
                   10916:                          '<th>'.&mt('File').'</th>'.
                   10917:                          '<th>'.&mt('Size (KB)').'</th>'.
                   10918:                          '<th>'.&mt('Modified').'</th>'.
                   10919:                          '<th>'.&mt('Upload replacement?').'</th>'.
                   10920:                          &end_data_table_header_row().
                   10921:                          $modify_output.
                   10922:                          &end_data_table()."\n";
                   10923:     }
                   10924:     if ($delete_output) {
                   10925:         $delete_output = &start_data_table().
                   10926:                          &start_data_table_header_row().
                   10927:                          '<th>'.&mt('File').'</th>'.
                   10928:                          '<th>'.&mt('Size (KB)').'</th>'.
                   10929:                          '<th>'.&mt('Modified').'</th>'.
                   10930:                          '<th>'.&mt('Delete?').'</th>'.
                   10931:                          &end_data_table_header_row().
                   10932:                          $delete_output.
                   10933:                          &end_data_table()."\n";
                   10934:     }
1.987     raeburn  10935:     my $applies = 0;
                   10936:     if ($numremref) {
                   10937:         $applies ++;
                   10938:     }
                   10939:     if ($numinvalid) {
                   10940:         $applies ++;
                   10941:     }
                   10942:     if ($numexisting) {
                   10943:         $applies ++;
                   10944:     }
1.1071    raeburn  10945:     if ($counter || $numunused) {
1.987     raeburn  10946:         $output = '<form name="upload_embedded" action="'.$actionurl.'"'.
                   10947:                   ' method="post" enctype="multipart/form-data">'."\n".
1.1071    raeburn  10948:                   $state.'<h3>'.$heading.'</h3>'; 
                   10949:         if ($actionurl eq '/adm/dependencies') {
                   10950:             if ($numnew) {
                   10951:                 $output .= '<h4>'.&mt('Missing dependencies').'</h4>'.
                   10952:                            '<p>'.&mt('The following files need to be uploaded.').'</p>'."\n".
                   10953:                            $upload_output.'<br />'."\n";
                   10954:             }
                   10955:             if ($numexisting) {
                   10956:                 $output .= '<h4>'.&mt('Uploaded dependencies (in use)').'</h4>'.
                   10957:                            '<p>'.&mt('Upload a new file to replace the one currently in use.').'</p>'."\n".
                   10958:                            $modify_output.'<br />'."\n";
                   10959:                            $buttontext = &mt('Save changes');
                   10960:             }
                   10961:             if ($numunused) {
                   10962:                 $output .= '<h4>'.&mt('Unused files').'</h4>'.
                   10963:                            '<p>'.&mt('The following uploaded files are no longer used.').'</p>'."\n".
                   10964:                            $delete_output.'<br />'."\n";
                   10965:                            $buttontext = &mt('Save changes');
                   10966:             }
                   10967:         } else {
                   10968:             $output .= $upload_output.'<br />'."\n";
                   10969:         }
                   10970:         $output .= '<input type ="hidden" name="number_embedded_items" value="'.
                   10971:                    $counter.'" />'."\n";
                   10972:         if ($actionurl eq '/adm/dependencies') { 
                   10973:             $output .= '<input type ="hidden" name="number_newemb_items" value="'.
                   10974:                        $numnew.'" />'."\n";
                   10975:         } elsif ($actionurl eq '') {
1.987     raeburn  10976:             $output .=  '<input type="hidden" name="phase" value="three" />';
                   10977:         }
                   10978:     } elsif ($applies) {
                   10979:         $output = '<b>'.&mt('Referenced files').'</b>:<br />';
                   10980:         if ($applies > 1) {
                   10981:             $output .=  
1.1123    raeburn  10982:                 &mt('No dependencies need to be uploaded, as one of the following applies to each reference:').'<ul>';
1.987     raeburn  10983:             if ($numremref) {
                   10984:                 $output .= '<li>'.&mt('reference is to a URL which points to another server').'</li>'."\n";
                   10985:             }
                   10986:             if ($numinvalid) {
                   10987:                 $output .= '<li>'.&mt('reference is to file with a name containing invalid characters').'</li>'."\n";
                   10988:             }
                   10989:             if ($numexisting) {
                   10990:                 $output .= '<li>'.&mt('reference is to an existing file at the specified location').'</li>'."\n";
                   10991:             }
                   10992:             $output .= '</ul><br />';
                   10993:         } elsif ($numremref) {
                   10994:             $output .= '<p>'.&mt('None to upload, as all references are to URLs pointing to another server.').'</p>';
                   10995:         } elsif ($numinvalid) {
                   10996:             $output .= '<p>'.&mt('None to upload, as all references are to files with names containing invalid characters.').'</p>';
                   10997:         } elsif ($numexisting) {
                   10998:             $output .= '<p>'.&mt('None to upload, as all references are to existing files.').'</p>';
                   10999:         }
                   11000:         $output .= $upload_output.'<br />';
                   11001:     }
                   11002:     my ($pathchange_output,$chgcount);
1.1071    raeburn  11003:     $chgcount = $counter;
1.987     raeburn  11004:     if (keys(%pathchanges) > 0) {
                   11005:         foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%pathchanges)) {
1.1071    raeburn  11006:             if ($counter) {
1.987     raeburn  11007:                 $output .= &embedded_file_element('pathchange',$chgcount,
                   11008:                                                   $embed_file,\%mapping,
1.1071    raeburn  11009:                                                   $allfiles,$codebase,'change');
1.987     raeburn  11010:             } else {
                   11011:                 $pathchange_output .= 
                   11012:                     &start_data_table_row().
                   11013:                     '<td><input type ="checkbox" name="namechange" value="'.
                   11014:                     $chgcount.'" checked="checked" /></td>'.
                   11015:                     '<td>'.$mapping{$embed_file}.'</td>'.
                   11016:                     '<td>'.$embed_file.
                   11017:                     &embedded_file_element('pathchange',$numpathchg,$embed_file,
1.1071    raeburn  11018:                                            \%mapping,$allfiles,$codebase,'change').
1.987     raeburn  11019:                     '</td>'.&end_data_table_row();
1.660     raeburn  11020:             }
1.987     raeburn  11021:             $numpathchg ++;
                   11022:             $chgcount ++;
1.660     raeburn  11023:         }
                   11024:     }
1.1127    raeburn  11025:     if (($counter) || ($numunused)) {
1.987     raeburn  11026:         if ($numpathchg) {
                   11027:             $output .= '<input type ="hidden" name="number_pathchange_items" value="'.
                   11028:                        $numpathchg.'" />'."\n";
                   11029:         }
                   11030:         if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') || 
                   11031:             ($actionurl eq '/adm/imsimport')) {
                   11032:             $output .= '<input type="hidden" name="phase" value="three" />'."\n";
                   11033:         } elsif ($actionurl eq '/adm/portfolio' || $actionurl eq '/adm/coursegrp_portfolio') {
                   11034:             $output .= '<input type="hidden" name="action" value="upload_embedded" />';
1.1071    raeburn  11035:         } elsif ($actionurl eq '/adm/dependencies') {
                   11036:             $output .= '<input type="hidden" name="action" value="process_changes" />';
1.987     raeburn  11037:         }
1.1123    raeburn  11038:         $output .= '<input type ="submit" value="'.$buttontext.'" />'."\n".'</form>'."\n";
1.987     raeburn  11039:     } elsif ($numpathchg) {
                   11040:         my %pathchange = ();
                   11041:         $output .= &modify_html_form('pathchange',$actionurl,$state,\%pathchange,$pathchange_output);
                   11042:         if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
                   11043:             $output .= '<p>'.&mt('or').'</p>'; 
1.1123    raeburn  11044:         }
1.987     raeburn  11045:     }
1.1071    raeburn  11046:     return ($output,$counter,$numpathchg);
1.987     raeburn  11047: }
                   11048: 
1.1147    raeburn  11049: =pod
                   11050: 
                   11051: =item * clean_path($name)
                   11052: 
                   11053: Performs clean-up of directories, subdirectories and filename in an
                   11054: embedded object, referenced in an HTML file which is being uploaded
                   11055: to a course or portfolio, where 
                   11056: "Upload embedded images/multimedia files if HTML file" checkbox was
                   11057: checked.
                   11058: 
                   11059: Clean-up is similar to replacements in lonnet::clean_filename()
                   11060: except each / between sub-directory and next level is preserved.
                   11061: 
                   11062: =cut
                   11063: 
                   11064: sub clean_path {
                   11065:     my ($embed_file) = @_;
                   11066:     $embed_file =~s{^/+}{};
                   11067:     my @contents;
                   11068:     if ($embed_file =~ m{/}) {
                   11069:         @contents = split(/\//,$embed_file);
                   11070:     } else {
                   11071:         @contents = ($embed_file);
                   11072:     }
                   11073:     my $lastidx = scalar(@contents)-1;
                   11074:     for (my $i=0; $i<=$lastidx; $i++) { 
                   11075:         $contents[$i]=~s{\\}{/}g;
                   11076:         $contents[$i]=~s/\s+/\_/g;
                   11077:         $contents[$i]=~s{[^/\w\.\-]}{}g;
                   11078:         if ($i == $lastidx) {
                   11079:             $contents[$i]=~s/\.(\d+)(?=\.)/_$1/g;
                   11080:         }
                   11081:     }
                   11082:     if ($lastidx > 0) {
                   11083:         return join('/',@contents);
                   11084:     } else {
                   11085:         return $contents[0];
                   11086:     }
                   11087: }
                   11088: 
1.987     raeburn  11089: sub embedded_file_element {
1.1071    raeburn  11090:     my ($context,$num,$embed_file,$mapping,$allfiles,$codebase,$type) = @_;
1.987     raeburn  11091:     return unless ((ref($mapping) eq 'HASH') && (ref($allfiles) eq 'HASH') &&
                   11092:                    (ref($codebase) eq 'HASH'));
                   11093:     my $output;
1.1071    raeburn  11094:     if (($context eq 'upload_embedded') && ($type ne 'delete')) {
1.987     raeburn  11095:        $output = '<input name="embedded_item_'.$num.'" type="file" value="" />'."\n";
                   11096:     }
                   11097:     $output .= '<input name="embedded_orig_'.$num.'" type="hidden" value="'.
                   11098:                &escape($embed_file).'" />';
                   11099:     unless (($context eq 'upload_embedded') && 
                   11100:             ($mapping->{$embed_file} eq $embed_file)) {
                   11101:         $output .='
                   11102:         <input name="embedded_ref_'.$num.'" type="hidden" value="'.&escape($mapping->{$embed_file}).'" />';
                   11103:     }
                   11104:     my $attrib;
                   11105:     if (ref($allfiles->{$mapping->{$embed_file}}) eq 'ARRAY') {
                   11106:         $attrib = &escape(join(':',@{$allfiles->{$mapping->{$embed_file}}}));
                   11107:     }
                   11108:     $output .=
                   11109:         "\n\t\t".
                   11110:         '<input name="embedded_attrib_'.$num.'" type="hidden" value="'.
                   11111:         $attrib.'" />';
                   11112:     if (exists($codebase->{$mapping->{$embed_file}})) {
                   11113:         $output .=
                   11114:             "\n\t\t".
                   11115:             '<input name="codebase_'.$num.'" type="hidden" value="'.
                   11116:             &escape($codebase->{$mapping->{$embed_file}}).'" />';
1.984     raeburn  11117:     }
1.987     raeburn  11118:     return $output;
1.660     raeburn  11119: }
                   11120: 
1.1071    raeburn  11121: sub get_dependency_details {
                   11122:     my ($currfile,$currsubfile,$embed_file) = @_;
                   11123:     my ($size,$mtime,$showsize,$showmtime);
                   11124:     if ((ref($currfile) eq 'HASH') && (ref($currsubfile))) {
                   11125:         if ($embed_file =~ m{/}) {
                   11126:             my ($path,$fname) = split(/\//,$embed_file);
                   11127:             if (ref($currsubfile->{$path}{$fname}) eq 'ARRAY') {
                   11128:                 ($size,$mtime) = @{$currsubfile->{$path}{$fname}};
                   11129:             }
                   11130:         } else {
                   11131:             if (ref($currfile->{$embed_file}) eq 'ARRAY') {
                   11132:                 ($size,$mtime) = @{$currfile->{$embed_file}};
                   11133:             }
                   11134:         }
                   11135:         $showsize = $size/1024.0;
                   11136:         $showsize = sprintf("%.1f",$showsize);
                   11137:         if ($mtime > 0) {
                   11138:             $showmtime = &Apache::lonlocal::locallocaltime($mtime);
                   11139:         }
                   11140:     }
                   11141:     return ($showsize,$showmtime);
                   11142: }
                   11143: 
                   11144: sub ask_embedded_js {
                   11145:     return <<"END";
                   11146: <script type="text/javascript"">
                   11147: // <![CDATA[
                   11148: function toggleBrowse(counter) {
                   11149:     var chkboxid = document.getElementById('mod_upload_dep_'+counter);
                   11150:     var fileid = document.getElementById('embedded_item_'+counter);
                   11151:     var uploaddivid = document.getElementById('moduploaddep_'+counter);
                   11152:     if (chkboxid.checked == true) {
                   11153:         uploaddivid.style.display='block';
                   11154:     } else {
                   11155:         uploaddivid.style.display='none';
                   11156:         fileid.value = '';
                   11157:     }
                   11158: }
                   11159: // ]]>
                   11160: </script>
                   11161: 
                   11162: END
                   11163: }
                   11164: 
1.661     raeburn  11165: sub upload_embedded {
                   11166:     my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota,
1.987     raeburn  11167:         $current_disk_usage,$hiddenstate,$actionurl) = @_;
                   11168:     my (%pathchange,$output,$modifyform,$footer,$returnflag);
1.661     raeburn  11169:     for (my $i=0; $i<$env{'form.number_embedded_items'}; $i++) {
                   11170:         next if (!exists($env{'form.embedded_item_'.$i.'.filename'}));
                   11171:         my $orig_uploaded_filename =
                   11172:             $env{'form.embedded_item_'.$i.'.filename'};
1.987     raeburn  11173:         foreach my $type ('orig','ref','attrib','codebase') {
                   11174:             if ($env{'form.embedded_'.$type.'_'.$i} ne '') {
                   11175:                 $env{'form.embedded_'.$type.'_'.$i} =
                   11176:                     &unescape($env{'form.embedded_'.$type.'_'.$i});
                   11177:             }
                   11178:         }
1.661     raeburn  11179:         my ($path,$fname) =
                   11180:             ($env{'form.embedded_orig_'.$i} =~ m{(.*/)([^/]*)});
                   11181:         # no path, whole string is fname
                   11182:         if (!$fname) { $fname = $env{'form.embedded_orig_'.$i} };
                   11183:         $fname = &Apache::lonnet::clean_filename($fname);
                   11184:         # See if there is anything left
                   11185:         next if ($fname eq '');
                   11186: 
                   11187:         # Check if file already exists as a file or directory.
                   11188:         my ($state,$msg);
                   11189:         if ($context eq 'portfolio') {
                   11190:             my $port_path = $dirpath;
                   11191:             if ($group ne '') {
                   11192:                 $port_path = "groups/$group/$port_path";
                   11193:             }
1.987     raeburn  11194:             ($state,$msg) = &check_for_upload($env{'form.currentpath'}.$path,
                   11195:                                               $fname,$group,'embedded_item_'.$i,
1.661     raeburn  11196:                                               $dir_root,$port_path,$disk_quota,
                   11197:                                               $current_disk_usage,$uname,$udom);
                   11198:             if ($state eq 'will_exceed_quota'
1.984     raeburn  11199:                 || $state eq 'file_locked') {
1.661     raeburn  11200:                 $output .= $msg;
                   11201:                 next;
                   11202:             }
                   11203:         } elsif (($context eq 'author') || ($context eq 'testbank')) {
                   11204:             ($state,$msg) = &check_for_existing($path,$fname,'embedded_item_'.$i);
                   11205:             if ($state eq 'exists') {
                   11206:                 $output .= $msg;
                   11207:                 next;
                   11208:             }
                   11209:         }
                   11210:         # Check if extension is valid
                   11211:         if (($fname =~ /\.(\w+)$/) &&
                   11212:             (&Apache::loncommon::fileembstyle($1) eq 'hdn')) {
1.1155    bisitz   11213:             $output .= &mt('Invalid file extension ([_1]) - reserved for internal use.',$1)
                   11214:                       .' '.&mt('Rename the file with a different extension and re-upload.').'<br />';
1.661     raeburn  11215:             next;
                   11216:         } elsif (($fname =~ /\.(\w+)$/) &&
                   11217:                  (!defined(&Apache::loncommon::fileembstyle($1)))) {
1.987     raeburn  11218:             $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1).'<br />';
1.661     raeburn  11219:             next;
                   11220:         } elsif ($fname=~/\.(\d+)\.(\w+)$/) {
1.1120    bisitz   11221:             $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  11222:             next;
                   11223:         }
                   11224:         $env{'form.embedded_item_'.$i.'.filename'}=$fname;
1.1123    raeburn  11225:         my $subdir = $path;
                   11226:         $subdir =~ s{/+$}{};
1.661     raeburn  11227:         if ($context eq 'portfolio') {
1.984     raeburn  11228:             my $result;
                   11229:             if ($state eq 'existingfile') {
                   11230:                 $result=
                   11231:                     &Apache::lonnet::userfileupload('embedded_item_'.$i,'existingfile',
1.1123    raeburn  11232:                                                     $dirpath.$env{'form.currentpath'}.$subdir);
1.661     raeburn  11233:             } else {
1.984     raeburn  11234:                 $result=
                   11235:                     &Apache::lonnet::userfileupload('embedded_item_'.$i,'',
1.987     raeburn  11236:                                                     $dirpath.
1.1123    raeburn  11237:                                                     $env{'form.currentpath'}.$subdir);
1.984     raeburn  11238:                 if ($result !~ m|^/uploaded/|) {
                   11239:                     $output .= '<span class="LC_error">'
                   11240:                                .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
                   11241:                                ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
                   11242:                                .'</span><br />';
                   11243:                     next;
                   11244:                 } else {
1.987     raeburn  11245:                     $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
                   11246:                                $path.$fname.'</span>').'<br />';     
1.984     raeburn  11247:                 }
1.661     raeburn  11248:             }
1.1123    raeburn  11249:         } elsif (($context eq 'coursedoc') || ($context eq 'syllabus')) {
1.1126    raeburn  11250:             my $extendedsubdir = $dirpath.'/'.$subdir;
                   11251:             $extendedsubdir =~ s{/+$}{};
1.987     raeburn  11252:             my $result =
1.1126    raeburn  11253:                 &Apache::lonnet::userfileupload('embedded_item_'.$i,$context,$extendedsubdir);
1.987     raeburn  11254:             if ($result !~ m|^/uploaded/|) {
                   11255:                 $output .= '<span class="LC_error">'
                   11256:                            .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
                   11257:                            ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
                   11258:                            .'</span><br />';
                   11259:                     next;
                   11260:             } else {
                   11261:                 $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
                   11262:                            $path.$fname.'</span>').'<br />';
1.1125    raeburn  11263:                 if ($context eq 'syllabus') {
                   11264:                     &Apache::lonnet::make_public_indefinitely($result);
                   11265:                 }
1.987     raeburn  11266:             }
1.661     raeburn  11267:         } else {
                   11268: # Save the file
                   11269:             my $target = $env{'form.embedded_item_'.$i};
                   11270:             my $fullpath = $dir_root.$dirpath.'/'.$path;
                   11271:             my $dest = $fullpath.$fname;
                   11272:             my $url = $url_root.$dirpath.'/'.$path.$fname;
1.1027    raeburn  11273:             my @parts=split(/\//,"$dirpath/$path");
1.661     raeburn  11274:             my $count;
                   11275:             my $filepath = $dir_root;
1.1027    raeburn  11276:             foreach my $subdir (@parts) {
                   11277:                 $filepath .= "/$subdir";
                   11278:                 if (!-e $filepath) {
1.661     raeburn  11279:                     mkdir($filepath,0770);
                   11280:                 }
                   11281:             }
                   11282:             my $fh;
                   11283:             if (!open($fh,'>'.$dest)) {
                   11284:                 &Apache::lonnet::logthis('Failed to create '.$dest);
                   11285:                 $output .= '<span class="LC_error">'.
1.1071    raeburn  11286:                            &mt('An error occurred while trying to upload [_1] for embedded element [_2].',
                   11287:                                $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661     raeburn  11288:                            '</span><br />';
                   11289:             } else {
                   11290:                 if (!print $fh $env{'form.embedded_item_'.$i}) {
                   11291:                     &Apache::lonnet::logthis('Failed to write to '.$dest);
                   11292:                     $output .= '<span class="LC_error">'.
1.1071    raeburn  11293:                               &mt('An error occurred while writing the file [_1] for embedded element [_2].',
                   11294:                                   $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661     raeburn  11295:                               '</span><br />';
                   11296:                 } else {
1.987     raeburn  11297:                     $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
                   11298:                                $url.'</span>').'<br />';
                   11299:                     unless ($context eq 'testbank') {
                   11300:                         $footer .= &mt('View embedded file: [_1]',
                   11301:                                        '<a href="'.$url.'">'.$fname.'</a>').'<br />';
                   11302:                     }
                   11303:                 }
                   11304:                 close($fh);
                   11305:             }
                   11306:         }
                   11307:         if ($env{'form.embedded_ref_'.$i}) {
                   11308:             $pathchange{$i} = 1;
                   11309:         }
                   11310:     }
                   11311:     if ($output) {
                   11312:         $output = '<p>'.$output.'</p>';
                   11313:     }
                   11314:     $output .= &modify_html_form('upload_embedded',$actionurl,$hiddenstate,\%pathchange);
                   11315:     $returnflag = 'ok';
1.1071    raeburn  11316:     my $numpathchgs = scalar(keys(%pathchange));
                   11317:     if ($numpathchgs > 0) {
1.987     raeburn  11318:         if ($context eq 'portfolio') {
                   11319:             $output .= '<p>'.&mt('or').'</p>';
                   11320:         } elsif ($context eq 'testbank') {
1.1071    raeburn  11321:             $output .=  '<p>'.&mt('Or [_1]continue[_2] the testbank import without modifying the reference(s).',
                   11322:                                   '<a href="javascript:document.testbankForm.submit();">','</a>').'</p>';
1.987     raeburn  11323:             $returnflag = 'modify_orightml';
                   11324:         }
                   11325:     }
1.1071    raeburn  11326:     return ($output.$footer,$returnflag,$numpathchgs);
1.987     raeburn  11327: }
                   11328: 
                   11329: sub modify_html_form {
                   11330:     my ($context,$actionurl,$hiddenstate,$pathchange,$pathchgtable) = @_;
                   11331:     my $end = 0;
                   11332:     my $modifyform;
                   11333:     if ($context eq 'upload_embedded') {
                   11334:         return unless (ref($pathchange) eq 'HASH');
                   11335:         if ($env{'form.number_embedded_items'}) {
                   11336:             $end += $env{'form.number_embedded_items'};
                   11337:         }
                   11338:         if ($env{'form.number_pathchange_items'}) {
                   11339:             $end += $env{'form.number_pathchange_items'};
                   11340:         }
                   11341:         if ($end) {
                   11342:             for (my $i=0; $i<$end; $i++) {
                   11343:                 if ($i < $env{'form.number_embedded_items'}) {
                   11344:                     next unless($pathchange->{$i});
                   11345:                 }
                   11346:                 $modifyform .=
                   11347:                     &start_data_table_row().
                   11348:                     '<td><input type ="checkbox" name="namechange" value="'.$i.'" '.
                   11349:                     'checked="checked" /></td>'.
                   11350:                     '<td>'.$env{'form.embedded_ref_'.$i}.
                   11351:                     '<input type="hidden" name="embedded_ref_'.$i.'" value="'.
                   11352:                     &escape($env{'form.embedded_ref_'.$i}).'" />'.
                   11353:                     '<input type="hidden" name="embedded_codebase_'.$i.'" value="'.
                   11354:                     &escape($env{'form.embedded_codebase_'.$i}).'" />'.
                   11355:                     '<input type="hidden" name="embedded_attrib_'.$i.'" value="'.
                   11356:                     &escape($env{'form.embedded_attrib_'.$i}).'" /></td>'.
                   11357:                     '<td>'.$env{'form.embedded_orig_'.$i}.
                   11358:                     '<input type="hidden" name="embedded_orig_'.$i.'" value="'.
                   11359:                     &escape($env{'form.embedded_orig_'.$i}).'" /></td>'.
                   11360:                     &end_data_table_row();
1.1071    raeburn  11361:             }
1.987     raeburn  11362:         }
                   11363:     } else {
                   11364:         $modifyform = $pathchgtable;
                   11365:         if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
                   11366:             $hiddenstate .= '<input type="hidden" name="phase" value="four" />';
                   11367:         } elsif (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
                   11368:             $hiddenstate .= '<input type="hidden" name="action" value="modify_orightml" />';
                   11369:         }
                   11370:     }
                   11371:     if ($modifyform) {
1.1071    raeburn  11372:         if ($actionurl eq '/adm/dependencies') {
                   11373:             $hiddenstate .= '<input type="hidden" name="action" value="modifyhrefs" />';
                   11374:         }
1.987     raeburn  11375:         return '<h3>'.&mt('Changes in content of HTML file required').'</h3>'."\n".
                   11376:                '<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".
                   11377:                '<li>'.&mt('For consistency between the reference(s) and the location of the corresponding stored file within LON-CAPA.').'</li>'."\n".
                   11378:                '<li>'.&mt('To change absolute paths to relative paths, or replace directory traversal via "../" within the original reference.').'</li>'."\n".
                   11379:                '</ol></p>'."\n".'<p>'.
                   11380:                &mt('LON-CAPA can make the required changes to your HTML file.').'</p>'."\n".
                   11381:                '<form method="post" name="refchanger" action="'.$actionurl.'">'.
                   11382:                &start_data_table()."\n".
                   11383:                &start_data_table_header_row().
                   11384:                '<th>'.&mt('Change?').'</th>'.
                   11385:                '<th>'.&mt('Current reference').'</th>'.
                   11386:                '<th>'.&mt('Required reference').'</th>'.
                   11387:                &end_data_table_header_row()."\n".
                   11388:                $modifyform.
                   11389:                &end_data_table().'<br />'."\n".$hiddenstate.
                   11390:                '<input type="submit" name="pathchanges" value="'.&mt('Modify HTML file').'" />'.
                   11391:                '</form>'."\n";
                   11392:     }
                   11393:     return;
                   11394: }
                   11395: 
                   11396: sub modify_html_refs {
1.1123    raeburn  11397:     my ($context,$dirpath,$uname,$udom,$dir_root,$url) = @_;
1.987     raeburn  11398:     my $container;
                   11399:     if ($context eq 'portfolio') {
                   11400:         $container = $env{'form.container'};
                   11401:     } elsif ($context eq 'coursedoc') {
                   11402:         $container = $env{'form.primaryurl'};
1.1071    raeburn  11403:     } elsif ($context eq 'manage_dependencies') {
                   11404:         (undef,undef,$container) = &Apache::lonnet::decode_symb($env{'form.symb'});
                   11405:         $container = "/$container";
1.1123    raeburn  11406:     } elsif ($context eq 'syllabus') {
                   11407:         $container = $url;
1.987     raeburn  11408:     } else {
1.1027    raeburn  11409:         $container = $Apache::lonnet::perlvar{'lonDocRoot'}.$env{'form.filename'};
1.987     raeburn  11410:     }
                   11411:     my (%allfiles,%codebase,$output,$content);
                   11412:     my @changes = &get_env_multiple('form.namechange');
1.1126    raeburn  11413:     unless ((@changes > 0) || ($context eq 'syllabus')) {
1.1071    raeburn  11414:         if (wantarray) {
                   11415:             return ('',0,0); 
                   11416:         } else {
                   11417:             return;
                   11418:         }
                   11419:     }
                   11420:     if (($context eq 'portfolio') || ($context eq 'coursedoc') || 
1.1123    raeburn  11421:         ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
1.1071    raeburn  11422:         unless ($container =~ m{^/uploaded/\Q$udom\E/\Q$uname\E/}) {
                   11423:             if (wantarray) {
                   11424:                 return ('',0,0);
                   11425:             } else {
                   11426:                 return;
                   11427:             }
                   11428:         } 
1.987     raeburn  11429:         $content = &Apache::lonnet::getfile($container);
1.1071    raeburn  11430:         if ($content eq '-1') {
                   11431:             if (wantarray) {
                   11432:                 return ('',0,0);
                   11433:             } else {
                   11434:                 return;
                   11435:             }
                   11436:         }
1.987     raeburn  11437:     } else {
1.1071    raeburn  11438:         unless ($container =~ /^\Q$dir_root\E/) {
                   11439:             if (wantarray) {
                   11440:                 return ('',0,0);
                   11441:             } else {
                   11442:                 return;
                   11443:             }
                   11444:         } 
1.987     raeburn  11445:         if (open(my $fh,"<$container")) {
                   11446:             $content = join('', <$fh>);
                   11447:             close($fh);
                   11448:         } else {
1.1071    raeburn  11449:             if (wantarray) {
                   11450:                 return ('',0,0);
                   11451:             } else {
                   11452:                 return;
                   11453:             }
1.987     raeburn  11454:         }
                   11455:     }
                   11456:     my ($count,$codebasecount) = (0,0);
                   11457:     my $mm = new File::MMagic;
                   11458:     my $mime_type = $mm->checktype_contents($content);
                   11459:     if ($mime_type eq 'text/html') {
                   11460:         my $parse_result = 
                   11461:             &Apache::lonnet::extract_embedded_items($container,\%allfiles,
                   11462:                                                     \%codebase,\$content);
                   11463:         if ($parse_result eq 'ok') {
                   11464:             foreach my $i (@changes) {
                   11465:                 my $orig = &unescape($env{'form.embedded_orig_'.$i});
                   11466:                 my $ref = &unescape($env{'form.embedded_ref_'.$i});
                   11467:                 if ($allfiles{$ref}) {
                   11468:                     my $newname =  $orig;
                   11469:                     my ($attrib_regexp,$codebase);
1.1006    raeburn  11470:                     $attrib_regexp = &unescape($env{'form.embedded_attrib_'.$i});
1.987     raeburn  11471:                     if ($attrib_regexp =~ /:/) {
                   11472:                         $attrib_regexp =~ s/\:/|/g;
                   11473:                     }
                   11474:                     if ($content =~ m{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
                   11475:                         my $numchg = ($content =~ s{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
                   11476:                         $count += $numchg;
1.1123    raeburn  11477:                         $allfiles{$newname} = $allfiles{$ref};
1.1148    raeburn  11478:                         delete($allfiles{$ref});
1.987     raeburn  11479:                     }
                   11480:                     if ($env{'form.embedded_codebase_'.$i} ne '') {
1.1006    raeburn  11481:                         $codebase = &unescape($env{'form.embedded_codebase_'.$i});
1.987     raeburn  11482:                         my $numchg = ($content =~ s/(codebase\s*=\s*["']?)\Q$codebase\E(["']?)/$1.$2/i); #' stupid emacs
                   11483:                         $codebasecount ++;
                   11484:                     }
                   11485:                 }
                   11486:             }
1.1123    raeburn  11487:             my $skiprewrites;
1.987     raeburn  11488:             if ($count || $codebasecount) {
                   11489:                 my $saveresult;
1.1071    raeburn  11490:                 if (($context eq 'portfolio') || ($context eq 'coursedoc') || 
1.1123    raeburn  11491:                     ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
1.987     raeburn  11492:                     my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
                   11493:                     if ($url eq $container) {
                   11494:                         my ($fname) = ($container =~ m{/([^/]+)$});
                   11495:                         $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
                   11496:                                             $count,'<span class="LC_filename">'.
1.1071    raeburn  11497:                                             $fname.'</span>').'</p>';
1.987     raeburn  11498:                     } else {
                   11499:                          $output = '<p class="LC_error">'.
                   11500:                                    &mt('Error: update failed for: [_1].',
                   11501:                                    '<span class="LC_filename">'.
                   11502:                                    $container.'</span>').'</p>';
                   11503:                     }
1.1123    raeburn  11504:                     if ($context eq 'syllabus') {
                   11505:                         unless ($saveresult eq 'ok') {
                   11506:                             $skiprewrites = 1;
                   11507:                         }
                   11508:                     }
1.987     raeburn  11509:                 } else {
                   11510:                     if (open(my $fh,">$container")) {
                   11511:                         print $fh $content;
                   11512:                         close($fh);
                   11513:                         $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
                   11514:                                   $count,'<span class="LC_filename">'.
                   11515:                                   $container.'</span>').'</p>';
1.661     raeburn  11516:                     } else {
1.987     raeburn  11517:                          $output = '<p class="LC_error">'.
                   11518:                                    &mt('Error: could not update [_1].',
                   11519:                                    '<span class="LC_filename">'.
                   11520:                                    $container.'</span>').'</p>';
1.661     raeburn  11521:                     }
                   11522:                 }
                   11523:             }
1.1123    raeburn  11524:             if (($context eq 'syllabus') && (!$skiprewrites)) {
                   11525:                 my ($actionurl,$state);
                   11526:                 $actionurl = "/public/$udom/$uname/syllabus";
                   11527:                 my ($ignore,$num,$numpathchanges,$existing,$mapping) =
                   11528:                     &ask_for_embedded_content($actionurl,$state,\%allfiles,
                   11529:                                               \%codebase,
                   11530:                                               {'context' => 'rewrites',
                   11531:                                                'ignore_remote_references' => 1,});
                   11532:                 if (ref($mapping) eq 'HASH') {
                   11533:                     my $rewrites = 0;
                   11534:                     foreach my $key (keys(%{$mapping})) {
                   11535:                         next if ($key =~ m{^https?://});
                   11536:                         my $ref = $mapping->{$key};
                   11537:                         my $newname = "/uploaded/$udom/$uname/portfolio/syllabus/$key";
                   11538:                         my $attrib;
                   11539:                         if (ref($allfiles{$mapping->{$key}}) eq 'ARRAY') {
                   11540:                             $attrib = join('|',@{$allfiles{$mapping->{$key}}});
                   11541:                         }
                   11542:                         if ($content =~ m{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
                   11543:                             my $numchg = ($content =~ s{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
                   11544:                             $rewrites += $numchg;
                   11545:                         }
                   11546:                     }
                   11547:                     if ($rewrites) {
                   11548:                         my $saveresult; 
                   11549:                         my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
                   11550:                         if ($url eq $container) {
                   11551:                             my ($fname) = ($container =~ m{/([^/]+)$});
                   11552:                             $output .= '<p>'.&mt('Rewrote [quant,_1,link] as [quant,_1,absolute link] in [_2].',
                   11553:                                             $count,'<span class="LC_filename">'.
                   11554:                                             $fname.'</span>').'</p>';
                   11555:                         } else {
                   11556:                             $output .= '<p class="LC_error">'.
                   11557:                                        &mt('Error: could not update links in [_1].',
                   11558:                                        '<span class="LC_filename">'.
                   11559:                                        $container.'</span>').'</p>';
                   11560: 
                   11561:                         }
                   11562:                     }
                   11563:                 }
                   11564:             }
1.987     raeburn  11565:         } else {
                   11566:             &logthis('Failed to parse '.$container.
                   11567:                      ' to modify references: '.$parse_result);
1.661     raeburn  11568:         }
                   11569:     }
1.1071    raeburn  11570:     if (wantarray) {
                   11571:         return ($output,$count,$codebasecount);
                   11572:     } else {
                   11573:         return $output;
                   11574:     }
1.661     raeburn  11575: }
                   11576: 
                   11577: sub check_for_existing {
                   11578:     my ($path,$fname,$element) = @_;
                   11579:     my ($state,$msg);
                   11580:     if (-d $path.'/'.$fname) {
                   11581:         $state = 'exists';
                   11582:         $msg = &mt('Unable to upload [_1]. A directory by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
                   11583:     } elsif (-e $path.'/'.$fname) {
                   11584:         $state = 'exists';
                   11585:         $msg = &mt('Unable to upload [_1]. A file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
                   11586:     }
                   11587:     if ($state eq 'exists') {
                   11588:         $msg = '<span class="LC_error">'.$msg.'</span><br />';
                   11589:     }
                   11590:     return ($state,$msg);
                   11591: }
                   11592: 
                   11593: sub check_for_upload {
                   11594:     my ($path,$fname,$group,$element,$portfolio_root,$port_path,
                   11595:         $disk_quota,$current_disk_usage,$uname,$udom) = @_;
1.985     raeburn  11596:     my $filesize = length($env{'form.'.$element});
                   11597:     if (!$filesize) {
                   11598:         my $msg = '<span class="LC_error">'.
                   11599:                   &mt('Unable to upload [_1]. (size = [_2] bytes)', 
                   11600:                       '<span class="LC_filename">'.$fname.'</span>',
                   11601:                       $filesize).'<br />'.
1.1007    raeburn  11602:                   &mt('Either the file you attempted to upload was empty, or your web browser was unable to read its contents.').'<br />'.
1.985     raeburn  11603:                   '</span>';
                   11604:         return ('zero_bytes',$msg);
                   11605:     }
                   11606:     $filesize =  $filesize/1000; #express in k (1024?)
1.661     raeburn  11607:     my $getpropath = 1;
1.1021    raeburn  11608:     my ($dirlistref,$listerror) =
                   11609:          &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname,$getpropath);
1.661     raeburn  11610:     my $found_file = 0;
                   11611:     my $locked_file = 0;
1.991     raeburn  11612:     my @lockers;
                   11613:     my $navmap;
                   11614:     if ($env{'request.course.id'}) {
                   11615:         $navmap = Apache::lonnavmaps::navmap->new();
                   11616:     }
1.1021    raeburn  11617:     if (ref($dirlistref) eq 'ARRAY') {
                   11618:         foreach my $line (@{$dirlistref}) {
                   11619:             my ($file_name,$rest)=split(/\&/,$line,2);
                   11620:             if ($file_name eq $fname){
                   11621:                 $file_name = $path.$file_name;
                   11622:                 if ($group ne '') {
                   11623:                     $file_name = $group.$file_name;
                   11624:                 }
                   11625:                 $found_file = 1;
                   11626:                 if (&Apache::lonnet::is_locked($file_name,$udom,$uname,\@lockers) eq 'true') {
                   11627:                     foreach my $lock (@lockers) {
                   11628:                         if (ref($lock) eq 'ARRAY') {
                   11629:                             my ($symb,$crsid) = @{$lock};
                   11630:                             if ($crsid eq $env{'request.course.id'}) {
                   11631:                                 if (ref($navmap)) {
                   11632:                                     my $res = $navmap->getBySymb($symb);
                   11633:                                     foreach my $part (@{$res->parts()}) { 
                   11634:                                         my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part);
                   11635:                                         unless (($slot_status == $res->RESERVED) ||
                   11636:                                                 ($slot_status == $res->RESERVED_LOCATION)) {
                   11637:                                             $locked_file = 1;
                   11638:                                         }
1.991     raeburn  11639:                                     }
1.1021    raeburn  11640:                                 } else {
                   11641:                                     $locked_file = 1;
1.991     raeburn  11642:                                 }
                   11643:                             } else {
                   11644:                                 $locked_file = 1;
                   11645:                             }
                   11646:                         }
1.1021    raeburn  11647:                    }
                   11648:                 } else {
                   11649:                     my @info = split(/\&/,$rest);
                   11650:                     my $currsize = $info[6]/1000;
                   11651:                     if ($currsize < $filesize) {
                   11652:                         my $extra = $filesize - $currsize;
                   11653:                         if (($current_disk_usage + $extra) > $disk_quota) {
1.1179    bisitz   11654:                             my $msg = '<p class="LC_warning">'.
1.1021    raeburn  11655:                                       &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded if existing (smaller) file with same name (size = [_3] kilobytes) is replaced.',
1.1179    bisitz   11656:                                           '<span class="LC_filename">'.$fname.'</span>',$filesize,$currsize).'</p>'.
                   11657:                                       '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
                   11658:                                                    $disk_quota,$current_disk_usage).'</p>';
1.1021    raeburn  11659:                             return ('will_exceed_quota',$msg);
                   11660:                         }
1.984     raeburn  11661:                     }
                   11662:                 }
1.661     raeburn  11663:             }
                   11664:         }
                   11665:     }
                   11666:     if (($current_disk_usage + $filesize) > $disk_quota){
1.1179    bisitz   11667:         my $msg = '<p class="LC_warning">'.
                   11668:                 &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.','<span class="LC_filename">'.$fname.'</span>',$filesize).'</p>'.
1.1184    raeburn  11669:                   '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',$disk_quota,$current_disk_usage).'</p>';
1.661     raeburn  11670:         return ('will_exceed_quota',$msg);
                   11671:     } elsif ($found_file) {
                   11672:         if ($locked_file) {
1.1179    bisitz   11673:             my $msg = '<p class="LC_warning">';
1.661     raeburn  11674:             $msg .= &mt('Unable to upload [_1]. A locked file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>','<span class="LC_filename">'.$port_path.$env{'form.currentpath'}.'</span>');
1.1179    bisitz   11675:             $msg .= '</p>';
1.661     raeburn  11676:             $msg .= &mt('You will be able to rename or delete existing [_1] after a grade has been assigned.','<span class="LC_filename">'.$fname.'</span>');
                   11677:             return ('file_locked',$msg);
                   11678:         } else {
1.1179    bisitz   11679:             my $msg = '<p class="LC_error">';
1.984     raeburn  11680:             $msg .= &mt(' A file by that name: [_1] was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$port_path.$env{'form.currentpath'});
1.1179    bisitz   11681:             $msg .= '</p>';
1.984     raeburn  11682:             return ('existingfile',$msg);
1.661     raeburn  11683:         }
                   11684:     }
                   11685: }
                   11686: 
1.987     raeburn  11687: sub check_for_traversal {
                   11688:     my ($path,$url,$toplevel) = @_;
                   11689:     my @parts=split(/\//,$path);
                   11690:     my $cleanpath;
                   11691:     my $fullpath = $url;
                   11692:     for (my $i=0;$i<@parts;$i++) {
                   11693:         next if ($parts[$i] eq '.');
                   11694:         if ($parts[$i] eq '..') {
                   11695:             $fullpath =~ s{([^/]+/)$}{};
                   11696:         } else {
                   11697:             $fullpath .= $parts[$i].'/';
                   11698:         }
                   11699:     }
                   11700:     if ($fullpath =~ /^\Q$url\E(.*)$/) {
                   11701:         $cleanpath = $1;
                   11702:     } elsif ($fullpath =~ /^\Q$toplevel\E(.*)$/) {
                   11703:         my $curr_toprel = $1;
                   11704:         my @parts = split(/\//,$curr_toprel);
                   11705:         my ($url_toprel) = ($url =~ /^\Q$toplevel\E(.*)$/);
                   11706:         my @urlparts = split(/\//,$url_toprel);
                   11707:         my $doubledots;
                   11708:         my $startdiff = -1;
                   11709:         for (my $i=0; $i<@urlparts; $i++) {
                   11710:             if ($startdiff == -1) {
                   11711:                 unless ($urlparts[$i] eq $parts[$i]) {
                   11712:                     $startdiff = $i;
                   11713:                     $doubledots .= '../';
                   11714:                 }
                   11715:             } else {
                   11716:                 $doubledots .= '../';
                   11717:             }
                   11718:         }
                   11719:         if ($startdiff > -1) {
                   11720:             $cleanpath = $doubledots;
                   11721:             for (my $i=$startdiff; $i<@parts; $i++) {
                   11722:                 $cleanpath .= $parts[$i].'/';
                   11723:             }
                   11724:         }
                   11725:     }
                   11726:     $cleanpath =~ s{(/)$}{};
                   11727:     return $cleanpath;
                   11728: }
1.31      albertel 11729: 
1.1053    raeburn  11730: sub is_archive_file {
                   11731:     my ($mimetype) = @_;
                   11732:     if (($mimetype eq 'application/octet-stream') ||
                   11733:         ($mimetype eq 'application/x-stuffit') ||
                   11734:         ($mimetype =~ m{^application/(x\-)?(compressed|tar|zip|tgz|gz|gtar|gzip|gunzip|bz|bz2|bzip2)})) {
                   11735:         return 1;
                   11736:     }
                   11737:     return;
                   11738: }
                   11739: 
                   11740: sub decompress_form {
1.1065    raeburn  11741:     my ($mimetype,$archiveurl,$action,$noextract,$hiddenelements,$dirlist) = @_;
1.1053    raeburn  11742:     my %lt = &Apache::lonlocal::texthash (
                   11743:         this => 'This file is an archive file.',
1.1067    raeburn  11744:         camt => 'This file is a Camtasia archive file.',
1.1065    raeburn  11745:         itsc => 'Its contents are as follows:',
1.1053    raeburn  11746:         youm => 'You may wish to extract its contents.',
                   11747:         extr => 'Extract contents',
1.1067    raeburn  11748:         auto => 'LON-CAPA can process the files automatically, or you can decide how each should be handled.',
                   11749:         proa => 'Process automatically?',
1.1053    raeburn  11750:         yes  => 'Yes',
                   11751:         no   => 'No',
1.1067    raeburn  11752:         fold => 'Title for folder containing movie',
                   11753:         movi => 'Title for page containing embedded movie', 
1.1053    raeburn  11754:     );
1.1065    raeburn  11755:     my $fileloc = &Apache::lonnet::filelocation(undef,$archiveurl);
1.1067    raeburn  11756:     my ($is_camtasia,$topdir,%toplevel,@paths);
1.1065    raeburn  11757:     my $info = &list_archive_contents($fileloc,\@paths);
                   11758:     if (@paths) {
                   11759:         foreach my $path (@paths) {
                   11760:             $path =~ s{^/}{};
1.1067    raeburn  11761:             if ($path =~ m{^([^/]+)/$}) {
                   11762:                 $topdir = $1;
                   11763:             }
1.1065    raeburn  11764:             if ($path =~ m{^([^/]+)/}) {
                   11765:                 $toplevel{$1} = $path;
                   11766:             } else {
                   11767:                 $toplevel{$path} = $path;
                   11768:             }
                   11769:         }
                   11770:     }
1.1067    raeburn  11771:     if ($mimetype =~ m{^application/(x\-)?(compressed|zip)}) {
1.1164    raeburn  11772:         my @camtasia6 = ("$topdir/","$topdir/index.html",
1.1067    raeburn  11773:                         "$topdir/media/",
                   11774:                         "$topdir/media/$topdir.mp4",
                   11775:                         "$topdir/media/FirstFrame.png",
                   11776:                         "$topdir/media/player.swf",
                   11777:                         "$topdir/media/swfobject.js",
                   11778:                         "$topdir/media/expressInstall.swf");
1.1197    raeburn  11779:         my @camtasia8_1 = ("$topdir/","$topdir/$topdir.html",
1.1164    raeburn  11780:                          "$topdir/$topdir.mp4",
                   11781:                          "$topdir/$topdir\_config.xml",
                   11782:                          "$topdir/$topdir\_controller.swf",
                   11783:                          "$topdir/$topdir\_embed.css",
                   11784:                          "$topdir/$topdir\_First_Frame.png",
                   11785:                          "$topdir/$topdir\_player.html",
                   11786:                          "$topdir/$topdir\_Thumbnails.png",
                   11787:                          "$topdir/playerProductInstall.swf",
                   11788:                          "$topdir/scripts/",
                   11789:                          "$topdir/scripts/config_xml.js",
                   11790:                          "$topdir/scripts/handlebars.js",
                   11791:                          "$topdir/scripts/jquery-1.7.1.min.js",
                   11792:                          "$topdir/scripts/jquery-ui-1.8.15.custom.min.js",
                   11793:                          "$topdir/scripts/modernizr.js",
                   11794:                          "$topdir/scripts/player-min.js",
                   11795:                          "$topdir/scripts/swfobject.js",
                   11796:                          "$topdir/skins/",
                   11797:                          "$topdir/skins/configuration_express.xml",
                   11798:                          "$topdir/skins/express_show/",
                   11799:                          "$topdir/skins/express_show/player-min.css",
                   11800:                          "$topdir/skins/express_show/spritesheet.png");
1.1197    raeburn  11801:         my @camtasia8_4 = ("$topdir/","$topdir/$topdir.html",
                   11802:                          "$topdir/$topdir.mp4",
                   11803:                          "$topdir/$topdir\_config.xml",
                   11804:                          "$topdir/$topdir\_controller.swf",
                   11805:                          "$topdir/$topdir\_embed.css",
                   11806:                          "$topdir/$topdir\_First_Frame.png",
                   11807:                          "$topdir/$topdir\_player.html",
                   11808:                          "$topdir/$topdir\_Thumbnails.png",
                   11809:                          "$topdir/playerProductInstall.swf",
                   11810:                          "$topdir/scripts/",
                   11811:                          "$topdir/scripts/config_xml.js",
                   11812:                          "$topdir/scripts/techsmith-smart-player.min.js",
                   11813:                          "$topdir/skins/",
                   11814:                          "$topdir/skins/configuration_express.xml",
                   11815:                          "$topdir/skins/express_show/",
                   11816:                          "$topdir/skins/express_show/spritesheet.min.css",
                   11817:                          "$topdir/skins/express_show/spritesheet.png",
                   11818:                          "$topdir/skins/express_show/techsmith-smart-player.min.css");
1.1164    raeburn  11819:         my @diffs = &compare_arrays(\@paths,\@camtasia6);
1.1067    raeburn  11820:         if (@diffs == 0) {
1.1164    raeburn  11821:             $is_camtasia = 6;
                   11822:         } else {
1.1197    raeburn  11823:             @diffs = &compare_arrays(\@paths,\@camtasia8_1);
1.1164    raeburn  11824:             if (@diffs == 0) {
                   11825:                 $is_camtasia = 8;
1.1197    raeburn  11826:             } else {
                   11827:                 @diffs = &compare_arrays(\@paths,\@camtasia8_4);
                   11828:                 if (@diffs == 0) {
                   11829:                     $is_camtasia = 8;
                   11830:                 }
1.1164    raeburn  11831:             }
1.1067    raeburn  11832:         }
                   11833:     }
                   11834:     my $output;
                   11835:     if ($is_camtasia) {
                   11836:         $output = <<"ENDCAM";
                   11837: <script type="text/javascript" language="Javascript">
                   11838: // <![CDATA[
                   11839: 
                   11840: function camtasiaToggle() {
                   11841:     for (var i=0; i<document.uploaded_decompress.autoextract_camtasia.length; i++) {
                   11842:         if (document.uploaded_decompress.autoextract_camtasia[i].checked) {
1.1164    raeburn  11843:             if (document.uploaded_decompress.autoextract_camtasia[i].value == $is_camtasia) {
1.1067    raeburn  11844:                 document.getElementById('camtasia_titles').style.display='block';
                   11845:             } else {
                   11846:                 document.getElementById('camtasia_titles').style.display='none';
                   11847:             }
                   11848:         }
                   11849:     }
                   11850:     return;
                   11851: }
                   11852: 
                   11853: // ]]>
                   11854: </script>
                   11855: <p>$lt{'camt'}</p>
                   11856: ENDCAM
1.1065    raeburn  11857:     } else {
1.1067    raeburn  11858:         $output = '<p>'.$lt{'this'};
                   11859:         if ($info eq '') {
                   11860:             $output .= ' '.$lt{'youm'}.'</p>'."\n";
                   11861:         } else {
                   11862:             $output .= ' '.$lt{'itsc'}.'</p>'."\n".
                   11863:                        '<div><pre>'.$info.'</pre></div>';
                   11864:         }
1.1065    raeburn  11865:     }
1.1067    raeburn  11866:     $output .= '<form name="uploaded_decompress" action="'.$action.'" method="post">'."\n";
1.1065    raeburn  11867:     my $duplicates;
                   11868:     my $num = 0;
                   11869:     if (ref($dirlist) eq 'ARRAY') {
                   11870:         foreach my $item (@{$dirlist}) {
                   11871:             if (ref($item) eq 'ARRAY') {
                   11872:                 if (exists($toplevel{$item->[0]})) {
                   11873:                     $duplicates .= 
                   11874:                         &start_data_table_row().
                   11875:                         '<td><label><input type="radio" name="archive_overwrite_'.$num.'" '.
                   11876:                         'value="0" checked="checked" />'.&mt('No').'</label>'.
                   11877:                         '&nbsp;<label><input type="radio" name="archive_overwrite_'.$num.'" '.
                   11878:                         'value="1" />'.&mt('Yes').'</label>'.
                   11879:                         '<input type="hidden" name="archive_overwrite_name_'.$num.'" value="'.$item->[0].'" /></td>'."\n".
                   11880:                         '<td>'.$item->[0].'</td>';
                   11881:                     if ($item->[2]) {
                   11882:                         $duplicates .= '<td>'.&mt('Directory').'</td>';
                   11883:                     } else {
                   11884:                         $duplicates .= '<td>'.&mt('File').'</td>';
                   11885:                     }
                   11886:                     $duplicates .= '<td>'.$item->[3].'</td>'.
                   11887:                                    '<td>'.
                   11888:                                    &Apache::lonlocal::locallocaltime($item->[4]).
                   11889:                                    '</td>'.
                   11890:                                    &end_data_table_row();
                   11891:                     $num ++;
                   11892:                 }
                   11893:             }
                   11894:         }
                   11895:     }
                   11896:     my $itemcount;
                   11897:     if (@paths > 0) {
                   11898:         $itemcount = scalar(@paths);
                   11899:     } else {
                   11900:         $itemcount = 1;
                   11901:     }
1.1067    raeburn  11902:     if ($is_camtasia) {
                   11903:         $output .= $lt{'auto'}.'<br />'.
                   11904:                    '<span class="LC_nobreak">'.$lt{'proa'}.'<label>'.
1.1164    raeburn  11905:                    '<input type="radio" name="autoextract_camtasia" value="'.$is_camtasia.'" onclick="javascript:camtasiaToggle();" checked="checked" />'.
1.1067    raeburn  11906:                    $lt{'yes'}.'</label>&nbsp;<label>'.
                   11907:                    '<input type="radio" name="autoextract_camtasia" value="0" onclick="javascript:camtasiaToggle();" />'.
                   11908:                    $lt{'no'}.'</label></span><br />'.
                   11909:                    '<div id="camtasia_titles" style="display:block">'.
                   11910:                    &Apache::lonhtmlcommon::start_pick_box().
                   11911:                    &Apache::lonhtmlcommon::row_title($lt{'fold'}).
                   11912:                    '<input type="textbox" name="camtasia_foldername" value="'.$env{'form.comment'}.'" />'."\n".
                   11913:                    &Apache::lonhtmlcommon::row_closure().
                   11914:                    &Apache::lonhtmlcommon::row_title($lt{'movi'}).
                   11915:                    '<input type="textbox" name="camtasia_moviename" value="" />'."\n".
                   11916:                    &Apache::lonhtmlcommon::row_closure(1).
                   11917:                    &Apache::lonhtmlcommon::end_pick_box().
                   11918:                    '</div>';
                   11919:     }
1.1065    raeburn  11920:     $output .= 
                   11921:         '<input type="hidden" name="archive_overwrite_total" value="'.$num.'" />'.
1.1067    raeburn  11922:         '<input type="hidden" name="archive_itemcount" value="'.$itemcount.'" />'.
                   11923:         "\n";
1.1065    raeburn  11924:     if ($duplicates ne '') {
                   11925:         $output .= '<p><span class="LC_warning">'.
                   11926:                    &mt('Warning: decompression of the archive will overwrite the following items which already exist:').'</span><br />'.  
                   11927:                    &start_data_table().
                   11928:                    &start_data_table_header_row().
                   11929:                    '<th>'.&mt('Overwrite?').'</th>'.
                   11930:                    '<th>'.&mt('Name').'</th>'.
                   11931:                    '<th>'.&mt('Type').'</th>'.
                   11932:                    '<th>'.&mt('Size').'</th>'.
                   11933:                    '<th>'.&mt('Last modified').'</th>'.
                   11934:                    &end_data_table_header_row().
                   11935:                    $duplicates.
                   11936:                    &end_data_table().
                   11937:                    '</p>';
                   11938:     }
1.1067    raeburn  11939:     $output .= '<input type="hidden" name="archiveurl" value="'.$archiveurl.'" />'."\n";
1.1053    raeburn  11940:     if (ref($hiddenelements) eq 'HASH') {
                   11941:         foreach my $hidden (sort(keys(%{$hiddenelements}))) {
                   11942:             $output .= '<input type="hidden" name="'.$hidden.'" value="'.$hiddenelements->{$hidden}.'" />'."\n";
                   11943:         }
                   11944:     }
                   11945:     $output .= <<"END";
1.1067    raeburn  11946: <br />
1.1053    raeburn  11947: <input type="submit" name="decompress" value="$lt{'extr'}" />
                   11948: </form>
                   11949: $noextract
                   11950: END
                   11951:     return $output;
                   11952: }
                   11953: 
1.1065    raeburn  11954: sub decompression_utility {
                   11955:     my ($program) = @_;
                   11956:     my @utilities = ('tar','gunzip','bunzip2','unzip'); 
                   11957:     my $location;
                   11958:     if (grep(/^\Q$program\E$/,@utilities)) { 
                   11959:         foreach my $dir ('/bin/','/usr/bin/','/usr/local/bin/','/sbin/',
                   11960:                          '/usr/sbin/') {
                   11961:             if (-x $dir.$program) {
                   11962:                 $location = $dir.$program;
                   11963:                 last;
                   11964:             }
                   11965:         }
                   11966:     }
                   11967:     return $location;
                   11968: }
                   11969: 
                   11970: sub list_archive_contents {
                   11971:     my ($file,$pathsref) = @_;
                   11972:     my (@cmd,$output);
                   11973:     my $needsregexp;
                   11974:     if ($file =~ /\.zip$/) {
                   11975:         @cmd = (&decompression_utility('unzip'),"-l");
                   11976:         $needsregexp = 1;
                   11977:     } elsif (($file =~ m/\.tar\.gz$/) ||
                   11978:              ($file =~ /\.tgz$/)) {
                   11979:         @cmd = (&decompression_utility('tar'),"-ztf");
                   11980:     } elsif ($file =~ /\.tar\.bz2$/) {
                   11981:         @cmd = (&decompression_utility('tar'),"-jtf");
                   11982:     } elsif ($file =~ m|\.tar$|) {
                   11983:         @cmd = (&decompression_utility('tar'),"-tf");
                   11984:     }
                   11985:     if (@cmd) {
                   11986:         undef($!);
                   11987:         undef($@);
                   11988:         if (open(my $fh,"-|", @cmd, $file)) {
                   11989:             while (my $line = <$fh>) {
                   11990:                 $output .= $line;
                   11991:                 chomp($line);
                   11992:                 my $item;
                   11993:                 if ($needsregexp) {
                   11994:                     ($item) = ($line =~ /^\s*\d+\s+[\d\-]+\s+[\d:]+\s*(.+)$/); 
                   11995:                 } else {
                   11996:                     $item = $line;
                   11997:                 }
                   11998:                 if ($item ne '') {
                   11999:                     unless (grep(/^\Q$item\E$/,@{$pathsref})) {
                   12000:                         push(@{$pathsref},$item);
                   12001:                     } 
                   12002:                 }
                   12003:             }
                   12004:             close($fh);
                   12005:         }
                   12006:     }
                   12007:     return $output;
                   12008: }
                   12009: 
1.1053    raeburn  12010: sub decompress_uploaded_file {
                   12011:     my ($file,$dir) = @_;
                   12012:     &Apache::lonnet::appenv({'cgi.file' => $file});
                   12013:     &Apache::lonnet::appenv({'cgi.dir' => $dir});
                   12014:     my $result = &Apache::lonnet::ssi_body('/cgi-bin/decompress.pl');
                   12015:     my ($handle) = ($env{'user.environment'} =~m{/([^/]+)\.id$});
                   12016:     my $lonidsdir = $Apache::lonnet::perlvar{'lonIDsDir'};
                   12017:     &Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle,1);
                   12018:     my $decompressed = $env{'cgi.decompressed'};
                   12019:     &Apache::lonnet::delenv('cgi.file');
                   12020:     &Apache::lonnet::delenv('cgi.dir');
                   12021:     &Apache::lonnet::delenv('cgi.decompressed');
                   12022:     return ($decompressed,$result);
                   12023: }
                   12024: 
1.1055    raeburn  12025: sub process_decompression {
                   12026:     my ($docudom,$docuname,$file,$destination,$dir_root,$hiddenelem) = @_;
                   12027:     my ($dir,$error,$warning,$output);
1.1180    raeburn  12028:     if ($file !~ /\.(zip|tar|bz2|gz|tar.gz|tar.bz2|tgz)$/i) {
1.1120    bisitz   12029:         $error = &mt('Filename not a supported archive file type.').
                   12030:                  '<br />'.&mt('Filename should end with one of: [_1].',
1.1055    raeburn  12031:                               '.zip, .tar, .bz2, .gz, .tar.gz, .tar.bz2, .tgz');
                   12032:     } else {
                   12033:         my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
                   12034:         if ($docuhome eq 'no_host') {
                   12035:             $error = &mt('Could not determine home server for course.');
                   12036:         } else {
                   12037:             my @ids=&Apache::lonnet::current_machine_ids();
                   12038:             my $currdir = "$dir_root/$destination";
                   12039:             if (grep(/^\Q$docuhome\E$/,@ids)) {
                   12040:                 $dir = &LONCAPA::propath($docudom,$docuname).
                   12041:                        "$dir_root/$destination";
                   12042:             } else {
                   12043:                 $dir = $Apache::lonnet::perlvar{'lonDocRoot'}.
                   12044:                        "$dir_root/$docudom/$docuname/$destination";
                   12045:                 unless (&Apache::lonnet::repcopy_userfile("$dir/$file") eq 'ok') {
                   12046:                     $error = &mt('Archive file not found.');
                   12047:                 }
                   12048:             }
1.1065    raeburn  12049:             my (@to_overwrite,@to_skip);
                   12050:             if ($env{'form.archive_overwrite_total'} > 0) {
                   12051:                 my $total = $env{'form.archive_overwrite_total'};
                   12052:                 for (my $i=0; $i<$total; $i++) {
                   12053:                     if ($env{'form.archive_overwrite_'.$i} == 1) {
                   12054:                         push(@to_overwrite,$env{'form.archive_overwrite_name_'.$i});
                   12055:                     } elsif ($env{'form.archive_overwrite_'.$i} == 0) {
                   12056:                         push(@to_skip,$env{'form.archive_overwrite_name_'.$i});
                   12057:                     }
                   12058:                 }
                   12059:             }
                   12060:             my $numskip = scalar(@to_skip);
                   12061:             if (($numskip > 0) && 
                   12062:                 ($numskip == $env{'form.archive_itemcount'})) {
                   12063:                 $warning = &mt('All items in the archive file already exist, and no overwriting of existing files has been requested.');         
                   12064:             } elsif ($dir eq '') {
1.1055    raeburn  12065:                 $error = &mt('Directory containing archive file unavailable.');
                   12066:             } elsif (!$error) {
1.1065    raeburn  12067:                 my ($decompressed,$display);
                   12068:                 if ($numskip > 0) {
                   12069:                     my $tempdir = time.'_'.$$.int(rand(10000));
                   12070:                     mkdir("$dir/$tempdir",0755);
                   12071:                     system("mv $dir/$file $dir/$tempdir/$file");
                   12072:                     ($decompressed,$display) = 
                   12073:                         &decompress_uploaded_file($file,"$dir/$tempdir");
                   12074:                     foreach my $item (@to_skip) {
                   12075:                         if (($item ne '') && ($item !~ /\.\./)) {
                   12076:                             if (-f "$dir/$tempdir/$item") { 
                   12077:                                 unlink("$dir/$tempdir/$item");
                   12078:                             } elsif (-d "$dir/$tempdir/$item") {
                   12079:                                 system("rm -rf $dir/$tempdir/$item");
                   12080:                             }
                   12081:                         }
                   12082:                     }
                   12083:                     system("mv $dir/$tempdir/* $dir");
                   12084:                     rmdir("$dir/$tempdir");   
                   12085:                 } else {
                   12086:                     ($decompressed,$display) = 
                   12087:                         &decompress_uploaded_file($file,$dir);
                   12088:                 }
1.1055    raeburn  12089:                 if ($decompressed eq 'ok') {
1.1065    raeburn  12090:                     $output = '<p class="LC_info">'.
                   12091:                               &mt('Files extracted successfully from archive.').
                   12092:                               '</p>'."\n";
1.1055    raeburn  12093:                     my ($warning,$result,@contents);
                   12094:                     my ($newdirlistref,$newlisterror) =
                   12095:                         &Apache::lonnet::dirlist($currdir,$docudom,
                   12096:                                                  $docuname,1);
                   12097:                     my (%is_dir,%changes,@newitems);
                   12098:                     my $dirptr = 16384;
1.1065    raeburn  12099:                     if (ref($newdirlistref) eq 'ARRAY') {
1.1055    raeburn  12100:                         foreach my $dir_line (@{$newdirlistref}) {
                   12101:                             my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
1.1065    raeburn  12102:                             unless (($item =~ /^\.+$/) || ($item eq $file) || 
                   12103:                                     ((@to_skip > 0) && (grep(/^\Q$item\E$/,@to_skip)))) {
1.1055    raeburn  12104:                                 push(@newitems,$item);
                   12105:                                 if ($dirptr&$testdir) {
                   12106:                                     $is_dir{$item} = 1;
                   12107:                                 }
                   12108:                                 $changes{$item} = 1;
                   12109:                             }
                   12110:                         }
                   12111:                     }
                   12112:                     if (keys(%changes) > 0) {
                   12113:                         foreach my $item (sort(@newitems)) {
                   12114:                             if ($changes{$item}) {
                   12115:                                 push(@contents,$item);
                   12116:                             }
                   12117:                         }
                   12118:                     }
                   12119:                     if (@contents > 0) {
1.1067    raeburn  12120:                         my $wantform;
                   12121:                         unless ($env{'form.autoextract_camtasia'}) {
                   12122:                             $wantform = 1;
                   12123:                         }
1.1056    raeburn  12124:                         my (%children,%parent,%dirorder,%titles);
1.1055    raeburn  12125:                         my ($count,$datatable) = &get_extracted($docudom,$docuname,
                   12126:                                                                 $currdir,\%is_dir,
                   12127:                                                                 \%children,\%parent,
1.1056    raeburn  12128:                                                                 \@contents,\%dirorder,
                   12129:                                                                 \%titles,$wantform);
1.1055    raeburn  12130:                         if ($datatable ne '') {
                   12131:                             $output .= &archive_options_form('decompressed',$datatable,
                   12132:                                                              $count,$hiddenelem);
1.1065    raeburn  12133:                             my $startcount = 6;
1.1055    raeburn  12134:                             $output .= &archive_javascript($startcount,$count,
1.1056    raeburn  12135:                                                            \%titles,\%children);
1.1055    raeburn  12136:                         }
1.1067    raeburn  12137:                         if ($env{'form.autoextract_camtasia'}) {
1.1164    raeburn  12138:                             my $version = $env{'form.autoextract_camtasia'};
1.1067    raeburn  12139:                             my %displayed;
                   12140:                             my $total = 1;
                   12141:                             $env{'form.archive_directory'} = [];
                   12142:                             foreach my $i (sort { $a <=> $b } keys(%dirorder)) {
                   12143:                                 my $path = join('/',map { $titles{$_}; } @{$dirorder{$i}});
                   12144:                                 $path =~ s{/$}{};
                   12145:                                 my $item;
                   12146:                                 if ($path ne '') {
                   12147:                                     $item = "$path/$titles{$i}";
                   12148:                                 } else {
                   12149:                                     $item = $titles{$i};
                   12150:                                 }
                   12151:                                 $env{'form.archive_content_'.$i} = "$dir_root/$destination/$item";
                   12152:                                 if ($item eq $contents[0]) {
                   12153:                                     push(@{$env{'form.archive_directory'}},$i);
                   12154:                                     $env{'form.archive_'.$i} = 'display';
                   12155:                                     $env{'form.archive_title_'.$i} = $env{'form.camtasia_foldername'};
                   12156:                                     $displayed{'folder'} = $i;
1.1164    raeburn  12157:                                 } elsif ((($item eq "$contents[0]/index.html") && ($version == 6)) ||
                   12158:                                          (($item eq "$contents[0]/$contents[0]".'.html') && ($version == 8))) { 
1.1067    raeburn  12159:                                     $env{'form.archive_'.$i} = 'display';
                   12160:                                     $env{'form.archive_title_'.$i} = $env{'form.camtasia_moviename'};
                   12161:                                     $displayed{'web'} = $i;
                   12162:                                 } else {
1.1164    raeburn  12163:                                     if ((($item eq "$contents[0]/media") && ($version == 6)) ||
                   12164:                                         ((($item eq "$contents[0]/scripts") || ($item eq "$contents[0]/skins") ||
                   12165:                                              ($item eq "$contents[0]/skins/express_show")) && ($version == 8))) {
1.1067    raeburn  12166:                                         push(@{$env{'form.archive_directory'}},$i);
                   12167:                                     }
                   12168:                                     $env{'form.archive_'.$i} = 'dependency';
                   12169:                                 }
                   12170:                                 $total ++;
                   12171:                             }
                   12172:                             for (my $i=1; $i<$total; $i++) {
                   12173:                                 next if ($i == $displayed{'web'});
                   12174:                                 next if ($i == $displayed{'folder'});
                   12175:                                 $env{'form.archive_dependent_on_'.$i} = $displayed{'web'};
                   12176:                             }
                   12177:                             $env{'form.phase'} = 'decompress_cleanup';
                   12178:                             $env{'form.archivedelete'} = 1;
                   12179:                             $env{'form.archive_count'} = $total-1;
                   12180:                             $output .=
                   12181:                                 &process_extracted_files('coursedocs',$docudom,
                   12182:                                                          $docuname,$destination,
                   12183:                                                          $dir_root,$hiddenelem);
                   12184:                         }
1.1055    raeburn  12185:                     } else {
                   12186:                         $warning = &mt('No new items extracted from archive file.');
                   12187:                     }
                   12188:                 } else {
                   12189:                     $output = $display;
                   12190:                     $error = &mt('An error occurred during extraction from the archive file.');
                   12191:                 }
                   12192:             }
                   12193:         }
                   12194:     }
                   12195:     if ($error) {
                   12196:         $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
                   12197:                    $error.'</p>'."\n";
                   12198:     }
                   12199:     if ($warning) {
                   12200:         $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
                   12201:     }
                   12202:     return $output;
                   12203: }
                   12204: 
                   12205: sub get_extracted {
1.1056    raeburn  12206:     my ($docudom,$docuname,$currdir,$is_dir,$children,$parent,$contents,$dirorder,
                   12207:         $titles,$wantform) = @_;
1.1055    raeburn  12208:     my $count = 0;
                   12209:     my $depth = 0;
                   12210:     my $datatable;
1.1056    raeburn  12211:     my @hierarchy;
1.1055    raeburn  12212:     return unless ((ref($is_dir) eq 'HASH') && (ref($children) eq 'HASH') &&
1.1056    raeburn  12213:                    (ref($parent) eq 'HASH') && (ref($contents) eq 'ARRAY') &&
                   12214:                    (ref($dirorder) eq 'HASH') && (ref($titles) eq 'HASH'));
1.1055    raeburn  12215:     foreach my $item (@{$contents}) {
                   12216:         $count ++;
1.1056    raeburn  12217:         @{$dirorder->{$count}} = @hierarchy;
                   12218:         $titles->{$count} = $item;
1.1055    raeburn  12219:         &archive_hierarchy($depth,$count,$parent,$children);
                   12220:         if ($wantform) {
                   12221:             $datatable .= &archive_row($is_dir->{$item},$item,
                   12222:                                        $currdir,$depth,$count);
                   12223:         }
                   12224:         if ($is_dir->{$item}) {
                   12225:             $depth ++;
1.1056    raeburn  12226:             push(@hierarchy,$count);
                   12227:             $parent->{$depth} = $count;
1.1055    raeburn  12228:             $datatable .=
                   12229:                 &recurse_extracted_archive("$currdir/$item",$docudom,$docuname,
1.1056    raeburn  12230:                                            \$depth,\$count,\@hierarchy,$dirorder,
                   12231:                                            $children,$parent,$titles,$wantform);
1.1055    raeburn  12232:             $depth --;
1.1056    raeburn  12233:             pop(@hierarchy);
1.1055    raeburn  12234:         }
                   12235:     }
                   12236:     return ($count,$datatable);
                   12237: }
                   12238: 
                   12239: sub recurse_extracted_archive {
1.1056    raeburn  12240:     my ($currdir,$docudom,$docuname,$depth,$count,$hierarchy,$dirorder,
                   12241:         $children,$parent,$titles,$wantform) = @_;
1.1055    raeburn  12242:     my $result='';
1.1056    raeburn  12243:     unless ((ref($depth)) && (ref($count)) && (ref($hierarchy) eq 'ARRAY') &&
                   12244:             (ref($children) eq 'HASH') && (ref($parent) eq 'HASH') &&
                   12245:             (ref($dirorder) eq 'HASH')) {
1.1055    raeburn  12246:         return $result;
                   12247:     }
                   12248:     my $dirptr = 16384;
                   12249:     my ($newdirlistref,$newlisterror) =
                   12250:         &Apache::lonnet::dirlist($currdir,$docudom,$docuname,1);
                   12251:     if (ref($newdirlistref) eq 'ARRAY') {
                   12252:         foreach my $dir_line (@{$newdirlistref}) {
                   12253:             my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
                   12254:             unless ($item =~ /^\.+$/) {
                   12255:                 $$count ++;
1.1056    raeburn  12256:                 @{$dirorder->{$$count}} = @{$hierarchy};
                   12257:                 $titles->{$$count} = $item;
1.1055    raeburn  12258:                 &archive_hierarchy($$depth,$$count,$parent,$children);
1.1056    raeburn  12259: 
1.1055    raeburn  12260:                 my $is_dir;
                   12261:                 if ($dirptr&$testdir) {
                   12262:                     $is_dir = 1;
                   12263:                 }
                   12264:                 if ($wantform) {
                   12265:                     $result .= &archive_row($is_dir,$item,$currdir,$$depth,$$count);
                   12266:                 }
                   12267:                 if ($is_dir) {
                   12268:                     $$depth ++;
1.1056    raeburn  12269:                     push(@{$hierarchy},$$count);
                   12270:                     $parent->{$$depth} = $$count;
1.1055    raeburn  12271:                     $result .=
                   12272:                         &recurse_extracted_archive("$currdir/$item",$docudom,
                   12273:                                                    $docuname,$depth,$count,
1.1056    raeburn  12274:                                                    $hierarchy,$dirorder,$children,
                   12275:                                                    $parent,$titles,$wantform);
1.1055    raeburn  12276:                     $$depth --;
1.1056    raeburn  12277:                     pop(@{$hierarchy});
1.1055    raeburn  12278:                 }
                   12279:             }
                   12280:         }
                   12281:     }
                   12282:     return $result;
                   12283: }
                   12284: 
                   12285: sub archive_hierarchy {
                   12286:     my ($depth,$count,$parent,$children) =@_;
                   12287:     if ((ref($parent) eq 'HASH') && (ref($children) eq 'HASH')) {
                   12288:         if (exists($parent->{$depth})) {
                   12289:              $children->{$parent->{$depth}} .= $count.':';
                   12290:         }
                   12291:     }
                   12292:     return;
                   12293: }
                   12294: 
                   12295: sub archive_row {
                   12296:     my ($is_dir,$item,$currdir,$depth,$count) = @_;
                   12297:     my ($name) = ($item =~ m{([^/]+)$});
                   12298:     my %choices = &Apache::lonlocal::texthash (
1.1059    raeburn  12299:                                        'display'    => 'Add as file',
1.1055    raeburn  12300:                                        'dependency' => 'Include as dependency',
                   12301:                                        'discard'    => 'Discard',
                   12302:                                       );
                   12303:     if ($is_dir) {
1.1059    raeburn  12304:         $choices{'display'} = &mt('Add as folder'); 
1.1055    raeburn  12305:     }
1.1056    raeburn  12306:     my $output = &start_data_table_row().'<td align="right">'.$count.'</td>'."\n";
                   12307:     my $offset = 0;
1.1055    raeburn  12308:     foreach my $action ('display','dependency','discard') {
1.1056    raeburn  12309:         $offset ++;
1.1065    raeburn  12310:         if ($action ne 'display') {
                   12311:             $offset ++;
                   12312:         }  
1.1055    raeburn  12313:         $output .= '<td><span class="LC_nobreak">'.
                   12314:                    '<label><input type="radio" name="archive_'.$count.
                   12315:                    '" id="archive_'.$action.'_'.$count.'" value="'.$action.'"';
                   12316:         my $text = $choices{$action};
                   12317:         if ($is_dir) {
                   12318:             $output .= ' onclick="javascript:propagateCheck(this.form,'."'$count'".');"';
                   12319:             if ($action eq 'display') {
1.1059    raeburn  12320:                 $text = &mt('Add as folder');
1.1055    raeburn  12321:             }
1.1056    raeburn  12322:         } else {
                   12323:             $output .= ' onclick="javascript:dependencyCheck(this.form,'."$count,$offset".');"';
                   12324: 
                   12325:         }
                   12326:         $output .= ' />&nbsp;'.$choices{$action}.'</label></span>';
                   12327:         if ($action eq 'dependency') {
                   12328:             $output .= '<div id="arc_depon_'.$count.'" style="display:none;">'."\n".
                   12329:                        &mt('Used by:').'&nbsp;<select name="archive_dependent_on_'.$count.'" '.
                   12330:                        'onchange="propagateSelect(this.form,'."$count,$offset".')">'."\n".
                   12331:                        '<option value=""></option>'."\n".
                   12332:                        '</select>'."\n".
                   12333:                        '</div>';
1.1059    raeburn  12334:         } elsif ($action eq 'display') {
                   12335:             $output .= '<div id="arc_title_'.$count.'" style="display:none;">'."\n".
                   12336:                        &mt('Title:').'&nbsp;<input type="text" name="archive_title_'.$count.'" id="archive_title_'.$count.'" />'."\n".
                   12337:                        '</div>';
1.1055    raeburn  12338:         }
1.1056    raeburn  12339:         $output .= '</td>';
1.1055    raeburn  12340:     }
                   12341:     $output .= '<td><input type="hidden" name="archive_content_'.$count.'" value="'.
                   12342:                &HTML::Entities::encode("$currdir/$item",'"<>&').'" />'.('&nbsp;' x 2);
                   12343:     for (my $i=0; $i<$depth; $i++) {
                   12344:         $output .= ('<img src="/adm/lonIcons/whitespace1.gif" class="LC_docs_spacer" alt="" />' x2)."\n";
                   12345:     }
                   12346:     if ($is_dir) {
                   12347:         $output .= '<img src="/adm/lonIcons/navmap.folder.open.gif" alt="" />&nbsp;'."\n".
                   12348:                    '<input type="hidden" name="archive_directory" value="'.$count.'" />'."\n";
                   12349:     } else {
                   12350:         $output .= '<input type="hidden" name="archive_file" value="'.$count.'" />'."\n";
                   12351:     }
                   12352:     $output .= '&nbsp;'.$name.'</td>'."\n".
                   12353:                &end_data_table_row();
                   12354:     return $output;
                   12355: }
                   12356: 
                   12357: sub archive_options_form {
1.1065    raeburn  12358:     my ($form,$display,$count,$hiddenelem) = @_;
                   12359:     my %lt = &Apache::lonlocal::texthash(
                   12360:                perm => 'Permanently remove archive file?',
                   12361:                hows => 'How should each extracted item be incorporated in the course?',
                   12362:                cont => 'Content actions for all',
                   12363:                addf => 'Add as folder/file',
                   12364:                incd => 'Include as dependency for a displayed file',
                   12365:                disc => 'Discard',
                   12366:                no   => 'No',
                   12367:                yes  => 'Yes',
                   12368:                save => 'Save',
                   12369:     );
                   12370:     my $output = <<"END";
                   12371: <form name="$form" method="post" action="">
                   12372: <p><span class="LC_nobreak">$lt{'perm'}&nbsp;
                   12373: <label>
                   12374:   <input type="radio" name="archivedelete" value="0" checked="checked" />$lt{'no'}
                   12375: </label>
                   12376: &nbsp;
                   12377: <label>
                   12378:   <input type="radio" name="archivedelete" value="1" />$lt{'yes'}</label>
                   12379: </span>
                   12380: </p>
                   12381: <input type="hidden" name="phase" value="decompress_cleanup" />
                   12382: <br />$lt{'hows'}
                   12383: <div class="LC_columnSection">
                   12384:   <fieldset>
                   12385:     <legend>$lt{'cont'}</legend>
                   12386:     <input type="button" value="$lt{'addf'}" onclick="javascript:checkAll(document.$form,'display');" /> 
                   12387:     &nbsp;&nbsp;<input type="button" value="$lt{'incd'}" onclick="javascript:checkAll(document.$form,'dependency');" />
                   12388:     &nbsp;&nbsp;<input type="button" value="$lt{'disc'}" onclick="javascript:checkAll(document.$form,'discard');" />
                   12389:   </fieldset>
                   12390: </div>
                   12391: END
                   12392:     return $output.
1.1055    raeburn  12393:            &start_data_table()."\n".
1.1065    raeburn  12394:            $display."\n".
1.1055    raeburn  12395:            &end_data_table()."\n".
                   12396:            '<input type="hidden" name="archive_count" value="'.$count.'" />'.
                   12397:            $hiddenelem.
1.1065    raeburn  12398:            '<br /><input type="submit" name="archive_submit" value="'.$lt{'save'}.'" />'.
1.1055    raeburn  12399:            '</form>';
                   12400: }
                   12401: 
                   12402: sub archive_javascript {
1.1056    raeburn  12403:     my ($startcount,$numitems,$titles,$children) = @_;
                   12404:     return unless ((ref($titles) eq 'HASH') && (ref($children) eq 'HASH'));
1.1059    raeburn  12405:     my $maintitle = $env{'form.comment'};
1.1055    raeburn  12406:     my $scripttag = <<START;
                   12407: <script type="text/javascript">
                   12408: // <![CDATA[
                   12409: 
                   12410: function checkAll(form,prefix) {
                   12411:     var idstr =  new RegExp("^archive_"+prefix+"_\\\\d+\$");
                   12412:     for (var i=0; i < form.elements.length; i++) {
                   12413:         var id = form.elements[i].id;
                   12414:         if ((id != '') && (id != undefined)) {
                   12415:             if (idstr.test(id)) {
                   12416:                 if (form.elements[i].type == 'radio') {
                   12417:                     form.elements[i].checked = true;
1.1056    raeburn  12418:                     var nostart = i-$startcount;
1.1059    raeburn  12419:                     var offset = nostart%7;
                   12420:                     var count = (nostart-offset)/7;    
1.1056    raeburn  12421:                     dependencyCheck(form,count,offset);
1.1055    raeburn  12422:                 }
                   12423:             }
                   12424:         }
                   12425:     }
                   12426: }
                   12427: 
                   12428: function propagateCheck(form,count) {
                   12429:     if (count > 0) {
1.1059    raeburn  12430:         var startelement = $startcount + ((count-1) * 7);
                   12431:         for (var j=1; j<6; j++) {
                   12432:             if ((j != 2) && (j != 4)) {
1.1056    raeburn  12433:                 var item = startelement + j; 
                   12434:                 if (form.elements[item].type == 'radio') {
                   12435:                     if (form.elements[item].checked) {
                   12436:                         containerCheck(form,count,j);
                   12437:                         break;
                   12438:                     }
1.1055    raeburn  12439:                 }
                   12440:             }
                   12441:         }
                   12442:     }
                   12443: }
                   12444: 
                   12445: numitems = $numitems
1.1056    raeburn  12446: var titles = new Array(numitems);
                   12447: var parents = new Array(numitems);
1.1055    raeburn  12448: for (var i=0; i<numitems; i++) {
1.1056    raeburn  12449:     parents[i] = new Array;
1.1055    raeburn  12450: }
1.1059    raeburn  12451: var maintitle = '$maintitle';
1.1055    raeburn  12452: 
                   12453: START
                   12454: 
1.1056    raeburn  12455:     foreach my $container (sort { $a <=> $b } (keys(%{$children}))) {
                   12456:         my @contents = split(/:/,$children->{$container});
1.1055    raeburn  12457:         for (my $i=0; $i<@contents; $i ++) {
                   12458:             $scripttag .= 'parents['.$container.']['.$i.'] = '.$contents[$i]."\n";
                   12459:         }
                   12460:     }
                   12461: 
1.1056    raeburn  12462:     foreach my $key (sort { $a <=> $b } (keys(%{$titles}))) {
                   12463:         $scripttag .= "titles[$key] = '".$titles->{$key}."';\n";
                   12464:     }
                   12465: 
1.1055    raeburn  12466:     $scripttag .= <<END;
                   12467: 
                   12468: function containerCheck(form,count,offset) {
                   12469:     if (count > 0) {
1.1056    raeburn  12470:         dependencyCheck(form,count,offset);
1.1059    raeburn  12471:         var item = (offset+$startcount)+7*(count-1);
1.1055    raeburn  12472:         form.elements[item].checked = true;
                   12473:         if(Object.prototype.toString.call(parents[count]) === '[object Array]') {
                   12474:             if (parents[count].length > 0) {
                   12475:                 for (var j=0; j<parents[count].length; j++) {
1.1056    raeburn  12476:                     containerCheck(form,parents[count][j],offset);
                   12477:                 }
                   12478:             }
                   12479:         }
                   12480:     }
                   12481: }
                   12482: 
                   12483: function dependencyCheck(form,count,offset) {
                   12484:     if (count > 0) {
1.1059    raeburn  12485:         var chosen = (offset+$startcount)+7*(count-1);
                   12486:         var depitem = $startcount + ((count-1) * 7) + 4;
1.1056    raeburn  12487:         var currtype = form.elements[depitem].type;
                   12488:         if (form.elements[chosen].value == 'dependency') {
                   12489:             document.getElementById('arc_depon_'+count).style.display='block'; 
                   12490:             form.elements[depitem].options.length = 0;
                   12491:             form.elements[depitem].options[0] = new Option('Select','',true,true);
1.1085    raeburn  12492:             for (var i=1; i<=numitems; i++) {
                   12493:                 if (i == count) {
                   12494:                     continue;
                   12495:                 }
1.1059    raeburn  12496:                 var startelement = $startcount + (i-1) * 7;
                   12497:                 for (var j=1; j<6; j++) {
                   12498:                     if ((j != 2) && (j!= 4)) {
1.1056    raeburn  12499:                         var item = startelement + j;
                   12500:                         if (form.elements[item].type == 'radio') {
                   12501:                             if (form.elements[item].checked) {
                   12502:                                 if (form.elements[item].value == 'display') {
                   12503:                                     var n = form.elements[depitem].options.length;
                   12504:                                     form.elements[depitem].options[n] = new Option(titles[i],i,false,false);
                   12505:                                 }
                   12506:                             }
                   12507:                         }
                   12508:                     }
                   12509:                 }
                   12510:             }
                   12511:         } else {
                   12512:             document.getElementById('arc_depon_'+count).style.display='none';
                   12513:             form.elements[depitem].options.length = 0;
                   12514:             form.elements[depitem].options[0] = new Option('Select','',true,true);
                   12515:         }
1.1059    raeburn  12516:         titleCheck(form,count,offset);
1.1056    raeburn  12517:     }
                   12518: }
                   12519: 
                   12520: function propagateSelect(form,count,offset) {
                   12521:     if (count > 0) {
1.1065    raeburn  12522:         var item = (1+offset+$startcount)+7*(count-1);
1.1056    raeburn  12523:         var picked = form.elements[item].options[form.elements[item].selectedIndex].value; 
                   12524:         if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
                   12525:             if (parents[count].length > 0) {
                   12526:                 for (var j=0; j<parents[count].length; j++) {
                   12527:                     containerSelect(form,parents[count][j],offset,picked);
1.1055    raeburn  12528:                 }
                   12529:             }
                   12530:         }
                   12531:     }
                   12532: }
1.1056    raeburn  12533: 
                   12534: function containerSelect(form,count,offset,picked) {
                   12535:     if (count > 0) {
1.1065    raeburn  12536:         var item = (offset+$startcount)+7*(count-1);
1.1056    raeburn  12537:         if (form.elements[item].type == 'radio') {
                   12538:             if (form.elements[item].value == 'dependency') {
                   12539:                 if (form.elements[item+1].type == 'select-one') {
                   12540:                     for (var i=0; i<form.elements[item+1].options.length; i++) {
                   12541:                         if (form.elements[item+1].options[i].value == picked) {
                   12542:                             form.elements[item+1].selectedIndex = i;
                   12543:                             break;
                   12544:                         }
                   12545:                     }
                   12546:                 }
                   12547:                 if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
                   12548:                     if (parents[count].length > 0) {
                   12549:                         for (var j=0; j<parents[count].length; j++) {
                   12550:                             containerSelect(form,parents[count][j],offset,picked);
                   12551:                         }
                   12552:                     }
                   12553:                 }
                   12554:             }
                   12555:         }
                   12556:     }
                   12557: }
                   12558: 
1.1059    raeburn  12559: function titleCheck(form,count,offset) {
                   12560:     if (count > 0) {
                   12561:         var chosen = (offset+$startcount)+7*(count-1);
                   12562:         var depitem = $startcount + ((count-1) * 7) + 2;
                   12563:         var currtype = form.elements[depitem].type;
                   12564:         if (form.elements[chosen].value == 'display') {
                   12565:             document.getElementById('arc_title_'+count).style.display='block';
                   12566:             if ((count==1) && ((parents[count].length > 0) || (numitems == 1))) {
                   12567:                 document.getElementById('archive_title_'+count).value=maintitle;
                   12568:             }
                   12569:         } else {
                   12570:             document.getElementById('arc_title_'+count).style.display='none';
                   12571:             if (currtype == 'text') { 
                   12572:                 document.getElementById('archive_title_'+count).value='';
                   12573:             }
                   12574:         }
                   12575:     }
                   12576:     return;
                   12577: }
                   12578: 
1.1055    raeburn  12579: // ]]>
                   12580: </script>
                   12581: END
                   12582:     return $scripttag;
                   12583: }
                   12584: 
                   12585: sub process_extracted_files {
1.1067    raeburn  12586:     my ($context,$docudom,$docuname,$destination,$dir_root,$hiddenelem) = @_;
1.1055    raeburn  12587:     my $numitems = $env{'form.archive_count'};
                   12588:     return unless ($numitems);
                   12589:     my @ids=&Apache::lonnet::current_machine_ids();
                   12590:     my ($prefix,$pathtocheck,$dir,$ishome,$error,$warning,%toplevelitems,%is_dir,
1.1067    raeburn  12591:         %folders,%containers,%mapinner,%prompttofetch);
1.1055    raeburn  12592:     my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
                   12593:     if (grep(/^\Q$docuhome\E$/,@ids)) {
                   12594:         $prefix = &LONCAPA::propath($docudom,$docuname);
                   12595:         $pathtocheck = "$dir_root/$destination";
                   12596:         $dir = $dir_root;
                   12597:         $ishome = 1;
                   12598:     } else {
                   12599:         $prefix = $Apache::lonnet::perlvar{'lonDocRoot'};
                   12600:         $pathtocheck = "$dir_root/$docudom/$docuname/$destination";
                   12601:         $dir = "$dir_root/$docudom/$docuname";    
                   12602:     }
                   12603:     my $currdir = "$dir_root/$destination";
                   12604:     (my $docstype,$mapinner{'0'}) = ($destination =~ m{^(docs|supplemental)/(\w+)/});
                   12605:     if ($env{'form.folderpath'}) {
                   12606:         my @items = split('&',$env{'form.folderpath'});
                   12607:         $folders{'0'} = $items[-2];
1.1099    raeburn  12608:         if ($env{'form.folderpath'} =~ /\:1$/) {
                   12609:             $containers{'0'}='page';
                   12610:         } else {  
                   12611:             $containers{'0'}='sequence';
                   12612:         }
1.1055    raeburn  12613:     }
                   12614:     my @archdirs = &get_env_multiple('form.archive_directory');
                   12615:     if ($numitems) {
                   12616:         for (my $i=1; $i<=$numitems; $i++) {
                   12617:             my $path = $env{'form.archive_content_'.$i};
                   12618:             if ($path =~ m{^\Q$pathtocheck\E/([^/]+)$}) {
                   12619:                 my $item = $1;
                   12620:                 $toplevelitems{$item} = $i;
                   12621:                 if (grep(/^\Q$i\E$/,@archdirs)) {
                   12622:                     $is_dir{$item} = 1;
                   12623:                 }
                   12624:             }
                   12625:         }
                   12626:     }
1.1067    raeburn  12627:     my ($output,%children,%parent,%titles,%dirorder,$result);
1.1055    raeburn  12628:     if (keys(%toplevelitems) > 0) {
                   12629:         my @contents = sort(keys(%toplevelitems));
1.1056    raeburn  12630:         (my $count,undef) = &get_extracted($docudom,$docuname,$currdir,\%is_dir,\%children,
                   12631:                                            \%parent,\@contents,\%dirorder,\%titles);
1.1055    raeburn  12632:     }
1.1066    raeburn  12633:     my (%referrer,%orphaned,%todelete,%todeletedir,%newdest,%newseqid);
1.1055    raeburn  12634:     if ($numitems) {
                   12635:         for (my $i=1; $i<=$numitems; $i++) {
1.1086    raeburn  12636:             next if ($env{'form.archive_'.$i} eq 'dependency');
1.1055    raeburn  12637:             my $path = $env{'form.archive_content_'.$i};
                   12638:             if ($path =~ /^\Q$pathtocheck\E/) {
                   12639:                 if ($env{'form.archive_'.$i} eq 'discard') {
                   12640:                     if ($prefix ne '' && $path ne '') {
                   12641:                         if (-e $prefix.$path) {
1.1066    raeburn  12642:                             if ((@archdirs > 0) && 
                   12643:                                 (grep(/^\Q$i\E$/,@archdirs))) {
                   12644:                                 $todeletedir{$prefix.$path} = 1;
                   12645:                             } else {
                   12646:                                 $todelete{$prefix.$path} = 1;
                   12647:                             }
1.1055    raeburn  12648:                         }
                   12649:                     }
                   12650:                 } elsif ($env{'form.archive_'.$i} eq 'display') {
1.1059    raeburn  12651:                     my ($docstitle,$title,$url,$outer);
1.1055    raeburn  12652:                     ($title) = ($path =~ m{/([^/]+)$});
1.1059    raeburn  12653:                     $docstitle = $env{'form.archive_title_'.$i};
                   12654:                     if ($docstitle eq '') {
                   12655:                         $docstitle = $title;
                   12656:                     }
1.1055    raeburn  12657:                     $outer = 0;
1.1056    raeburn  12658:                     if (ref($dirorder{$i}) eq 'ARRAY') {
                   12659:                         if (@{$dirorder{$i}} > 0) {
                   12660:                             foreach my $item (reverse(@{$dirorder{$i}})) {
1.1055    raeburn  12661:                                 if ($env{'form.archive_'.$item} eq 'display') {
                   12662:                                     $outer = $item;
                   12663:                                     last;
                   12664:                                 }
                   12665:                             }
                   12666:                         }
                   12667:                     }
                   12668:                     my ($errtext,$fatal) = 
                   12669:                         &LONCAPA::map::mapread('/uploaded/'.$docudom.'/'.$docuname.
                   12670:                                                '/'.$folders{$outer}.'.'.
                   12671:                                                $containers{$outer});
                   12672:                     next if ($fatal);
                   12673:                     if ((@archdirs > 0) && (grep(/^\Q$i\E$/,@archdirs))) {
                   12674:                         if ($context eq 'coursedocs') {
1.1056    raeburn  12675:                             $mapinner{$i} = time;
1.1055    raeburn  12676:                             $folders{$i} = 'default_'.$mapinner{$i};
                   12677:                             $containers{$i} = 'sequence';
                   12678:                             my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
                   12679:                                       $folders{$i}.'.'.$containers{$i};
                   12680:                             my $newidx = &LONCAPA::map::getresidx();
                   12681:                             $LONCAPA::map::resources[$newidx]=
1.1059    raeburn  12682:                                 $docstitle.':'.$url.':false:normal:res';
1.1055    raeburn  12683:                             push(@LONCAPA::map::order,$newidx);
                   12684:                             my ($outtext,$errtext) =
                   12685:                                 &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
                   12686:                                                         $docuname.'/'.$folders{$outer}.
1.1087    raeburn  12687:                                                         '.'.$containers{$outer},1,1);
1.1056    raeburn  12688:                             $newseqid{$i} = $newidx;
1.1067    raeburn  12689:                             unless ($errtext) {
                   12690:                                 $result .=  '<li>'.&mt('Folder: [_1] added to course',$docstitle).'</li>'."\n";
                   12691:                             }
1.1055    raeburn  12692:                         }
                   12693:                     } else {
                   12694:                         if ($context eq 'coursedocs') {
                   12695:                             my $newidx=&LONCAPA::map::getresidx();
                   12696:                             my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
                   12697:                                       $docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'.
                   12698:                                       $title;
                   12699:                             if (!-e "$prefix$dir/$docstype/$mapinner{$outer}") {
                   12700:                                 mkdir("$prefix$dir/$docstype/$mapinner{$outer}",0755);
                   12701:                             }
                   12702:                             if (!-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
                   12703:                                 mkdir("$prefix$dir/$docstype/$mapinner{$outer}/$newidx");
                   12704:                             }
                   12705:                             if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
                   12706:                                 system("mv $prefix$path $prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title");
1.1056    raeburn  12707:                                 $newdest{$i} = "$prefix$dir/$docstype/$mapinner{$outer}/$newidx";
1.1067    raeburn  12708:                                 unless ($ishome) {
                   12709:                                     my $fetch = "$newdest{$i}/$title";
                   12710:                                     $fetch =~ s/^\Q$prefix$dir\E//;
                   12711:                                     $prompttofetch{$fetch} = 1;
                   12712:                                 }
1.1055    raeburn  12713:                             }
                   12714:                             $LONCAPA::map::resources[$newidx]=
1.1059    raeburn  12715:                                 $docstitle.':'.$url.':false:normal:res';
1.1055    raeburn  12716:                             push(@LONCAPA::map::order, $newidx);
                   12717:                             my ($outtext,$errtext)=
                   12718:                                 &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
                   12719:                                                         $docuname.'/'.$folders{$outer}.
1.1087    raeburn  12720:                                                         '.'.$containers{$outer},1,1);
1.1067    raeburn  12721:                             unless ($errtext) {
                   12722:                                 if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title") {
                   12723:                                     $result .= '<li>'.&mt('File: [_1] added to course',$docstitle).'</li>'."\n";
                   12724:                                 }
                   12725:                             }
1.1055    raeburn  12726:                         }
                   12727:                     }
1.1086    raeburn  12728:                 }
                   12729:             } else {
                   12730:                 $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',$path).'<br />'; 
                   12731:             }
                   12732:         }
                   12733:         for (my $i=1; $i<=$numitems; $i++) {
                   12734:             next unless ($env{'form.archive_'.$i} eq 'dependency');
                   12735:             my $path = $env{'form.archive_content_'.$i};
                   12736:             if ($path =~ /^\Q$pathtocheck\E/) {
                   12737:                 my ($title) = ($path =~ m{/([^/]+)$});
                   12738:                 $referrer{$i} = $env{'form.archive_dependent_on_'.$i};
                   12739:                 if ($env{'form.archive_'.$referrer{$i}} eq 'display') {
                   12740:                     if (ref($dirorder{$i}) eq 'ARRAY') {
                   12741:                         my ($itemidx,$fullpath,$relpath);
                   12742:                         if (ref($dirorder{$referrer{$i}}) eq 'ARRAY') {
                   12743:                             my $container = $dirorder{$referrer{$i}}->[-1];
1.1056    raeburn  12744:                             for (my $j=0; $j<@{$dirorder{$i}}; $j++) {
1.1086    raeburn  12745:                                 if ($dirorder{$i}->[$j] eq $container) {
                   12746:                                     $itemidx = $j;
1.1056    raeburn  12747:                                 }
                   12748:                             }
1.1086    raeburn  12749:                         }
                   12750:                         if ($itemidx eq '') {
                   12751:                             $itemidx =  0;
                   12752:                         } 
                   12753:                         if (grep(/^\Q$referrer{$i}\E$/,@archdirs)) {
                   12754:                             if ($mapinner{$referrer{$i}}) {
                   12755:                                 $fullpath = "$prefix$dir/$docstype/$mapinner{$referrer{$i}}";
                   12756:                                 for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
                   12757:                                     if (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
                   12758:                                         unless (defined($newseqid{$dirorder{$i}->[$j]})) {
                   12759:                                             $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
                   12760:                                             $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
                   12761:                                             if (!-e $fullpath) {
                   12762:                                                 mkdir($fullpath,0755);
1.1056    raeburn  12763:                                             }
                   12764:                                         }
1.1086    raeburn  12765:                                     } else {
                   12766:                                         last;
1.1056    raeburn  12767:                                     }
1.1086    raeburn  12768:                                 }
                   12769:                             }
                   12770:                         } elsif ($newdest{$referrer{$i}}) {
                   12771:                             $fullpath = $newdest{$referrer{$i}};
                   12772:                             for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
                   12773:                                 if ($env{'form.archive_'.$dirorder{$i}->[$j]} eq 'discard') {
                   12774:                                     $orphaned{$i} = $env{'form.archive_'.$dirorder{$i}->[$j]};
                   12775:                                     last;
                   12776:                                 } elsif (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
                   12777:                                     unless (defined($newseqid{$dirorder{$i}->[$j]})) {
                   12778:                                         $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
                   12779:                                         $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
                   12780:                                         if (!-e $fullpath) {
                   12781:                                             mkdir($fullpath,0755);
1.1056    raeburn  12782:                                         }
                   12783:                                     }
1.1086    raeburn  12784:                                 } else {
                   12785:                                     last;
1.1056    raeburn  12786:                                 }
1.1055    raeburn  12787:                             }
                   12788:                         }
1.1086    raeburn  12789:                         if ($fullpath ne '') {
                   12790:                             if (-e "$prefix$path") {
                   12791:                                 system("mv $prefix$path $fullpath/$title");
                   12792:                             }
                   12793:                             if (-e "$fullpath/$title") {
                   12794:                                 my $showpath;
                   12795:                                 if ($relpath ne '') {
                   12796:                                     $showpath = "$relpath/$title";
                   12797:                                 } else {
                   12798:                                     $showpath = "/$title";
                   12799:                                 } 
                   12800:                                 $result .= '<li>'.&mt('[_1] included as a dependency',$showpath).'</li>'."\n";
                   12801:                             } 
                   12802:                             unless ($ishome) {
                   12803:                                 my $fetch = "$fullpath/$title";
                   12804:                                 $fetch =~ s/^\Q$prefix$dir\E//; 
                   12805:                                 $prompttofetch{$fetch} = 1;
                   12806:                             }
                   12807:                         }
1.1055    raeburn  12808:                     }
1.1086    raeburn  12809:                 } elsif ($env{'form.archive_'.$referrer{$i}} eq 'discard') {
                   12810:                     $warning .= &mt('[_1] is a dependency of [_2], which was discarded.',
                   12811:                                     $path,$env{'form.archive_content_'.$referrer{$i}}).'<br />';
1.1055    raeburn  12812:                 }
                   12813:             } else {
                   12814:                 $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',$path).'<br />'; 
                   12815:             }
                   12816:         }
                   12817:         if (keys(%todelete)) {
                   12818:             foreach my $key (keys(%todelete)) {
                   12819:                 unlink($key);
1.1066    raeburn  12820:             }
                   12821:         }
                   12822:         if (keys(%todeletedir)) {
                   12823:             foreach my $key (keys(%todeletedir)) {
                   12824:                 rmdir($key);
                   12825:             }
                   12826:         }
                   12827:         foreach my $dir (sort(keys(%is_dir))) {
                   12828:             if (($pathtocheck ne '') && ($dir ne ''))  {
                   12829:                 &cleanup_empty_dirs($prefix."$pathtocheck/$dir");
1.1055    raeburn  12830:             }
                   12831:         }
1.1067    raeburn  12832:         if ($result ne '') {
                   12833:             $output .= '<ul>'."\n".
                   12834:                        $result."\n".
                   12835:                        '</ul>';
                   12836:         }
                   12837:         unless ($ishome) {
                   12838:             my $replicationfail;
                   12839:             foreach my $item (keys(%prompttofetch)) {
                   12840:                 my $fetchresult= &Apache::lonnet::reply('fetchuserfile:'.$item,$docuhome);
                   12841:                 unless ($fetchresult eq 'ok') {
                   12842:                     $replicationfail .= '<li>'.$item.'</li>'."\n";
                   12843:                 }
                   12844:             }
                   12845:             if ($replicationfail) {
                   12846:                 $output .= '<p class="LC_error">'.
                   12847:                            &mt('Course home server failed to retrieve:').'<ul>'.
                   12848:                            $replicationfail.
                   12849:                            '</ul></p>';
                   12850:             }
                   12851:         }
1.1055    raeburn  12852:     } else {
                   12853:         $warning = &mt('No items found in archive.');
                   12854:     }
                   12855:     if ($error) {
                   12856:         $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
                   12857:                    $error.'</p>'."\n";
                   12858:     }
                   12859:     if ($warning) {
                   12860:         $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
                   12861:     }
                   12862:     return $output;
                   12863: }
                   12864: 
1.1066    raeburn  12865: sub cleanup_empty_dirs {
                   12866:     my ($path) = @_;
                   12867:     if (($path ne '') && (-d $path)) {
                   12868:         if (opendir(my $dirh,$path)) {
                   12869:             my @dircontents = grep(!/^\./,readdir($dirh));
                   12870:             my $numitems = 0;
                   12871:             foreach my $item (@dircontents) {
                   12872:                 if (-d "$path/$item") {
1.1111    raeburn  12873:                     &cleanup_empty_dirs("$path/$item");
1.1066    raeburn  12874:                     if (-e "$path/$item") {
                   12875:                         $numitems ++;
                   12876:                     }
                   12877:                 } else {
                   12878:                     $numitems ++;
                   12879:                 }
                   12880:             }
                   12881:             if ($numitems == 0) {
                   12882:                 rmdir($path);
                   12883:             }
                   12884:             closedir($dirh);
                   12885:         }
                   12886:     }
                   12887:     return;
                   12888: }
                   12889: 
1.41      ng       12890: =pod
1.45      matthew  12891: 
1.1162    raeburn  12892: =item * &get_folder_hierarchy()
1.1068    raeburn  12893: 
                   12894: Provides hierarchy of names of folders/sub-folders containing the current
                   12895: item,
                   12896: 
                   12897: Inputs: 3
                   12898:      - $navmap - navmaps object
                   12899: 
                   12900:      - $map - url for map (either the trigger itself, or map containing
                   12901:                            the resource, which is the trigger).
                   12902: 
                   12903:      - $showitem - 1 => show title for map itself; 0 => do not show.
                   12904: 
                   12905: Outputs: 1 @pathitems - array of folder/subfolder names.
                   12906: 
                   12907: =cut
                   12908: 
                   12909: sub get_folder_hierarchy {
                   12910:     my ($navmap,$map,$showitem) = @_;
                   12911:     my @pathitems;
                   12912:     if (ref($navmap)) {
                   12913:         my $mapres = $navmap->getResourceByUrl($map);
                   12914:         if (ref($mapres)) {
                   12915:             my $pcslist = $mapres->map_hierarchy();
                   12916:             if ($pcslist ne '') {
                   12917:                 my @pcs = split(/,/,$pcslist);
                   12918:                 foreach my $pc (@pcs) {
                   12919:                     if ($pc == 1) {
1.1129    raeburn  12920:                         push(@pathitems,&mt('Main Content'));
1.1068    raeburn  12921:                     } else {
                   12922:                         my $res = $navmap->getByMapPc($pc);
                   12923:                         if (ref($res)) {
                   12924:                             my $title = $res->compTitle();
                   12925:                             $title =~ s/\W+/_/g;
                   12926:                             if ($title ne '') {
                   12927:                                 push(@pathitems,$title);
                   12928:                             }
                   12929:                         }
                   12930:                     }
                   12931:                 }
                   12932:             }
1.1071    raeburn  12933:             if ($showitem) {
                   12934:                 if ($mapres->{ID} eq '0.0') {
1.1129    raeburn  12935:                     push(@pathitems,&mt('Main Content'));
1.1071    raeburn  12936:                 } else {
                   12937:                     my $maptitle = $mapres->compTitle();
                   12938:                     $maptitle =~ s/\W+/_/g;
                   12939:                     if ($maptitle ne '') {
                   12940:                         push(@pathitems,$maptitle);
                   12941:                     }
1.1068    raeburn  12942:                 }
                   12943:             }
                   12944:         }
                   12945:     }
                   12946:     return @pathitems;
                   12947: }
                   12948: 
                   12949: =pod
                   12950: 
1.1015    raeburn  12951: =item * &get_turnedin_filepath()
                   12952: 
                   12953: Determines path in a user's portfolio file for storage of files uploaded
                   12954: to a specific essayresponse or dropbox item.
                   12955: 
                   12956: Inputs: 3 required + 1 optional.
                   12957: $symb is symb for resource, $uname and $udom are for current user (required).
                   12958: $caller is optional (can be "submission", if routine is called when storing
                   12959: an upoaded file when "Submit Answer" button was pressed).
                   12960: 
                   12961: Returns array containing $path and $multiresp. 
                   12962: $path is path in portfolio.  $multiresp is 1 if this resource contains more
                   12963: than one file upload item.  Callers of routine should append partid as a 
                   12964: subdirectory to $path in cases where $multiresp is 1.
                   12965: 
                   12966: Called by: homework/essayresponse.pm and homework/structuretags.pm
                   12967: 
                   12968: =cut
                   12969: 
                   12970: sub get_turnedin_filepath {
                   12971:     my ($symb,$uname,$udom,$caller) = @_;
                   12972:     my ($map,$resid,$resurl)=&Apache::lonnet::decode_symb($symb);
                   12973:     my $turnindir;
                   12974:     my %userhash = &Apache::lonnet::userenvironment($udom,$uname,'turnindir');
                   12975:     $turnindir = $userhash{'turnindir'};
                   12976:     my ($path,$multiresp);
                   12977:     if ($turnindir eq '') {
                   12978:         if ($caller eq 'submission') {
                   12979:             $turnindir = &mt('turned in');
                   12980:             $turnindir =~ s/\W+/_/g;
                   12981:             my %newhash = (
                   12982:                             'turnindir' => $turnindir,
                   12983:                           );
                   12984:             &Apache::lonnet::put('environment',\%newhash,$udom,$uname);
                   12985:         }
                   12986:     }
                   12987:     if ($turnindir ne '') {
                   12988:         $path = '/'.$turnindir.'/';
                   12989:         my ($multipart,$turnin,@pathitems);
                   12990:         my $navmap = Apache::lonnavmaps::navmap->new();
                   12991:         if (defined($navmap)) {
                   12992:             my $mapres = $navmap->getResourceByUrl($map);
                   12993:             if (ref($mapres)) {
                   12994:                 my $pcslist = $mapres->map_hierarchy();
                   12995:                 if ($pcslist ne '') {
                   12996:                     foreach my $pc (split(/,/,$pcslist)) {
                   12997:                         my $res = $navmap->getByMapPc($pc);
                   12998:                         if (ref($res)) {
                   12999:                             my $title = $res->compTitle();
                   13000:                             $title =~ s/\W+/_/g;
                   13001:                             if ($title ne '') {
1.1149    raeburn  13002:                                 if (($pc > 1) && (length($title) > 12)) {
                   13003:                                     $title = substr($title,0,12);
                   13004:                                 }
1.1015    raeburn  13005:                                 push(@pathitems,$title);
                   13006:                             }
                   13007:                         }
                   13008:                     }
                   13009:                 }
                   13010:                 my $maptitle = $mapres->compTitle();
                   13011:                 $maptitle =~ s/\W+/_/g;
                   13012:                 if ($maptitle ne '') {
1.1149    raeburn  13013:                     if (length($maptitle) > 12) {
                   13014:                         $maptitle = substr($maptitle,0,12);
                   13015:                     }
1.1015    raeburn  13016:                     push(@pathitems,$maptitle);
                   13017:                 }
                   13018:                 unless ($env{'request.state'} eq 'construct') {
                   13019:                     my $res = $navmap->getBySymb($symb);
                   13020:                     if (ref($res)) {
                   13021:                         my $partlist = $res->parts();
                   13022:                         my $totaluploads = 0;
                   13023:                         if (ref($partlist) eq 'ARRAY') {
                   13024:                             foreach my $part (@{$partlist}) {
                   13025:                                 my @types = $res->responseType($part);
                   13026:                                 my @ids = $res->responseIds($part);
                   13027:                                 for (my $i=0; $i < scalar(@ids); $i++) {
                   13028:                                     if ($types[$i] eq 'essay') {
                   13029:                                         my $partid = $part.'_'.$ids[$i];
                   13030:                                         if (&Apache::lonnet::EXT("resource.$partid.uploadedfiletypes") ne '') {
                   13031:                                             $totaluploads ++;
                   13032:                                         }
                   13033:                                     }
                   13034:                                 }
                   13035:                             }
                   13036:                             if ($totaluploads > 1) {
                   13037:                                 $multiresp = 1;
                   13038:                             }
                   13039:                         }
                   13040:                     }
                   13041:                 }
                   13042:             } else {
                   13043:                 return;
                   13044:             }
                   13045:         } else {
                   13046:             return;
                   13047:         }
                   13048:         my $restitle=&Apache::lonnet::gettitle($symb);
                   13049:         $restitle =~ s/\W+/_/g;
                   13050:         if ($restitle eq '') {
                   13051:             $restitle = ($resurl =~ m{/[^/]+$});
                   13052:             if ($restitle eq '') {
                   13053:                 $restitle = time;
                   13054:             }
                   13055:         }
1.1149    raeburn  13056:         if (length($restitle) > 12) {
                   13057:             $restitle = substr($restitle,0,12);
                   13058:         }
1.1015    raeburn  13059:         push(@pathitems,$restitle);
                   13060:         $path .= join('/',@pathitems);
                   13061:     }
                   13062:     return ($path,$multiresp);
                   13063: }
                   13064: 
                   13065: =pod
                   13066: 
1.464     albertel 13067: =back
1.41      ng       13068: 
1.112     bowersj2 13069: =head1 CSV Upload/Handling functions
1.38      albertel 13070: 
1.41      ng       13071: =over 4
                   13072: 
1.648     raeburn  13073: =item * &upfile_store($r)
1.41      ng       13074: 
                   13075: Store uploaded file, $r should be the HTTP Request object,
1.258     albertel 13076: needs $env{'form.upfile'}
1.41      ng       13077: returns $datatoken to be put into hidden field
                   13078: 
                   13079: =cut
1.31      albertel 13080: 
                   13081: sub upfile_store {
                   13082:     my $r=shift;
1.258     albertel 13083:     $env{'form.upfile'}=~s/\r/\n/gs;
                   13084:     $env{'form.upfile'}=~s/\f/\n/gs;
                   13085:     $env{'form.upfile'}=~s/\n+/\n/gs;
                   13086:     $env{'form.upfile'}=~s/\n+$//gs;
1.31      albertel 13087: 
1.258     albertel 13088:     my $datatoken=$env{'user.name'}.'_'.$env{'user.domain'}.
                   13089: 	'_enroll_'.$env{'request.course.id'}.'_'.time.'_'.$$;
1.31      albertel 13090:     {
1.158     raeburn  13091:         my $datafile = $r->dir_config('lonDaemons').
                   13092:                            '/tmp/'.$datatoken.'.tmp';
                   13093:         if ( open(my $fh,">$datafile") ) {
1.258     albertel 13094:             print $fh $env{'form.upfile'};
1.158     raeburn  13095:             close($fh);
                   13096:         }
1.31      albertel 13097:     }
                   13098:     return $datatoken;
                   13099: }
                   13100: 
1.56      matthew  13101: =pod
                   13102: 
1.648     raeburn  13103: =item * &load_tmp_file($r)
1.41      ng       13104: 
                   13105: Load uploaded file from tmp, $r should be the HTTP Request object,
1.258     albertel 13106: needs $env{'form.datatoken'},
                   13107: sets $env{'form.upfile'} to the contents of the file
1.41      ng       13108: 
                   13109: =cut
1.31      albertel 13110: 
                   13111: sub load_tmp_file {
                   13112:     my $r=shift;
                   13113:     my @studentdata=();
                   13114:     {
1.158     raeburn  13115:         my $studentfile = $r->dir_config('lonDaemons').
1.258     albertel 13116:                               '/tmp/'.$env{'form.datatoken'}.'.tmp';
1.158     raeburn  13117:         if ( open(my $fh,"<$studentfile") ) {
                   13118:             @studentdata=<$fh>;
                   13119:             close($fh);
                   13120:         }
1.31      albertel 13121:     }
1.258     albertel 13122:     $env{'form.upfile'}=join('',@studentdata);
1.31      albertel 13123: }
                   13124: 
1.56      matthew  13125: =pod
                   13126: 
1.648     raeburn  13127: =item * &upfile_record_sep()
1.41      ng       13128: 
                   13129: Separate uploaded file into records
                   13130: returns array of records,
1.258     albertel 13131: needs $env{'form.upfile'} and $env{'form.upfiletype'}
1.41      ng       13132: 
                   13133: =cut
1.31      albertel 13134: 
                   13135: sub upfile_record_sep {
1.258     albertel 13136:     if ($env{'form.upfiletype'} eq 'xml') {
1.31      albertel 13137:     } else {
1.248     albertel 13138: 	my @records;
1.258     albertel 13139: 	foreach my $line (split(/\n/,$env{'form.upfile'})) {
1.248     albertel 13140: 	    if ($line=~/^\s*$/) { next; }
                   13141: 	    push(@records,$line);
                   13142: 	}
                   13143: 	return @records;
1.31      albertel 13144:     }
                   13145: }
                   13146: 
1.56      matthew  13147: =pod
                   13148: 
1.648     raeburn  13149: =item * &record_sep($record)
1.41      ng       13150: 
1.258     albertel 13151: Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'}
1.41      ng       13152: 
                   13153: =cut
                   13154: 
1.263     www      13155: sub takeleft {
                   13156:     my $index=shift;
                   13157:     return substr('0000'.$index,-4,4);
                   13158: }
                   13159: 
1.31      albertel 13160: sub record_sep {
                   13161:     my $record=shift;
                   13162:     my %components=();
1.258     albertel 13163:     if ($env{'form.upfiletype'} eq 'xml') {
                   13164:     } elsif ($env{'form.upfiletype'} eq 'space') {
1.31      albertel 13165:         my $i=0;
1.356     albertel 13166:         foreach my $field (split(/\s+/,$record)) {
1.31      albertel 13167:             $field=~s/^(\"|\')//;
                   13168:             $field=~s/(\"|\')$//;
1.263     www      13169:             $components{&takeleft($i)}=$field;
1.31      albertel 13170:             $i++;
                   13171:         }
1.258     albertel 13172:     } elsif ($env{'form.upfiletype'} eq 'tab') {
1.31      albertel 13173:         my $i=0;
1.356     albertel 13174:         foreach my $field (split(/\t/,$record)) {
1.31      albertel 13175:             $field=~s/^(\"|\')//;
                   13176:             $field=~s/(\"|\')$//;
1.263     www      13177:             $components{&takeleft($i)}=$field;
1.31      albertel 13178:             $i++;
                   13179:         }
                   13180:     } else {
1.561     www      13181:         my $separator=',';
1.480     banghart 13182:         if ($env{'form.upfiletype'} eq 'semisv') {
1.561     www      13183:             $separator=';';
1.480     banghart 13184:         }
1.31      albertel 13185:         my $i=0;
1.561     www      13186: # the character we are looking for to indicate the end of a quote or a record 
                   13187:         my $looking_for=$separator;
                   13188: # do not add the characters to the fields
                   13189:         my $ignore=0;
                   13190: # we just encountered a separator (or the beginning of the record)
                   13191:         my $just_found_separator=1;
                   13192: # store the field we are working on here
                   13193:         my $field='';
                   13194: # work our way through all characters in record
                   13195:         foreach my $character ($record=~/(.)/g) {
                   13196:             if ($character eq $looking_for) {
                   13197:                if ($character ne $separator) {
                   13198: # Found the end of a quote, again looking for separator
                   13199:                   $looking_for=$separator;
                   13200:                   $ignore=1;
                   13201:                } else {
                   13202: # Found a separator, store away what we got
                   13203:                   $components{&takeleft($i)}=$field;
                   13204: 	          $i++;
                   13205:                   $just_found_separator=1;
                   13206:                   $ignore=0;
                   13207:                   $field='';
                   13208:                }
                   13209:                next;
                   13210:             }
                   13211: # single or double quotation marks after a separator indicate beginning of a quote
                   13212: # we are now looking for the end of the quote and need to ignore separators
                   13213:             if ((($character eq '"') || ($character eq "'")) && ($just_found_separator))  {
                   13214:                $looking_for=$character;
                   13215:                next;
                   13216:             }
                   13217: # ignore would be true after we reached the end of a quote
                   13218:             if ($ignore) { next; }
                   13219:             if (($just_found_separator) && ($character=~/\s/)) { next; }
                   13220:             $field.=$character;
                   13221:             $just_found_separator=0; 
1.31      albertel 13222:         }
1.561     www      13223: # catch the very last entry, since we never encountered the separator
                   13224:         $components{&takeleft($i)}=$field;
1.31      albertel 13225:     }
                   13226:     return %components;
                   13227: }
                   13228: 
1.144     matthew  13229: ######################################################
                   13230: ######################################################
                   13231: 
1.56      matthew  13232: =pod
                   13233: 
1.648     raeburn  13234: =item * &upfile_select_html()
1.41      ng       13235: 
1.144     matthew  13236: Return HTML code to select a file from the users machine and specify 
                   13237: the file type.
1.41      ng       13238: 
                   13239: =cut
                   13240: 
1.144     matthew  13241: ######################################################
                   13242: ######################################################
1.31      albertel 13243: sub upfile_select_html {
1.144     matthew  13244:     my %Types = (
                   13245:                  csv   => &mt('CSV (comma separated values, spreadsheet)'),
1.480     banghart 13246:                  semisv => &mt('Semicolon separated values'),
1.144     matthew  13247:                  space => &mt('Space separated'),
                   13248:                  tab   => &mt('Tabulator separated'),
                   13249: #                 xml   => &mt('HTML/XML'),
                   13250:                  );
                   13251:     my $Str = '<input type="file" name="upfile" size="50" />'.
1.727     riegler  13252:         '<br />'.&mt('Type').': <select name="upfiletype">';
1.144     matthew  13253:     foreach my $type (sort(keys(%Types))) {
                   13254:         $Str .= '<option value="'.$type.'" >'.$Types{$type}."</option>\n";
                   13255:     }
                   13256:     $Str .= "</select>\n";
                   13257:     return $Str;
1.31      albertel 13258: }
                   13259: 
1.301     albertel 13260: sub get_samples {
                   13261:     my ($records,$toget) = @_;
                   13262:     my @samples=({});
                   13263:     my $got=0;
                   13264:     foreach my $rec (@$records) {
                   13265: 	my %temp = &record_sep($rec);
                   13266: 	if (! grep(/\S/, values(%temp))) { next; }
                   13267: 	if (%temp) {
                   13268: 	    $samples[$got]=\%temp;
                   13269: 	    $got++;
                   13270: 	    if ($got == $toget) { last; }
                   13271: 	}
                   13272:     }
                   13273:     return \@samples;
                   13274: }
                   13275: 
1.144     matthew  13276: ######################################################
                   13277: ######################################################
                   13278: 
1.56      matthew  13279: =pod
                   13280: 
1.648     raeburn  13281: =item * &csv_print_samples($r,$records)
1.41      ng       13282: 
                   13283: Prints a table of sample values from each column uploaded $r is an
                   13284: Apache Request ref, $records is an arrayref from
                   13285: &Apache::loncommon::upfile_record_sep
                   13286: 
                   13287: =cut
                   13288: 
1.144     matthew  13289: ######################################################
                   13290: ######################################################
1.31      albertel 13291: sub csv_print_samples {
                   13292:     my ($r,$records) = @_;
1.662     bisitz   13293:     my $samples = &get_samples($records,5);
1.301     albertel 13294: 
1.594     raeburn  13295:     $r->print(&mt('Samples').'<br />'.&start_data_table().
                   13296:               &start_data_table_header_row());
1.356     albertel 13297:     foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) { 
1.845     bisitz   13298:         $r->print('<th>'.&mt('Column [_1]',($sample+1)).'</th>'); }
1.594     raeburn  13299:     $r->print(&end_data_table_header_row());
1.301     albertel 13300:     foreach my $hash (@$samples) {
1.594     raeburn  13301: 	$r->print(&start_data_table_row());
1.356     albertel 13302: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.31      albertel 13303: 	    $r->print('<td>');
1.356     albertel 13304: 	    if (defined($$hash{$sample})) { $r->print($$hash{$sample}); }
1.31      albertel 13305: 	    $r->print('</td>');
                   13306: 	}
1.594     raeburn  13307: 	$r->print(&end_data_table_row());
1.31      albertel 13308:     }
1.594     raeburn  13309:     $r->print(&end_data_table().'<br />'."\n");
1.31      albertel 13310: }
                   13311: 
1.144     matthew  13312: ######################################################
                   13313: ######################################################
                   13314: 
1.56      matthew  13315: =pod
                   13316: 
1.648     raeburn  13317: =item * &csv_print_select_table($r,$records,$d)
1.41      ng       13318: 
                   13319: Prints a table to create associations between values and table columns.
1.144     matthew  13320: 
1.41      ng       13321: $r is an Apache Request ref,
                   13322: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
1.174     matthew  13323: $d is an array of 2 element arrays (internal name, displayed name,defaultcol)
1.41      ng       13324: 
                   13325: =cut
                   13326: 
1.144     matthew  13327: ######################################################
                   13328: ######################################################
1.31      albertel 13329: sub csv_print_select_table {
                   13330:     my ($r,$records,$d) = @_;
1.301     albertel 13331:     my $i=0;
                   13332:     my $samples = &get_samples($records,1);
1.144     matthew  13333:     $r->print(&mt('Associate columns with student attributes.')."\n".
1.594     raeburn  13334: 	      &start_data_table().&start_data_table_header_row().
1.144     matthew  13335:               '<th>'.&mt('Attribute').'</th>'.
1.594     raeburn  13336:               '<th>'.&mt('Column').'</th>'.
                   13337:               &end_data_table_header_row()."\n");
1.356     albertel 13338:     foreach my $array_ref (@$d) {
                   13339: 	my ($value,$display,$defaultcol)=@{ $array_ref };
1.729     raeburn  13340: 	$r->print(&start_data_table_row().'<td>'.$display.'</td>');
1.31      albertel 13341: 
1.875     bisitz   13342: 	$r->print('<td><select name="f'.$i.'"'.
1.32      matthew  13343: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.31      albertel 13344: 	$r->print('<option value="none"></option>');
1.356     albertel 13345: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
                   13346: 	    $r->print('<option value="'.$sample.'"'.
                   13347:                       ($sample eq $defaultcol ? ' selected="selected" ' : '').
1.662     bisitz   13348:                       '>'.&mt('Column [_1]',($sample+1)).'</option>');
1.31      albertel 13349: 	}
1.594     raeburn  13350: 	$r->print('</select></td>'.&end_data_table_row()."\n");
1.31      albertel 13351: 	$i++;
                   13352:     }
1.594     raeburn  13353:     $r->print(&end_data_table());
1.31      albertel 13354:     $i--;
                   13355:     return $i;
                   13356: }
1.56      matthew  13357: 
1.144     matthew  13358: ######################################################
                   13359: ######################################################
                   13360: 
1.56      matthew  13361: =pod
1.31      albertel 13362: 
1.648     raeburn  13363: =item * &csv_samples_select_table($r,$records,$d)
1.41      ng       13364: 
                   13365: Prints a table of sample values from the upload and can make associate samples to internal names.
                   13366: 
                   13367: $r is an Apache Request ref,
                   13368: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
                   13369: $d is an array of 2 element arrays (internal name, displayed name)
                   13370: 
                   13371: =cut
                   13372: 
1.144     matthew  13373: ######################################################
                   13374: ######################################################
1.31      albertel 13375: sub csv_samples_select_table {
                   13376:     my ($r,$records,$d) = @_;
                   13377:     my $i=0;
1.144     matthew  13378:     #
1.662     bisitz   13379:     my $max_samples = 5;
                   13380:     my $samples = &get_samples($records,$max_samples);
1.594     raeburn  13381:     $r->print(&start_data_table().
                   13382:               &start_data_table_header_row().'<th>'.
                   13383:               &mt('Field').'</th><th>'.&mt('Samples').'</th>'.
                   13384:               &end_data_table_header_row());
1.301     albertel 13385: 
                   13386:     foreach my $key (sort(keys(%{ $samples->[0] }))) {
1.594     raeburn  13387: 	$r->print(&start_data_table_row().'<td><select name="f'.$i.'"'.
1.32      matthew  13388: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.301     albertel 13389: 	foreach my $option (@$d) {
                   13390: 	    my ($value,$display,$defaultcol)=@{ $option };
1.174     matthew  13391: 	    $r->print('<option value="'.$value.'"'.
1.253     albertel 13392:                       ($i eq $defaultcol ? ' selected="selected" ':'').'>'.
1.174     matthew  13393:                       $display.'</option>');
1.31      albertel 13394: 	}
                   13395: 	$r->print('</select></td><td>');
1.662     bisitz   13396: 	foreach my $line (0..($max_samples-1)) {
1.301     albertel 13397: 	    if (defined($samples->[$line]{$key})) { 
                   13398: 		$r->print($samples->[$line]{$key}."<br />\n"); 
                   13399: 	    }
                   13400: 	}
1.594     raeburn  13401: 	$r->print('</td>'.&end_data_table_row());
1.31      albertel 13402: 	$i++;
                   13403:     }
1.594     raeburn  13404:     $r->print(&end_data_table());
1.31      albertel 13405:     $i--;
                   13406:     return($i);
1.115     matthew  13407: }
                   13408: 
1.144     matthew  13409: ######################################################
                   13410: ######################################################
                   13411: 
1.115     matthew  13412: =pod
                   13413: 
1.648     raeburn  13414: =item * &clean_excel_name($name)
1.115     matthew  13415: 
                   13416: Returns a replacement for $name which does not contain any illegal characters.
                   13417: 
                   13418: =cut
                   13419: 
1.144     matthew  13420: ######################################################
                   13421: ######################################################
1.115     matthew  13422: sub clean_excel_name {
                   13423:     my ($name) = @_;
                   13424:     $name =~ s/[:\*\?\/\\]//g;
                   13425:     if (length($name) > 31) {
                   13426:         $name = substr($name,0,31);
                   13427:     }
                   13428:     return $name;
1.25      albertel 13429: }
1.84      albertel 13430: 
1.85      albertel 13431: =pod
                   13432: 
1.648     raeburn  13433: =item * &check_if_partid_hidden($id,$symb,$udom,$uname)
1.85      albertel 13434: 
                   13435: Returns either 1 or undef
                   13436: 
                   13437: 1 if the part is to be hidden, undef if it is to be shown
                   13438: 
                   13439: Arguments are:
                   13440: 
                   13441: $id the id of the part to be checked
                   13442: $symb, optional the symb of the resource to check
                   13443: $udom, optional the domain of the user to check for
                   13444: $uname, optional the username of the user to check for
                   13445: 
                   13446: =cut
1.84      albertel 13447: 
                   13448: sub check_if_partid_hidden {
                   13449:     my ($id,$symb,$udom,$uname) = @_;
1.133     albertel 13450:     my $hiddenparts=&Apache::lonnet::EXT('resource.0.hiddenparts',
1.84      albertel 13451: 					 $symb,$udom,$uname);
1.141     albertel 13452:     my $truth=1;
                   13453:     #if the string starts with !, then the list is the list to show not hide
                   13454:     if ($hiddenparts=~s/^\s*!//) { $truth=undef; }
1.84      albertel 13455:     my @hiddenlist=split(/,/,$hiddenparts);
                   13456:     foreach my $checkid (@hiddenlist) {
1.141     albertel 13457: 	if ($checkid =~ /^\s*\Q$id\E\s*$/) { return $truth; }
1.84      albertel 13458:     }
1.141     albertel 13459:     return !$truth;
1.84      albertel 13460: }
1.127     matthew  13461: 
1.138     matthew  13462: 
                   13463: ############################################################
                   13464: ############################################################
                   13465: 
                   13466: =pod
                   13467: 
1.157     matthew  13468: =back 
                   13469: 
1.138     matthew  13470: =head1 cgi-bin script and graphing routines
                   13471: 
1.157     matthew  13472: =over 4
                   13473: 
1.648     raeburn  13474: =item * &get_cgi_id()
1.138     matthew  13475: 
                   13476: Inputs: none
                   13477: 
                   13478: Returns an id which can be used to pass environment variables
                   13479: to various cgi-bin scripts.  These environment variables will
                   13480: be removed from the users environment after a given time by
                   13481: the routine &Apache::lonnet::transfer_profile_to_env.
                   13482: 
                   13483: =cut
                   13484: 
                   13485: ############################################################
                   13486: ############################################################
1.152     albertel 13487: my $uniq=0;
1.136     matthew  13488: sub get_cgi_id {
1.154     albertel 13489:     $uniq=($uniq+1)%100000;
1.280     albertel 13490:     return (time.'_'.$$.'_'.$uniq);
1.136     matthew  13491: }
                   13492: 
1.127     matthew  13493: ############################################################
                   13494: ############################################################
                   13495: 
                   13496: =pod
                   13497: 
1.648     raeburn  13498: =item * &DrawBarGraph()
1.127     matthew  13499: 
1.138     matthew  13500: Facilitates the plotting of data in a (stacked) bar graph.
                   13501: Puts plot definition data into the users environment in order for 
                   13502: graph.png to plot it.  Returns an <img> tag for the plot.
                   13503: The bars on the plot are labeled '1','2',...,'n'.
                   13504: 
                   13505: Inputs:
                   13506: 
                   13507: =over 4
                   13508: 
                   13509: =item $Title: string, the title of the plot
                   13510: 
                   13511: =item $xlabel: string, text describing the X-axis of the plot
                   13512: 
                   13513: =item $ylabel: string, text describing the Y-axis of the plot
                   13514: 
                   13515: =item $Max: scalar, the maximum Y value to use in the plot
                   13516: If $Max is < any data point, the graph will not be rendered.
                   13517: 
1.140     matthew  13518: =item $colors: array ref holding the colors to be used for the data sets when
1.138     matthew  13519: they are plotted.  If undefined, default values will be used.
                   13520: 
1.178     matthew  13521: =item $labels: array ref holding the labels to use on the x-axis for the bars.
                   13522: 
1.138     matthew  13523: =item @Values: An array of array references.  Each array reference holds data
                   13524: to be plotted in a stacked bar chart.
                   13525: 
1.239     matthew  13526: =item If the final element of @Values is a hash reference the key/value
                   13527: pairs will be added to the graph definition.
                   13528: 
1.138     matthew  13529: =back
                   13530: 
                   13531: Returns:
                   13532: 
                   13533: An <img> tag which references graph.png and the appropriate identifying
                   13534: information for the plot.
                   13535: 
1.127     matthew  13536: =cut
                   13537: 
                   13538: ############################################################
                   13539: ############################################################
1.134     matthew  13540: sub DrawBarGraph {
1.178     matthew  13541:     my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_;
1.134     matthew  13542:     #
                   13543:     if (! defined($colors)) {
                   13544:         $colors = ['#33ff00', 
                   13545:                   '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
                   13546:                   '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
                   13547:                   ]; 
                   13548:     }
1.228     matthew  13549:     my $extra_settings = {};
                   13550:     if (ref($Values[-1]) eq 'HASH') {
                   13551:         $extra_settings = pop(@Values);
                   13552:     }
1.127     matthew  13553:     #
1.136     matthew  13554:     my $identifier = &get_cgi_id();
                   13555:     my $id = 'cgi.'.$identifier;        
1.129     matthew  13556:     if (! @Values || ref($Values[0]) ne 'ARRAY') {
1.127     matthew  13557:         return '';
                   13558:     }
1.225     matthew  13559:     #
                   13560:     my @Labels;
                   13561:     if (defined($labels)) {
                   13562:         @Labels = @$labels;
                   13563:     } else {
                   13564:         for (my $i=0;$i<@{$Values[0]};$i++) {
                   13565:             push (@Labels,$i+1);
                   13566:         }
                   13567:     }
                   13568:     #
1.129     matthew  13569:     my $NumBars = scalar(@{$Values[0]});
1.225     matthew  13570:     if ($NumBars < scalar(@Labels)) { $NumBars = scalar(@Labels); }
1.129     matthew  13571:     my %ValuesHash;
                   13572:     my $NumSets=1;
                   13573:     foreach my $array (@Values) {
                   13574:         next if (! ref($array));
1.136     matthew  13575:         $ValuesHash{$id.'.data.'.$NumSets++} = 
1.132     matthew  13576:             join(',',@$array);
1.129     matthew  13577:     }
1.127     matthew  13578:     #
1.136     matthew  13579:     my ($height,$width,$xskip,$bar_width) = (200,120,1,15);
1.225     matthew  13580:     if ($NumBars < 3) {
                   13581:         $width = 120+$NumBars*32;
1.220     matthew  13582:         $xskip = 1;
1.225     matthew  13583:         $bar_width = 30;
                   13584:     } elsif ($NumBars < 5) {
                   13585:         $width = 120+$NumBars*20;
                   13586:         $xskip = 1;
                   13587:         $bar_width = 20;
1.220     matthew  13588:     } elsif ($NumBars < 10) {
1.136     matthew  13589:         $width = 120+$NumBars*15;
                   13590:         $xskip = 1;
                   13591:         $bar_width = 15;
                   13592:     } elsif ($NumBars <= 25) {
                   13593:         $width = 120+$NumBars*11;
                   13594:         $xskip = 5;
                   13595:         $bar_width = 8;
                   13596:     } elsif ($NumBars <= 50) {
                   13597:         $width = 120+$NumBars*8;
                   13598:         $xskip = 5;
                   13599:         $bar_width = 4;
                   13600:     } else {
                   13601:         $width = 120+$NumBars*8;
                   13602:         $xskip = 5;
                   13603:         $bar_width = 4;
                   13604:     }
                   13605:     #
1.137     matthew  13606:     $Max = 1 if ($Max < 1);
                   13607:     if ( int($Max) < $Max ) {
                   13608:         $Max++;
                   13609:         $Max = int($Max);
                   13610:     }
1.127     matthew  13611:     $Title  = '' if (! defined($Title));
                   13612:     $xlabel = '' if (! defined($xlabel));
                   13613:     $ylabel = '' if (! defined($ylabel));
1.369     www      13614:     $ValuesHash{$id.'.title'}    = &escape($Title);
                   13615:     $ValuesHash{$id.'.xlabel'}   = &escape($xlabel);
                   13616:     $ValuesHash{$id.'.ylabel'}   = &escape($ylabel);
1.137     matthew  13617:     $ValuesHash{$id.'.y_max_value'} = $Max;
1.136     matthew  13618:     $ValuesHash{$id.'.NumBars'}  = $NumBars;
                   13619:     $ValuesHash{$id.'.NumSets'}  = $NumSets;
                   13620:     $ValuesHash{$id.'.PlotType'} = 'bar';
                   13621:     $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   13622:     $ValuesHash{$id.'.height'}   = $height;
                   13623:     $ValuesHash{$id.'.width'}    = $width;
                   13624:     $ValuesHash{$id.'.xskip'}    = $xskip;
                   13625:     $ValuesHash{$id.'.bar_width'} = $bar_width;
                   13626:     $ValuesHash{$id.'.labels'} = join(',',@Labels);
1.127     matthew  13627:     #
1.228     matthew  13628:     # Deal with other parameters
                   13629:     while (my ($key,$value) = each(%$extra_settings)) {
                   13630:         $ValuesHash{$id.'.'.$key} = $value;
                   13631:     }
                   13632:     #
1.646     raeburn  13633:     &Apache::lonnet::appenv(\%ValuesHash);
1.137     matthew  13634:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   13635: }
                   13636: 
                   13637: ############################################################
                   13638: ############################################################
                   13639: 
                   13640: =pod
                   13641: 
1.648     raeburn  13642: =item * &DrawXYGraph()
1.137     matthew  13643: 
1.138     matthew  13644: Facilitates the plotting of data in an XY graph.
                   13645: Puts plot definition data into the users environment in order for 
                   13646: graph.png to plot it.  Returns an <img> tag for the plot.
                   13647: 
                   13648: Inputs:
                   13649: 
                   13650: =over 4
                   13651: 
                   13652: =item $Title: string, the title of the plot
                   13653: 
                   13654: =item $xlabel: string, text describing the X-axis of the plot
                   13655: 
                   13656: =item $ylabel: string, text describing the Y-axis of the plot
                   13657: 
                   13658: =item $Max: scalar, the maximum Y value to use in the plot
                   13659: If $Max is < any data point, the graph will not be rendered.
                   13660: 
                   13661: =item $colors: Array ref containing the hex color codes for the data to be 
                   13662: plotted in.  If undefined, default values will be used.
                   13663: 
                   13664: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   13665: 
                   13666: =item $Ydata: Array ref containing Array refs.  
1.185     www      13667: Each of the contained arrays will be plotted as a separate curve.
1.138     matthew  13668: 
                   13669: =item %Values: hash indicating or overriding any default values which are 
                   13670: passed to graph.png.  
                   13671: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   13672: 
                   13673: =back
                   13674: 
                   13675: Returns:
                   13676: 
                   13677: An <img> tag which references graph.png and the appropriate identifying
                   13678: information for the plot.
                   13679: 
1.137     matthew  13680: =cut
                   13681: 
                   13682: ############################################################
                   13683: ############################################################
                   13684: sub DrawXYGraph {
                   13685:     my ($Title,$xlabel,$ylabel,$Max,$colors,$Xlabels,$Ydata,%Values)=@_;
                   13686:     #
                   13687:     # Create the identifier for the graph
                   13688:     my $identifier = &get_cgi_id();
                   13689:     my $id = 'cgi.'.$identifier;
                   13690:     #
                   13691:     $Title  = '' if (! defined($Title));
                   13692:     $xlabel = '' if (! defined($xlabel));
                   13693:     $ylabel = '' if (! defined($ylabel));
                   13694:     my %ValuesHash = 
                   13695:         (
1.369     www      13696:          $id.'.title'  => &escape($Title),
                   13697:          $id.'.xlabel' => &escape($xlabel),
                   13698:          $id.'.ylabel' => &escape($ylabel),
1.137     matthew  13699:          $id.'.y_max_value'=> $Max,
                   13700:          $id.'.labels'     => join(',',@$Xlabels),
                   13701:          $id.'.PlotType'   => 'XY',
                   13702:          );
                   13703:     #
                   13704:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   13705:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   13706:     }
                   13707:     #
                   13708:     if (! ref($Ydata) || ref($Ydata) ne 'ARRAY') {
                   13709:         return '';
                   13710:     }
                   13711:     my $NumSets=1;
1.138     matthew  13712:     foreach my $array (@{$Ydata}){
1.137     matthew  13713:         next if (! ref($array));
                   13714:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
                   13715:     }
1.138     matthew  13716:     $ValuesHash{$id.'.NumSets'} = $NumSets-1;
1.137     matthew  13717:     #
                   13718:     # Deal with other parameters
                   13719:     while (my ($key,$value) = each(%Values)) {
                   13720:         $ValuesHash{$id.'.'.$key} = $value;
1.127     matthew  13721:     }
                   13722:     #
1.646     raeburn  13723:     &Apache::lonnet::appenv(\%ValuesHash);
1.136     matthew  13724:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   13725: }
                   13726: 
                   13727: ############################################################
                   13728: ############################################################
                   13729: 
                   13730: =pod
                   13731: 
1.648     raeburn  13732: =item * &DrawXYYGraph()
1.138     matthew  13733: 
                   13734: Facilitates the plotting of data in an XY graph with two Y axes.
                   13735: Puts plot definition data into the users environment in order for 
                   13736: graph.png to plot it.  Returns an <img> tag for the plot.
                   13737: 
                   13738: Inputs:
                   13739: 
                   13740: =over 4
                   13741: 
                   13742: =item $Title: string, the title of the plot
                   13743: 
                   13744: =item $xlabel: string, text describing the X-axis of the plot
                   13745: 
                   13746: =item $ylabel: string, text describing the Y-axis of the plot
                   13747: 
                   13748: =item $colors: Array ref containing the hex color codes for the data to be 
                   13749: plotted in.  If undefined, default values will be used.
                   13750: 
                   13751: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   13752: 
                   13753: =item $Ydata1: The first data set
                   13754: 
                   13755: =item $Min1: The minimum value of the left Y-axis
                   13756: 
                   13757: =item $Max1: The maximum value of the left Y-axis
                   13758: 
                   13759: =item $Ydata2: The second data set
                   13760: 
                   13761: =item $Min2: The minimum value of the right Y-axis
                   13762: 
                   13763: =item $Max2: The maximum value of the left Y-axis
                   13764: 
                   13765: =item %Values: hash indicating or overriding any default values which are 
                   13766: passed to graph.png.  
                   13767: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   13768: 
                   13769: =back
                   13770: 
                   13771: Returns:
                   13772: 
                   13773: An <img> tag which references graph.png and the appropriate identifying
                   13774: information for the plot.
1.136     matthew  13775: 
                   13776: =cut
                   13777: 
                   13778: ############################################################
                   13779: ############################################################
1.137     matthew  13780: sub DrawXYYGraph {
                   13781:     my ($Title,$xlabel,$ylabel,$colors,$Xlabels,$Ydata1,$Min1,$Max1,
                   13782:                                         $Ydata2,$Min2,$Max2,%Values)=@_;
1.136     matthew  13783:     #
                   13784:     # Create the identifier for the graph
                   13785:     my $identifier = &get_cgi_id();
                   13786:     my $id = 'cgi.'.$identifier;
                   13787:     #
                   13788:     $Title  = '' if (! defined($Title));
                   13789:     $xlabel = '' if (! defined($xlabel));
                   13790:     $ylabel = '' if (! defined($ylabel));
                   13791:     my %ValuesHash = 
                   13792:         (
1.369     www      13793:          $id.'.title'  => &escape($Title),
                   13794:          $id.'.xlabel' => &escape($xlabel),
                   13795:          $id.'.ylabel' => &escape($ylabel),
1.136     matthew  13796:          $id.'.labels' => join(',',@$Xlabels),
                   13797:          $id.'.PlotType' => 'XY',
                   13798:          $id.'.NumSets' => 2,
1.137     matthew  13799:          $id.'.two_axes' => 1,
                   13800:          $id.'.y1_max_value' => $Max1,
                   13801:          $id.'.y1_min_value' => $Min1,
                   13802:          $id.'.y2_max_value' => $Max2,
                   13803:          $id.'.y2_min_value' => $Min2,
1.136     matthew  13804:          );
                   13805:     #
1.137     matthew  13806:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   13807:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   13808:     }
                   13809:     #
                   13810:     if (! ref($Ydata1) || ref($Ydata1) ne 'ARRAY' ||
                   13811:         ! ref($Ydata2) || ref($Ydata2) ne 'ARRAY'){
1.136     matthew  13812:         return '';
                   13813:     }
                   13814:     my $NumSets=1;
1.137     matthew  13815:     foreach my $array ($Ydata1,$Ydata2){
1.136     matthew  13816:         next if (! ref($array));
                   13817:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
1.137     matthew  13818:     }
                   13819:     #
                   13820:     # Deal with other parameters
                   13821:     while (my ($key,$value) = each(%Values)) {
                   13822:         $ValuesHash{$id.'.'.$key} = $value;
1.136     matthew  13823:     }
                   13824:     #
1.646     raeburn  13825:     &Apache::lonnet::appenv(\%ValuesHash);
1.130     albertel 13826:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
1.139     matthew  13827: }
                   13828: 
                   13829: ############################################################
                   13830: ############################################################
                   13831: 
                   13832: =pod
                   13833: 
1.157     matthew  13834: =back 
                   13835: 
1.139     matthew  13836: =head1 Statistics helper routines?  
                   13837: 
                   13838: Bad place for them but what the hell.
                   13839: 
1.157     matthew  13840: =over 4
                   13841: 
1.648     raeburn  13842: =item * &chartlink()
1.139     matthew  13843: 
                   13844: Returns a link to the chart for a specific student.  
                   13845: 
                   13846: Inputs:
                   13847: 
                   13848: =over 4
                   13849: 
                   13850: =item $linktext: The text of the link
                   13851: 
                   13852: =item $sname: The students username
                   13853: 
                   13854: =item $sdomain: The students domain
                   13855: 
                   13856: =back
                   13857: 
1.157     matthew  13858: =back
                   13859: 
1.139     matthew  13860: =cut
                   13861: 
                   13862: ############################################################
                   13863: ############################################################
                   13864: sub chartlink {
                   13865:     my ($linktext, $sname, $sdomain) = @_;
                   13866:     my $link = '<a href="/adm/statistics?reportSelected=student_assessment'.
1.369     www      13867:         '&amp;SelectedStudent='.&escape($sname.':'.$sdomain).
1.219     albertel 13868:         '&amp;chartoutputmode='.HTML::Entities::encode('html, with all links').
1.139     matthew  13869:        '">'.$linktext.'</a>';
1.153     matthew  13870: }
                   13871: 
                   13872: #######################################################
                   13873: #######################################################
                   13874: 
                   13875: =pod
                   13876: 
                   13877: =head1 Course Environment Routines
1.157     matthew  13878: 
                   13879: =over 4
1.153     matthew  13880: 
1.648     raeburn  13881: =item * &restore_course_settings()
1.153     matthew  13882: 
1.648     raeburn  13883: =item * &store_course_settings()
1.153     matthew  13884: 
                   13885: Restores/Store indicated form parameters from the course environment.
                   13886: Will not overwrite existing values of the form parameters.
                   13887: 
                   13888: Inputs: 
                   13889: a scalar describing the data (e.g. 'chart', 'problem_analysis')
                   13890: 
                   13891: a hash ref describing the data to be stored.  For example:
                   13892:    
                   13893: %Save_Parameters = ('Status' => 'scalar',
                   13894:     'chartoutputmode' => 'scalar',
                   13895:     'chartoutputdata' => 'scalar',
                   13896:     'Section' => 'array',
1.373     raeburn  13897:     'Group' => 'array',
1.153     matthew  13898:     'StudentData' => 'array',
                   13899:     'Maps' => 'array');
                   13900: 
                   13901: Returns: both routines return nothing
                   13902: 
1.631     raeburn  13903: =back
                   13904: 
1.153     matthew  13905: =cut
                   13906: 
                   13907: #######################################################
                   13908: #######################################################
                   13909: sub store_course_settings {
1.496     albertel 13910:     return &store_settings($env{'request.course.id'},@_);
                   13911: }
                   13912: 
                   13913: sub store_settings {
1.153     matthew  13914:     # save to the environment
                   13915:     # appenv the same items, just to be safe
1.300     albertel 13916:     my $udom  = $env{'user.domain'};
                   13917:     my $uname = $env{'user.name'};
1.496     albertel 13918:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  13919:     my %SaveHash;
                   13920:     my %AppHash;
                   13921:     while (my ($setting,$type) = each(%$Settings)) {
1.496     albertel 13922:         my $basename = join('.','internal',$context,$prefix,$setting);
1.300     albertel 13923:         my $envname = 'environment.'.$basename;
1.258     albertel 13924:         if (exists($env{'form.'.$setting})) {
1.153     matthew  13925:             # Save this value away
                   13926:             if ($type eq 'scalar' &&
1.258     albertel 13927:                 (! exists($env{$envname}) || 
                   13928:                  $env{$envname} ne $env{'form.'.$setting})) {
                   13929:                 $SaveHash{$basename} = $env{'form.'.$setting};
                   13930:                 $AppHash{$envname}   = $env{'form.'.$setting};
1.153     matthew  13931:             } elsif ($type eq 'array') {
                   13932:                 my $stored_form;
1.258     albertel 13933:                 if (ref($env{'form.'.$setting})) {
1.153     matthew  13934:                     $stored_form = join(',',
                   13935:                                         map {
1.369     www      13936:                                             &escape($_);
1.258     albertel 13937:                                         } sort(@{$env{'form.'.$setting}}));
1.153     matthew  13938:                 } else {
                   13939:                     $stored_form = 
1.369     www      13940:                         &escape($env{'form.'.$setting});
1.153     matthew  13941:                 }
                   13942:                 # Determine if the array contents are the same.
1.258     albertel 13943:                 if ($stored_form ne $env{$envname}) {
1.153     matthew  13944:                     $SaveHash{$basename} = $stored_form;
                   13945:                     $AppHash{$envname}   = $stored_form;
                   13946:                 }
                   13947:             }
                   13948:         }
                   13949:     }
                   13950:     my $put_result = &Apache::lonnet::put('environment',\%SaveHash,
1.300     albertel 13951:                                           $udom,$uname);
1.153     matthew  13952:     if ($put_result !~ /^(ok|delayed)/) {
                   13953:         &Apache::lonnet::logthis('unable to save form parameters, '.
                   13954:                                  'got error:'.$put_result);
                   13955:     }
                   13956:     # Make sure these settings stick around in this session, too
1.646     raeburn  13957:     &Apache::lonnet::appenv(\%AppHash);
1.153     matthew  13958:     return;
                   13959: }
                   13960: 
                   13961: sub restore_course_settings {
1.499     albertel 13962:     return &restore_settings($env{'request.course.id'},@_);
1.496     albertel 13963: }
                   13964: 
                   13965: sub restore_settings {
                   13966:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  13967:     while (my ($setting,$type) = each(%$Settings)) {
1.258     albertel 13968:         next if (exists($env{'form.'.$setting}));
1.496     albertel 13969:         my $envname = 'environment.internal.'.$context.'.'.$prefix.
1.153     matthew  13970:             '.'.$setting;
1.258     albertel 13971:         if (exists($env{$envname})) {
1.153     matthew  13972:             if ($type eq 'scalar') {
1.258     albertel 13973:                 $env{'form.'.$setting} = $env{$envname};
1.153     matthew  13974:             } elsif ($type eq 'array') {
1.258     albertel 13975:                 $env{'form.'.$setting} = [ 
1.153     matthew  13976:                                            map { 
1.369     www      13977:                                                &unescape($_); 
1.258     albertel 13978:                                            } split(',',$env{$envname})
1.153     matthew  13979:                                            ];
                   13980:             }
                   13981:         }
                   13982:     }
1.127     matthew  13983: }
                   13984: 
1.618     raeburn  13985: #######################################################
                   13986: #######################################################
                   13987: 
                   13988: =pod
                   13989: 
                   13990: =head1 Domain E-mail Routines  
                   13991: 
                   13992: =over 4
                   13993: 
1.648     raeburn  13994: =item * &build_recipient_list()
1.618     raeburn  13995: 
1.1144    raeburn  13996: Build recipient lists for following types of e-mail:
1.766     raeburn  13997: (a) Error Reports, (b) Package Updates, (c) lonstatus warnings/errors
1.1144    raeburn  13998: (d) Help requests, (e) Course requests needing approval, (f) loncapa
                   13999: module change checking, student/employee ID conflict checks, as
                   14000: generated by lonerrorhandler.pm, CHECKRPMS, loncron,
                   14001: lonsupportreq.pm, loncoursequeueadmin.pm, searchcat.pl respectively.
1.618     raeburn  14002: 
                   14003: Inputs:
1.619     raeburn  14004: defmail (scalar - email address of default recipient), 
1.1144    raeburn  14005: mailing type (scalar: errormail, packagesmail, helpdeskmail,
                   14006: requestsmail, updatesmail, or idconflictsmail).
                   14007: 
1.619     raeburn  14008: defdom (domain for which to retrieve configuration settings),
1.1144    raeburn  14009: 
1.619     raeburn  14010: origmail (scalar - email address of recipient from loncapa.conf, 
                   14011: i.e., predates configuration by DC via domainprefs.pm 
1.618     raeburn  14012: 
1.655     raeburn  14013: Returns: comma separated list of addresses to which to send e-mail.
                   14014: 
                   14015: =back
1.618     raeburn  14016: 
                   14017: =cut
                   14018: 
                   14019: ############################################################
                   14020: ############################################################
                   14021: sub build_recipient_list {
1.619     raeburn  14022:     my ($defmail,$mailing,$defdom,$origmail) = @_;
1.618     raeburn  14023:     my @recipients;
                   14024:     my $otheremails;
                   14025:     my %domconfig =
                   14026:          &Apache::lonnet::get_dom('configuration',['contacts'],$defdom);
                   14027:     if (ref($domconfig{'contacts'}) eq 'HASH') {
1.766     raeburn  14028:         if (exists($domconfig{'contacts'}{$mailing})) {
                   14029:             if (ref($domconfig{'contacts'}{$mailing}) eq 'HASH') {
                   14030:                 my @contacts = ('adminemail','supportemail');
                   14031:                 foreach my $item (@contacts) {
                   14032:                     if ($domconfig{'contacts'}{$mailing}{$item}) {
                   14033:                         my $addr = $domconfig{'contacts'}{$item}; 
                   14034:                         if (!grep(/^\Q$addr\E$/,@recipients)) {
                   14035:                             push(@recipients,$addr);
                   14036:                         }
1.619     raeburn  14037:                     }
1.766     raeburn  14038:                     $otheremails = $domconfig{'contacts'}{$mailing}{'others'};
1.618     raeburn  14039:                 }
                   14040:             }
1.766     raeburn  14041:         } elsif ($origmail ne '') {
                   14042:             push(@recipients,$origmail);
1.618     raeburn  14043:         }
1.619     raeburn  14044:     } elsif ($origmail ne '') {
                   14045:         push(@recipients,$origmail);
1.618     raeburn  14046:     }
1.688     raeburn  14047:     if (defined($defmail)) {
                   14048:         if ($defmail ne '') {
                   14049:             push(@recipients,$defmail);
                   14050:         }
1.618     raeburn  14051:     }
                   14052:     if ($otheremails) {
1.619     raeburn  14053:         my @others;
                   14054:         if ($otheremails =~ /,/) {
                   14055:             @others = split(/,/,$otheremails);
1.618     raeburn  14056:         } else {
1.619     raeburn  14057:             push(@others,$otheremails);
                   14058:         }
                   14059:         foreach my $addr (@others) {
                   14060:             if (!grep(/^\Q$addr\E$/,@recipients)) {
                   14061:                 push(@recipients,$addr);
                   14062:             }
1.618     raeburn  14063:         }
                   14064:     }
1.619     raeburn  14065:     my $recipientlist = join(',',@recipients); 
1.618     raeburn  14066:     return $recipientlist;
                   14067: }
                   14068: 
1.127     matthew  14069: ############################################################
                   14070: ############################################################
1.154     albertel 14071: 
1.655     raeburn  14072: =pod
                   14073: 
1.1224    musolffc 14074: =over 4
                   14075: 
1.1223    musolffc 14076: =item * &mime_email()
                   14077: 
                   14078: Sends an email with a possible attachment
                   14079: 
                   14080: Inputs:
                   14081: 
                   14082: =over 4
                   14083: 
                   14084: from -              Sender's email address
                   14085: 
                   14086: to -                Email address of recipient
                   14087: 
                   14088: subject -           Subject of email
                   14089: 
                   14090: body -              Body of email
                   14091: 
                   14092: cc_string -         Carbon copy email address
                   14093: 
                   14094: bcc -               Blind carbon copy email address
                   14095: 
                   14096: type -              File type of attachment
                   14097: 
                   14098: attachment_path -   Path of file to be attached
                   14099: 
                   14100: file_name -         Name of file to be attached
                   14101: 
                   14102: attachment_text -   The body of an attachment of type "TEXT"
                   14103: 
                   14104: =back
                   14105: 
                   14106: =back
                   14107: 
                   14108: =cut
                   14109: 
                   14110: ############################################################
                   14111: ############################################################
                   14112: 
                   14113: sub mime_email {
                   14114:     my ($from, $to, $subject, $body, $cc_string, $bcc, $attachment_path, 
                   14115:         $file_name, $attachment_text) = @_;
                   14116:     my $msg = MIME::Lite->new(
                   14117:              From    => $from,
                   14118:              To      => $to,
                   14119:              Subject => $subject,
                   14120:              Type    =>'TEXT',
                   14121:              Data    => $body,
                   14122:              );
                   14123:     if ($cc_string ne '') {
                   14124:         $msg->add("Cc" => $cc_string);
                   14125:     }
                   14126:     if ($bcc ne '') {
                   14127:         $msg->add("Bcc" => $bcc);
                   14128:     }
                   14129:     $msg->attr("content-type"         => "text/plain");
                   14130:     $msg->attr("content-type.charset" => "UTF-8");
                   14131:     # Attach file if given
                   14132:     if ($attachment_path) {
                   14133:         unless ($file_name) {
                   14134:             if ($attachment_path =~ m-/([^/]+)$-) { $file_name = $1; }
                   14135:         }
                   14136:         my ($type, $encoding) = MIME::Types::by_suffix($attachment_path);
                   14137:         $msg->attach(Type     => $type,
                   14138:                      Path     => $attachment_path,
                   14139:                      Filename => $file_name
                   14140:                      );
                   14141:     # Otherwise attach text if given
                   14142:     } elsif ($attachment_text) {
                   14143:         $msg->attach(Type => 'TEXT',
                   14144:                      Data => $attachment_text);
                   14145:     }
                   14146:     # Send it
                   14147:     $msg->send('sendmail');
                   14148: }
                   14149: 
                   14150: ############################################################
                   14151: ############################################################
                   14152: 
                   14153: =pod
                   14154: 
1.655     raeburn  14155: =head1 Course Catalog Routines
                   14156: 
                   14157: =over 4
                   14158: 
                   14159: =item * &gather_categories()
                   14160: 
                   14161: Converts category definitions - keys of categories hash stored in  
                   14162: coursecategories in configuration.db on the primary library server in a 
                   14163: domain - to an array.  Also generates javascript and idx hash used to 
                   14164: generate Domain Coordinator interface for editing Course Categories.
                   14165: 
                   14166: Inputs:
1.663     raeburn  14167: 
1.655     raeburn  14168: categories (reference to hash of category definitions).
1.663     raeburn  14169: 
1.655     raeburn  14170: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   14171:       categories and subcategories).
1.663     raeburn  14172: 
1.655     raeburn  14173: idx (reference to hash of counters used in Domain Coordinator interface for 
                   14174:       editing Course Categories).
1.663     raeburn  14175: 
1.655     raeburn  14176: jsarray (reference to array of categories used to create Javascript arrays for
                   14177:          Domain Coordinator interface for editing Course Categories).
                   14178: 
                   14179: Returns: nothing
                   14180: 
                   14181: Side effects: populates cats, idx and jsarray. 
                   14182: 
                   14183: =cut
                   14184: 
                   14185: sub gather_categories {
                   14186:     my ($categories,$cats,$idx,$jsarray) = @_;
                   14187:     my %counters;
                   14188:     my $num = 0;
                   14189:     foreach my $item (keys(%{$categories})) {
                   14190:         my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item);
                   14191:         if ($container eq '' && $depth == 0) {
                   14192:             $cats->[$depth][$categories->{$item}] = $cat;
                   14193:         } else {
                   14194:             $cats->[$depth]{$container}[$categories->{$item}] = $cat;
                   14195:         }
                   14196:         my ($escitem,$tail) = split(/:/,$item,2);
                   14197:         if ($counters{$tail} eq '') {
                   14198:             $counters{$tail} = $num;
                   14199:             $num ++;
                   14200:         }
                   14201:         if (ref($idx) eq 'HASH') {
                   14202:             $idx->{$item} = $counters{$tail};
                   14203:         }
                   14204:         if (ref($jsarray) eq 'ARRAY') {
                   14205:             push(@{$jsarray->[$counters{$tail}]},$item);
                   14206:         }
                   14207:     }
                   14208:     return;
                   14209: }
                   14210: 
                   14211: =pod
                   14212: 
                   14213: =item * &extract_categories()
                   14214: 
                   14215: Used to generate breadcrumb trails for course categories.
                   14216: 
                   14217: Inputs:
1.663     raeburn  14218: 
1.655     raeburn  14219: categories (reference to hash of category definitions).
1.663     raeburn  14220: 
1.655     raeburn  14221: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   14222:       categories and subcategories).
1.663     raeburn  14223: 
1.655     raeburn  14224: trails (reference to array of breacrumb trails for each category).
1.663     raeburn  14225: 
1.655     raeburn  14226: allitems (reference to hash - key is category key 
                   14227:          (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663     raeburn  14228: 
1.655     raeburn  14229: idx (reference to hash of counters used in Domain Coordinator interface for
                   14230:       editing Course Categories).
1.663     raeburn  14231: 
1.655     raeburn  14232: jsarray (reference to array of categories used to create Javascript arrays for
                   14233:          Domain Coordinator interface for editing Course Categories).
                   14234: 
1.665     raeburn  14235: subcats (reference to hash of arrays containing all subcategories within each 
                   14236:          category, -recursive)
                   14237: 
1.655     raeburn  14238: Returns: nothing
                   14239: 
                   14240: Side effects: populates trails and allitems hash references.
                   14241: 
                   14242: =cut
                   14243: 
                   14244: sub extract_categories {
1.665     raeburn  14245:     my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats) = @_;
1.655     raeburn  14246:     if (ref($categories) eq 'HASH') {
                   14247:         &gather_categories($categories,$cats,$idx,$jsarray);
                   14248:         if (ref($cats->[0]) eq 'ARRAY') {
                   14249:             for (my $i=0; $i<@{$cats->[0]}; $i++) {
                   14250:                 my $name = $cats->[0][$i];
                   14251:                 my $item = &escape($name).'::0';
                   14252:                 my $trailstr;
                   14253:                 if ($name eq 'instcode') {
                   14254:                     $trailstr = &mt('Official courses (with institutional codes)');
1.919     raeburn  14255:                 } elsif ($name eq 'communities') {
                   14256:                     $trailstr = &mt('Communities');
1.655     raeburn  14257:                 } else {
                   14258:                     $trailstr = $name;
                   14259:                 }
                   14260:                 if ($allitems->{$item} eq '') {
                   14261:                     push(@{$trails},$trailstr);
                   14262:                     $allitems->{$item} = scalar(@{$trails})-1;
                   14263:                 }
                   14264:                 my @parents = ($name);
                   14265:                 if (ref($cats->[1]{$name}) eq 'ARRAY') {
                   14266:                     for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) {
                   14267:                         my $category = $cats->[1]{$name}[$j];
1.665     raeburn  14268:                         if (ref($subcats) eq 'HASH') {
                   14269:                             push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1');
                   14270:                         }
                   14271:                         &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats);
                   14272:                     }
                   14273:                 } else {
                   14274:                     if (ref($subcats) eq 'HASH') {
                   14275:                         $subcats->{$item} = [];
1.655     raeburn  14276:                     }
                   14277:                 }
                   14278:             }
                   14279:         }
                   14280:     }
                   14281:     return;
                   14282: }
                   14283: 
                   14284: =pod
                   14285: 
1.1162    raeburn  14286: =item * &recurse_categories()
1.655     raeburn  14287: 
                   14288: Recursively used to generate breadcrumb trails for course categories.
                   14289: 
                   14290: Inputs:
1.663     raeburn  14291: 
1.655     raeburn  14292: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   14293:       categories and subcategories).
1.663     raeburn  14294: 
1.655     raeburn  14295: depth (current depth in hierarchy of categories and sub-categories - 0 indexed).
1.663     raeburn  14296: 
                   14297: category (current course category, for which breadcrumb trail is being generated).
                   14298: 
                   14299: trails (reference to array of breadcrumb trails for each category).
                   14300: 
1.655     raeburn  14301: allitems (reference to hash - key is category key
                   14302:          (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663     raeburn  14303: 
1.655     raeburn  14304: parents (array containing containers directories for current category, 
                   14305:          back to top level). 
                   14306: 
                   14307: Returns: nothing
                   14308: 
                   14309: Side effects: populates trails and allitems hash references
                   14310: 
                   14311: =cut
                   14312: 
                   14313: sub recurse_categories {
1.665     raeburn  14314:     my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats) = @_;
1.655     raeburn  14315:     my $shallower = $depth - 1;
                   14316:     if (ref($cats->[$depth]{$category}) eq 'ARRAY') {
                   14317:         for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
                   14318:             my $name = $cats->[$depth]{$category}[$k];
                   14319:             my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
                   14320:             my $trailstr = join(' -&gt; ',(@{$parents},$category));
                   14321:             if ($allitems->{$item} eq '') {
                   14322:                 push(@{$trails},$trailstr);
                   14323:                 $allitems->{$item} = scalar(@{$trails})-1;
                   14324:             }
                   14325:             my $deeper = $depth+1;
                   14326:             push(@{$parents},$category);
1.665     raeburn  14327:             if (ref($subcats) eq 'HASH') {
                   14328:                 my $subcat = &escape($name).':'.$category.':'.$depth;
                   14329:                 for (my $j=@{$parents}; $j>=0; $j--) {
                   14330:                     my $higher;
                   14331:                     if ($j > 0) {
                   14332:                         $higher = &escape($parents->[$j]).':'.
                   14333:                                   &escape($parents->[$j-1]).':'.$j;
                   14334:                     } else {
                   14335:                         $higher = &escape($parents->[$j]).'::'.$j;
                   14336:                     }
                   14337:                     push(@{$subcats->{$higher}},$subcat);
                   14338:                 }
                   14339:             }
                   14340:             &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents,
                   14341:                                 $subcats);
1.655     raeburn  14342:             pop(@{$parents});
                   14343:         }
                   14344:     } else {
                   14345:         my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
                   14346:         my $trailstr = join(' -&gt; ',(@{$parents},$category));
                   14347:         if ($allitems->{$item} eq '') {
                   14348:             push(@{$trails},$trailstr);
                   14349:             $allitems->{$item} = scalar(@{$trails})-1;
                   14350:         }
                   14351:     }
                   14352:     return;
                   14353: }
                   14354: 
1.663     raeburn  14355: =pod
                   14356: 
1.1162    raeburn  14357: =item * &assign_categories_table()
1.663     raeburn  14358: 
                   14359: Create a datatable for display of hierarchical categories in a domain,
                   14360: with checkboxes to allow a course to be categorized. 
                   14361: 
                   14362: Inputs:
                   14363: 
                   14364: cathash - reference to hash of categories defined for the domain (from
                   14365:           configuration.db)
                   14366: 
                   14367: currcat - scalar with an & separated list of categories assigned to a course. 
                   14368: 
1.919     raeburn  14369: type    - scalar contains course type (Course or Community).
                   14370: 
1.663     raeburn  14371: Returns: $output (markup to be displayed) 
                   14372: 
                   14373: =cut
                   14374: 
                   14375: sub assign_categories_table {
1.919     raeburn  14376:     my ($cathash,$currcat,$type) = @_;
1.663     raeburn  14377:     my $output;
                   14378:     if (ref($cathash) eq 'HASH') {
                   14379:         my (@cats,@trails,%allitems,%idx,@jsarray,@path,$maxdepth);
                   14380:         &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray);
                   14381:         $maxdepth = scalar(@cats);
                   14382:         if (@cats > 0) {
                   14383:             my $itemcount = 0;
                   14384:             if (ref($cats[0]) eq 'ARRAY') {
                   14385:                 my @currcategories;
                   14386:                 if ($currcat ne '') {
                   14387:                     @currcategories = split('&',$currcat);
                   14388:                 }
1.919     raeburn  14389:                 my $table;
1.663     raeburn  14390:                 for (my $i=0; $i<@{$cats[0]}; $i++) {
                   14391:                     my $parent = $cats[0][$i];
1.919     raeburn  14392:                     next if ($parent eq 'instcode');
                   14393:                     if ($type eq 'Community') {
                   14394:                         next unless ($parent eq 'communities');
                   14395:                     } else {
                   14396:                         next if ($parent eq 'communities');
                   14397:                     }
1.663     raeburn  14398:                     my $css_class = $itemcount%2?' class="LC_odd_row"':'';
                   14399:                     my $item = &escape($parent).'::0';
                   14400:                     my $checked = '';
                   14401:                     if (@currcategories > 0) {
                   14402:                         if (grep(/^\Q$item\E$/,@currcategories)) {
1.772     bisitz   14403:                             $checked = ' checked="checked"';
1.663     raeburn  14404:                         }
                   14405:                     }
1.919     raeburn  14406:                     my $parent_title = $parent;
                   14407:                     if ($parent eq 'communities') {
                   14408:                         $parent_title = &mt('Communities');
                   14409:                     }
                   14410:                     $table .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.
                   14411:                               '<input type="checkbox" name="usecategory" value="'.
                   14412:                               $item.'"'.$checked.' />'.$parent_title.'</span>'.
                   14413:                               '<input type="hidden" name="catname" value="'.$parent.'" /></td>';
1.663     raeburn  14414:                     my $depth = 1;
                   14415:                     push(@path,$parent);
1.919     raeburn  14416:                     $table .= &assign_category_rows($itemcount,\@cats,$depth,$parent,\@path,\@currcategories);
1.663     raeburn  14417:                     pop(@path);
1.919     raeburn  14418:                     $table .= '</tr><tr><td colspan="'.$maxdepth.'" class="LC_row_separator"></td></tr>';
1.663     raeburn  14419:                     $itemcount ++;
                   14420:                 }
1.919     raeburn  14421:                 if ($itemcount) {
                   14422:                     $output = &Apache::loncommon::start_data_table().
                   14423:                               $table.
                   14424:                               &Apache::loncommon::end_data_table();
                   14425:                 }
1.663     raeburn  14426:             }
                   14427:         }
                   14428:     }
                   14429:     return $output;
                   14430: }
                   14431: 
                   14432: =pod
                   14433: 
1.1162    raeburn  14434: =item * &assign_category_rows()
1.663     raeburn  14435: 
                   14436: Create a datatable row for display of nested categories in a domain,
                   14437: with checkboxes to allow a course to be categorized,called recursively.
                   14438: 
                   14439: Inputs:
                   14440: 
                   14441: itemcount - track row number for alternating colors
                   14442: 
                   14443: cats - reference to array of arrays/hashes which encapsulates hierarchy of
                   14444:       categories and subcategories.
                   14445: 
                   14446: depth - current depth in hierarchy of categories and sub-categories - 0 indexed.
                   14447: 
                   14448: parent - parent of current category item
                   14449: 
                   14450: path - Array containing all categories back up through the hierarchy from the
                   14451:        current category to the top level.
                   14452: 
                   14453: currcategories - reference to array of current categories assigned to the course
                   14454: 
                   14455: Returns: $output (markup to be displayed).
                   14456: 
                   14457: =cut
                   14458: 
                   14459: sub assign_category_rows {
                   14460:     my ($itemcount,$cats,$depth,$parent,$path,$currcategories) = @_;
                   14461:     my ($text,$name,$item,$chgstr);
                   14462:     if (ref($cats) eq 'ARRAY') {
                   14463:         my $maxdepth = scalar(@{$cats});
                   14464:         if (ref($cats->[$depth]) eq 'HASH') {
                   14465:             if (ref($cats->[$depth]{$parent}) eq 'ARRAY') {
                   14466:                 my $numchildren = @{$cats->[$depth]{$parent}};
                   14467:                 my $css_class = $itemcount%2?' class="LC_odd_row"':'';
1.1145    raeburn  14468:                 $text .= '<td><table class="LC_data_table">';
1.663     raeburn  14469:                 for (my $j=0; $j<$numchildren; $j++) {
                   14470:                     $name = $cats->[$depth]{$parent}[$j];
                   14471:                     $item = &escape($name).':'.&escape($parent).':'.$depth;
                   14472:                     my $deeper = $depth+1;
                   14473:                     my $checked = '';
                   14474:                     if (ref($currcategories) eq 'ARRAY') {
                   14475:                         if (@{$currcategories} > 0) {
                   14476:                             if (grep(/^\Q$item\E$/,@{$currcategories})) {
1.772     bisitz   14477:                                 $checked = ' checked="checked"';
1.663     raeburn  14478:                             }
                   14479:                         }
                   14480:                     }
1.664     raeburn  14481:                     $text .= '<tr><td><span class="LC_nobreak"><label>'.
                   14482:                              '<input type="checkbox" name="usecategory" value="'.
1.675     raeburn  14483:                              $item.'"'.$checked.' />'.$name.'</label></span>'.
                   14484:                              '<input type="hidden" name="catname" value="'.$name.'" />'.
                   14485:                              '</td><td>';
1.663     raeburn  14486:                     if (ref($path) eq 'ARRAY') {
                   14487:                         push(@{$path},$name);
                   14488:                         $text .= &assign_category_rows($itemcount,$cats,$deeper,$name,$path,$currcategories);
                   14489:                         pop(@{$path});
                   14490:                     }
                   14491:                     $text .= '</td></tr>';
                   14492:                 }
                   14493:                 $text .= '</table></td>';
                   14494:             }
                   14495:         }
                   14496:     }
                   14497:     return $text;
                   14498: }
                   14499: 
1.1181    raeburn  14500: =pod
                   14501: 
                   14502: =back
                   14503: 
                   14504: =cut
                   14505: 
1.655     raeburn  14506: ############################################################
                   14507: ############################################################
                   14508: 
                   14509: 
1.443     albertel 14510: sub commit_customrole {
1.664     raeburn  14511:     my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context) = @_;
1.630     raeburn  14512:     my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.
1.443     albertel 14513:                          ($start?', '.&mt('starting').' '.localtime($start):'').
                   14514:                          ($end?', ending '.localtime($end):'').': <b>'.
                   14515:               &Apache::lonnet::assigncustomrole(
1.664     raeburn  14516:                  $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context).
1.443     albertel 14517:                  '</b><br />';
                   14518:     return $output;
                   14519: }
                   14520: 
                   14521: sub commit_standardrole {
1.1116    raeburn  14522:     my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,$credits) = @_;
1.541     raeburn  14523:     my ($output,$logmsg,$linefeed);
                   14524:     if ($context eq 'auto') {
                   14525:         $linefeed = "\n";
                   14526:     } else {
                   14527:         $linefeed = "<br />\n";
                   14528:     }  
1.443     albertel 14529:     if ($three eq 'st') {
1.541     raeburn  14530:         my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
1.1116    raeburn  14531:                                          $one,$two,$sec,$context,$credits);
1.541     raeburn  14532:         if (($result =~ /^error/) || ($result eq 'not_in_class') || 
1.626     raeburn  14533:             ($result eq 'unknown_course') || ($result eq 'refused')) {
                   14534:             $output = $logmsg.' '.&mt('Error: ').$result."\n"; 
1.443     albertel 14535:         } else {
1.541     raeburn  14536:             $output = $logmsg.$linefeed.&mt('Assigning').' '.$three.' in '.$url.
1.443     albertel 14537:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  14538:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
                   14539:             if ($context eq 'auto') {
                   14540:                 $output .= $result.$linefeed.&mt('Add to classlist').': ok';
                   14541:             } else {
                   14542:                $output .= '<b>'.$result.'</b>'.$linefeed.
                   14543:                &mt('Add to classlist').': <b>ok</b>';
                   14544:             }
                   14545:             $output .= $linefeed;
1.443     albertel 14546:         }
                   14547:     } else {
                   14548:         $output = &mt('Assigning').' '.$three.' in '.$url.
                   14549:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  14550:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
1.652     raeburn  14551:         my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context);
1.541     raeburn  14552:         if ($context eq 'auto') {
                   14553:             $output .= $result.$linefeed;
                   14554:         } else {
                   14555:             $output .= '<b>'.$result.'</b>'.$linefeed;
                   14556:         }
1.443     albertel 14557:     }
                   14558:     return $output;
                   14559: }
                   14560: 
                   14561: sub commit_studentrole {
1.1116    raeburn  14562:     my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,
                   14563:         $credits) = @_;
1.626     raeburn  14564:     my ($result,$linefeed,$oldsecurl,$newsecurl);
1.541     raeburn  14565:     if ($context eq 'auto') {
                   14566:         $linefeed = "\n";
                   14567:     } else {
                   14568:         $linefeed = '<br />'."\n";
                   14569:     }
1.443     albertel 14570:     if (defined($one) && defined($two)) {
                   14571:         my $cid=$one.'_'.$two;
                   14572:         my $oldsec=&Apache::lonnet::getsection($udom,$uname,$cid);
                   14573:         my $secchange = 0;
                   14574:         my $expire_role_result;
                   14575:         my $modify_section_result;
1.628     raeburn  14576:         if ($oldsec ne '-1') { 
                   14577:             if ($oldsec ne $sec) {
1.443     albertel 14578:                 $secchange = 1;
1.628     raeburn  14579:                 my $now = time;
1.443     albertel 14580:                 my $uurl='/'.$cid;
                   14581:                 $uurl=~s/\_/\//g;
                   14582:                 if ($oldsec) {
                   14583:                     $uurl.='/'.$oldsec;
                   14584:                 }
1.626     raeburn  14585:                 $oldsecurl = $uurl;
1.628     raeburn  14586:                 $expire_role_result = 
1.652     raeburn  14587:                     &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context);
1.628     raeburn  14588:                 if ($env{'request.course.sec'} ne '') { 
                   14589:                     if ($expire_role_result eq 'refused') {
                   14590:                         my @roles = ('st');
                   14591:                         my @statuses = ('previous');
                   14592:                         my @roledoms = ($one);
                   14593:                         my $withsec = 1;
                   14594:                         my %roleshash = 
                   14595:                             &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
                   14596:                                               \@statuses,\@roles,\@roledoms,$withsec);
                   14597:                         if (defined ($roleshash{$two.':'.$one.':st:'.$oldsec})) {
                   14598:                             my ($oldstart,$oldend) = 
                   14599:                                 split(':',$roleshash{$two.':'.$one.':st:'.$oldsec});
                   14600:                             if ($oldend > 0 && $oldend <= $now) {
                   14601:                                 $expire_role_result = 'ok';
                   14602:                             }
                   14603:                         }
                   14604:                     }
                   14605:                 }
1.443     albertel 14606:                 $result = $expire_role_result;
                   14607:             }
                   14608:         }
                   14609:         if (($expire_role_result eq 'ok') || ($secchange == 0)) {
1.1116    raeburn  14610:             $modify_section_result = 
                   14611:                 &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,
                   14612:                                                            undef,undef,undef,$sec,
                   14613:                                                            $end,$start,'','',$cid,
                   14614:                                                            '',$context,$credits);
1.443     albertel 14615:             if ($modify_section_result =~ /^ok/) {
                   14616:                 if ($secchange == 1) {
1.628     raeburn  14617:                     if ($sec eq '') {
                   14618:                         $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to student role without a section.',$uname,$oldsec).$linefeed;
                   14619:                     } else {
                   14620:                         $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to new section: [_3].',$uname,$oldsec,$sec).$linefeed;
                   14621:                     }
1.443     albertel 14622:                 } elsif ($oldsec eq '-1') {
1.628     raeburn  14623:                     if ($sec eq '') {
                   14624:                         $$logmsg .= &mt('New student role without a section for [_1] in course [_2].',$uname,$cid).$linefeed;
                   14625:                     } else {
                   14626:                         $$logmsg .= &mt('New student role for [_1] in section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
                   14627:                     }
1.443     albertel 14628:                 } else {
1.628     raeburn  14629:                     if ($sec eq '') {
                   14630:                         $$logmsg .= &mt('Student [_1] assigned to course [_2] without a section.',$uname,$cid).$linefeed;
                   14631:                     } else {
                   14632:                         $$logmsg .= &mt('Student [_1] assigned to section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
                   14633:                     }
1.443     albertel 14634:                 }
                   14635:             } else {
1.1115    raeburn  14636:                 if ($secchange) { 
1.628     raeburn  14637:                     $$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;
                   14638:                 } else {
                   14639:                     $$logmsg .= &mt('Error when attempting to modify role for [_1] for section: "[_2]" in course [_3] -error:',$uname,$sec,$cid).' '.$modify_section_result.$linefeed;
                   14640:                 }
1.443     albertel 14641:             }
                   14642:             $result = $modify_section_result;
                   14643:         } elsif ($secchange == 1) {
1.628     raeburn  14644:             if ($oldsec eq '') {
1.1103    raeburn  14645:                 $$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  14646:             } else {
                   14647:                 $$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;
                   14648:             }
1.626     raeburn  14649:             if ($expire_role_result eq 'refused') {
                   14650:                 my $newsecurl = '/'.$cid;
                   14651:                 $newsecurl =~ s/\_/\//g;
                   14652:                 if ($sec ne '') {
                   14653:                     $newsecurl.='/'.$sec;
                   14654:                 }
                   14655:                 if (&Apache::lonnet::allowed('cst',$newsecurl) && !(&Apache::lonnet::allowed('cst',$oldsecurl))) {
                   14656:                     if ($sec eq '') {
                   14657:                         $$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;
                   14658:                     } else {
                   14659:                         $$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;
                   14660:                     }
                   14661:                 }
                   14662:             }
1.443     albertel 14663:         }
                   14664:     } else {
1.626     raeburn  14665:         $$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 14666:         $result = "error: incomplete course id\n";
                   14667:     }
                   14668:     return $result;
                   14669: }
                   14670: 
1.1108    raeburn  14671: sub show_role_extent {
                   14672:     my ($scope,$context,$role) = @_;
                   14673:     $scope =~ s{^/}{};
                   14674:     my @courseroles = &Apache::lonuserutils::roles_by_context('course',1);
                   14675:     push(@courseroles,'co');
                   14676:     my @authorroles = &Apache::lonuserutils::roles_by_context('author');
                   14677:     if (($context eq 'course') || (grep(/^\Q$role\E/,@courseroles))) {
                   14678:         $scope =~ s{/}{_};
                   14679:         return '<span class="LC_cusr_emph">'.$env{'course.'.$scope.'.description'}.'</span>';
                   14680:     } elsif (($context eq 'author') || (grep(/^\Q$role\E/,@authorroles))) {
                   14681:         my ($audom,$auname) = split(/\//,$scope);
                   14682:         return &mt('[_1] Author Space','<span class="LC_cusr_emph">'.
                   14683:                    &Apache::loncommon::plainname($auname,$audom).'</span>');
                   14684:     } else {
                   14685:         $scope =~ s{/$}{};
                   14686:         return &mt('Domain: [_1]','<span class="LC_cusr_emph">'.
                   14687:                    &Apache::lonnet::domain($scope,'description').'</span>');
                   14688:     }
                   14689: }
                   14690: 
1.443     albertel 14691: ############################################################
                   14692: ############################################################
                   14693: 
1.566     albertel 14694: sub check_clone {
1.578     raeburn  14695:     my ($args,$linefeed) = @_;
1.566     albertel 14696:     my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
                   14697:     my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
                   14698:     my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
                   14699:     my $clonemsg;
                   14700:     my $can_clone = 0;
1.944     raeburn  14701:     my $lctype = lc($args->{'crstype'});
1.908     raeburn  14702:     if ($lctype ne 'community') {
                   14703:         $lctype = 'course';
                   14704:     }
1.566     albertel 14705:     if ($clonehome eq 'no_host') {
1.944     raeburn  14706:         if ($args->{'crstype'} eq 'Community') {
1.908     raeburn  14707:             $clonemsg = &mt('No new community created.').$linefeed.&mt('A new community could not be cloned from the specified original - [_1] - because it is a non-existent community.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
                   14708:         } else {
                   14709:             $clonemsg = &mt('No new course created.').$linefeed.&mt('A new course could not be cloned from the specified original - [_1] - because it is a non-existent course.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
                   14710:         }     
1.566     albertel 14711:     } else {
                   14712: 	my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
1.944     raeburn  14713:         if ($args->{'crstype'} eq 'Community') {
1.908     raeburn  14714:             if ($clonedesc{'type'} ne 'Community') {
                   14715:                  $clonemsg = &mt('No new community created.').$linefeed.&mt('A new community could not be cloned from the specified original - [_1] - because it is a course not a community.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
                   14716:                 return ($can_clone, $clonemsg, $cloneid, $clonehome);
                   14717:             }
                   14718:         }
1.882     raeburn  14719: 	if (($env{'request.role.domain'} eq $args->{'clonedomain'}) && 
                   14720:             (&Apache::lonnet::allowed('ccc',$env{'request.role.domain'}))) {
1.566     albertel 14721: 	    $can_clone = 1;
                   14722: 	} else {
1.1221    raeburn  14723: 	    my %clonehash = &Apache::lonnet::get('environment',['cloners','internal.coursecode'],
1.566     albertel 14724: 						 $args->{'clonedomain'},$args->{'clonecourse'});
1.1221    raeburn  14725:             if ($clonehash{'cloners'} eq '') {
                   14726:                 my %domdefs = &Apache::lonnet::get_domain_defaults($args->{'course_domain'});
                   14727:                 if ($domdefs{'canclone'}) {
                   14728:                     unless ($domdefs{'canclone'} eq 'none') {
                   14729:                         if ($domdefs{'canclone'} eq 'domain') {
                   14730:                             if ($args->{'ccdomain'} eq $args->{'clonedomain'}) {
                   14731:                                 $can_clone = 1;
                   14732:                             }
                   14733:                         } elsif (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) && 
                   14734:                                  ($args->{'clonedomain'} eq  $args->{'course_domain'})) {
                   14735:                             if (&Apache::lonnet::default_instcode_cloning($args->{'clonedomain'},$domdefs{'canclone'},
                   14736:                                                                           $clonehash{'internal.coursecode'},$args->{'crscode'})) {
                   14737:                                 $can_clone = 1;
                   14738:                             }
                   14739:                         }
                   14740:                     }
                   14741:                 }
1.578     raeburn  14742:             } else {
1.1221    raeburn  14743: 	        my @cloners = split(/,/,$clonehash{'cloners'});
                   14744:                 if (grep(/^\*$/,@cloners)) {
1.942     raeburn  14745:                     $can_clone = 1;
1.1221    raeburn  14746:                 } elsif (grep(/^\*\:\Q$args->{'ccdomain'}\E$/,@cloners)) {
1.942     raeburn  14747:                     $can_clone = 1;
1.1225    raeburn  14748:                 } elsif (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners)) {
                   14749:                     $can_clone = 1;
1.1221    raeburn  14750:                 }
                   14751:                 unless ($can_clone) {
1.1225    raeburn  14752:                     if (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) && 
                   14753:                         ($args->{'clonedomain'} eq  $args->{'course_domain'})) {
1.1221    raeburn  14754:                         my (%gotdomdefaults,%gotcodedefaults);
                   14755:                         foreach my $cloner (@cloners) {
                   14756:                             if (($cloner ne '*') && ($cloner !~ /^\*\:$match_domain$/) &&
                   14757:                                 ($cloner !~ /^$match_username\:$match_domain$/) && ($cloner ne '')) {
                   14758:                                 my (%codedefaults,@code_order);
                   14759:                                 if (ref($gotcodedefaults{$args->{'clonedomain'}}) eq 'HASH') {
                   14760:                                     if (ref($gotcodedefaults{$args->{'clonedomain'}}{'defaults'}) eq 'HASH') {
                   14761:                                         %codedefaults = %{$gotcodedefaults{$args->{'clonedomain'}}{'defaults'}};
                   14762:                                     }
                   14763:                                     if (ref($gotcodedefaults{$args->{'clonedomain'}}{'order'}) eq 'ARRAY') {
                   14764:                                         @code_order = @{$gotcodedefaults{$args->{'clonedomain'}}{'order'}};
                   14765:                                     }
                   14766:                                 } else {
                   14767:                                     &Apache::lonnet::auto_instcode_defaults($args->{'clonedomain'},
                   14768:                                                                             \%codedefaults,
                   14769:                                                                             \@code_order);
                   14770:                                     $gotcodedefaults{$args->{'clonedomain'}}{'defaults'} = \%codedefaults;
                   14771:                                     $gotcodedefaults{$args->{'clonedomain'}}{'order'} = \@code_order;
                   14772:                                 }
                   14773:                                 if (@code_order > 0) {
                   14774:                                     if (&Apache::lonnet::check_instcode_cloning(\%codedefaults,\@code_order,
                   14775:                                                                                 $cloner,$clonehash{'internal.coursecode'},
                   14776:                                                                                 $args->{'crscode'})) {
                   14777:                                         $can_clone = 1;
                   14778:                                         last;
                   14779:                                     }
                   14780:                                 }
                   14781:                             }
                   14782:                         }
                   14783:                     }
1.1225    raeburn  14784:                 }
                   14785:             }
                   14786:             unless ($can_clone) {
                   14787:                 my $ccrole = 'cc';
                   14788:                 if ($args->{'crstype'} eq 'Community') {
                   14789:                     $ccrole = 'co';
                   14790:                 }
                   14791: 	        my %roleshash =
                   14792: 		    &Apache::lonnet::get_my_roles($args->{'ccuname'},
                   14793: 					          $args->{'ccdomain'},
                   14794:                                                   'userroles',['active'],[$ccrole],
                   14795: 					          [$args->{'clonedomain'}]);
                   14796: 	        if ($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':'.$ccrole}) {
                   14797:                     $can_clone = 1;
                   14798:                 } elsif (&Apache::lonnet::is_course_owner($args->{'clonedomain'},$args->{'clonecourse'},
                   14799:                                                           $args->{'ccuname'},$args->{'ccdomain'})) {
                   14800:                     $can_clone = 1;
1.1221    raeburn  14801:                 }
                   14802:             }
                   14803:             unless ($can_clone) {
                   14804:                 if ($args->{'crstype'} eq 'Community') {
                   14805:                     $clonemsg = &mt('No new community created.').$linefeed.&mt('The new community could not be cloned from the existing community because the new community owner ([_1]) does not have cloning rights in the existing community ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
1.942     raeburn  14806:                 } else {
1.1221    raeburn  14807:                     $clonemsg = &mt('No new course created.').$linefeed.&mt('The new course could not be cloned from the existing course because the new course owner ([_1]) does not have cloning rights in the existing course ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
                   14808:                 }
1.566     albertel 14809: 	    }
1.578     raeburn  14810:         }
1.566     albertel 14811:     }
                   14812:     return ($can_clone, $clonemsg, $cloneid, $clonehome);
                   14813: }
                   14814: 
1.444     albertel 14815: sub construct_course {
1.1166    raeburn  14816:     my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context,$cnum,$category,$coderef) = @_;
1.444     albertel 14817:     my $outcome;
1.541     raeburn  14818:     my $linefeed =  '<br />'."\n";
                   14819:     if ($context eq 'auto') {
                   14820:         $linefeed = "\n";
                   14821:     }
1.566     albertel 14822: 
                   14823: #
                   14824: # Are we cloning?
                   14825: #
                   14826:     my ($can_clone, $clonemsg, $cloneid, $clonehome);
                   14827:     if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
1.578     raeburn  14828: 	($can_clone, $clonemsg, $cloneid, $clonehome) = &check_clone($args,$linefeed);
1.566     albertel 14829: 	if ($context ne 'auto') {
1.578     raeburn  14830:             if ($clonemsg ne '') {
                   14831: 	        $clonemsg = '<span class="LC_error">'.$clonemsg.'</span>';
                   14832:             }
1.566     albertel 14833: 	}
                   14834: 	$outcome .= $clonemsg.$linefeed;
                   14835: 
                   14836:         if (!$can_clone) {
                   14837: 	    return (0,$outcome);
                   14838: 	}
                   14839:     }
                   14840: 
1.444     albertel 14841: #
                   14842: # Open course
                   14843: #
                   14844:     my $crstype = lc($args->{'crstype'});
                   14845:     my %cenv=();
                   14846:     $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},
                   14847:                                              $args->{'cdescr'},
                   14848:                                              $args->{'curl'},
                   14849:                                              $args->{'course_home'},
                   14850:                                              $args->{'nonstandard'},
                   14851:                                              $args->{'crscode'},
                   14852:                                              $args->{'ccuname'}.':'.
                   14853:                                              $args->{'ccdomain'},
1.882     raeburn  14854:                                              $args->{'crstype'},
1.885     raeburn  14855:                                              $cnum,$context,$category);
1.444     albertel 14856: 
                   14857:     # Note: The testing routines depend on this being output; see 
                   14858:     # Utils::Course. This needs to at least be output as a comment
                   14859:     # if anyone ever decides to not show this, and Utils::Course::new
                   14860:     # will need to be suitably modified.
1.541     raeburn  14861:     $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed;
1.943     raeburn  14862:     if ($$courseid =~ /^error:/) {
                   14863:         return (0,$outcome);
                   14864:     }
                   14865: 
1.444     albertel 14866: #
                   14867: # Check if created correctly
                   14868: #
1.479     albertel 14869:     ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
1.444     albertel 14870:     my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
1.943     raeburn  14871:     if ($crsuhome eq 'no_host') {
                   14872:         $outcome .= &mt('Course creation failed, unrecognized course home server.').$linefeed;
                   14873:         return (0,$outcome);
                   14874:     }
1.541     raeburn  14875:     $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
1.566     albertel 14876: 
1.444     albertel 14877: #
1.566     albertel 14878: # Do the cloning
                   14879: #   
                   14880:     if ($can_clone && $cloneid) {
                   14881: 	$clonemsg = &mt('Cloning [_1] from [_2]',$crstype,$clonehome);
                   14882: 	if ($context ne 'auto') {
                   14883: 	    $clonemsg = '<span class="LC_success">'.$clonemsg.'</span>';
                   14884: 	}
                   14885: 	$outcome .= $clonemsg.$linefeed;
                   14886: 	my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
1.444     albertel 14887: # Copy all files
1.637     www      14888: 	&Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},$args->{'dateshift'});
1.444     albertel 14889: # Restore URL
1.566     albertel 14890: 	$cenv{'url'}=$oldcenv{'url'};
1.444     albertel 14891: # Restore title
1.566     albertel 14892: 	$cenv{'description'}=$oldcenv{'description'};
1.955     raeburn  14893: # Restore creation date, creator and creation context.
                   14894:         $cenv{'internal.created'}=$oldcenv{'internal.created'};
                   14895:         $cenv{'internal.creator'}=$oldcenv{'internal.creator'};
                   14896:         $cenv{'internal.creationcontext'}=$oldcenv{'internal.creationcontext'};
1.444     albertel 14897: # Mark as cloned
1.566     albertel 14898: 	$cenv{'clonedfrom'}=$cloneid;
1.638     www      14899: # Need to clone grading mode
                   14900:         my %newenv=&Apache::lonnet::get('environment',['grading'],$$crsudom,$$crsunum);
                   14901:         $cenv{'grading'}=$newenv{'grading'};
                   14902: # Do not clone these environment entries
                   14903:         &Apache::lonnet::del('environment',
                   14904:                   ['default_enrollment_start_date',
                   14905:                    'default_enrollment_end_date',
                   14906:                    'question.email',
                   14907:                    'policy.email',
                   14908:                    'comment.email',
                   14909:                    'pch.users.denied',
1.725     raeburn  14910:                    'plc.users.denied',
                   14911:                    'hidefromcat',
1.1121    raeburn  14912:                    'checkforpriv',
1.1166    raeburn  14913:                    'categories',
                   14914:                    'internal.uniquecode'],
1.638     www      14915:                    $$crsudom,$$crsunum);
1.1170    raeburn  14916:         if ($args->{'textbook'}) {
                   14917:             $cenv{'internal.textbook'} = $args->{'textbook'};
                   14918:         }
1.444     albertel 14919:     }
1.566     albertel 14920: 
1.444     albertel 14921: #
                   14922: # Set environment (will override cloned, if existing)
                   14923: #
                   14924:     my @sections = ();
                   14925:     my @xlists = ();
                   14926:     if ($args->{'crstype'}) {
                   14927:         $cenv{'type'}=$args->{'crstype'};
                   14928:     }
                   14929:     if ($args->{'crsid'}) {
                   14930:         $cenv{'courseid'}=$args->{'crsid'};
                   14931:     }
                   14932:     if ($args->{'crscode'}) {
                   14933:         $cenv{'internal.coursecode'}=$args->{'crscode'};
                   14934:     }
                   14935:     if ($args->{'crsquota'} ne '') {
                   14936:         $cenv{'internal.coursequota'}=$args->{'crsquota'};
                   14937:     } else {
                   14938:         $cenv{'internal.coursequota'}=$args->{'crsquota'} = 20;
                   14939:     }
                   14940:     if ($args->{'ccuname'}) {
                   14941:         $cenv{'internal.courseowner'} = $args->{'ccuname'}.
                   14942:                                         ':'.$args->{'ccdomain'};
                   14943:     } else {
                   14944:         $cenv{'internal.courseowner'} = $args->{'curruser'};
                   14945:     }
1.1116    raeburn  14946:     if ($args->{'defaultcredits'}) {
                   14947:         $cenv{'internal.defaultcredits'} = $args->{'defaultcredits'};
                   14948:     }
1.444     albertel 14949:     my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
                   14950:     if ($args->{'crssections'}) {
                   14951:         $cenv{'internal.sectionnums'} = '';
                   14952:         if ($args->{'crssections'} =~ m/,/) {
                   14953:             @sections = split/,/,$args->{'crssections'};
                   14954:         } else {
                   14955:             $sections[0] = $args->{'crssections'};
                   14956:         }
                   14957:         if (@sections > 0) {
                   14958:             foreach my $item (@sections) {
                   14959:                 my ($sec,$gp) = split/:/,$item;
                   14960:                 my $class = $args->{'crscode'}.$sec;
                   14961:                 my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});
                   14962:                 $cenv{'internal.sectionnums'} .= $item.',';
                   14963:                 unless ($addcheck eq 'ok') {
                   14964:                     push @badclasses, $class;
                   14965:                 }
                   14966:             }
                   14967:             $cenv{'internal.sectionnums'} =~ s/,$//;
                   14968:         }
                   14969:     }
                   14970: # do not hide course coordinator from staff listing, 
                   14971: # even if privileged
                   14972:     $cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
1.1121    raeburn  14973: # add course coordinator's domain to domains to check for privileged users
                   14974: # if different to course domain
                   14975:     if ($$crsudom ne $args->{'ccdomain'}) {
                   14976:         $cenv{'checkforpriv'} = $args->{'ccdomain'};
                   14977:     }
1.444     albertel 14978: # add crosslistings
                   14979:     if ($args->{'crsxlist'}) {
                   14980:         $cenv{'internal.crosslistings'}='';
                   14981:         if ($args->{'crsxlist'} =~ m/,/) {
                   14982:             @xlists = split/,/,$args->{'crsxlist'};
                   14983:         } else {
                   14984:             $xlists[0] = $args->{'crsxlist'};
                   14985:         }
                   14986:         if (@xlists > 0) {
                   14987:             foreach my $item (@xlists) {
                   14988:                 my ($xl,$gp) = split/:/,$item;
                   14989:                 my $addcheck =  &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});
                   14990:                 $cenv{'internal.crosslistings'} .= $item.',';
                   14991:                 unless ($addcheck eq 'ok') {
                   14992:                     push @badclasses, $xl;
                   14993:                 }
                   14994:             }
                   14995:             $cenv{'internal.crosslistings'} =~ s/,$//;
                   14996:         }
                   14997:     }
                   14998:     if ($args->{'autoadds'}) {
                   14999:         $cenv{'internal.autoadds'}=$args->{'autoadds'};
                   15000:     }
                   15001:     if ($args->{'autodrops'}) {
                   15002:         $cenv{'internal.autodrops'}=$args->{'autodrops'};
                   15003:     }
                   15004: # check for notification of enrollment changes
                   15005:     my @notified = ();
                   15006:     if ($args->{'notify_owner'}) {
                   15007:         if ($args->{'ccuname'} ne '') {
                   15008:             push(@notified,$args->{'ccuname'}.':'.$args->{'ccdomain'});
                   15009:         }
                   15010:     }
                   15011:     if ($args->{'notify_dc'}) {
                   15012:         if ($uname ne '') { 
1.630     raeburn  15013:             push(@notified,$uname.':'.$udom);
1.444     albertel 15014:         }
                   15015:     }
                   15016:     if (@notified > 0) {
                   15017:         my $notifylist;
                   15018:         if (@notified > 1) {
                   15019:             $notifylist = join(',',@notified);
                   15020:         } else {
                   15021:             $notifylist = $notified[0];
                   15022:         }
                   15023:         $cenv{'internal.notifylist'} = $notifylist;
                   15024:     }
                   15025:     if (@badclasses > 0) {
                   15026:         my %lt=&Apache::lonlocal::texthash(
                   15027:                 'tclb' => 'The courses listed below were included as sections or crosslistings affiliated with your new LON-CAPA course.  However, if automated course roster updates are enabled for this class, these particular sections/crosslistings will not contribute towards enrollment, because the user identified as the course owner for this LON-CAPA course',
                   15028:                 'dnhr' => 'does not have rights to access enrollment in these classes',
                   15029:                 'adby' => 'as determined by the policies of your institution on access to official classlists'
                   15030:         );
1.541     raeburn  15031:         my $badclass_msg = $cenv{'internal.courseowner'}.') - '.$lt{'dnhr'}.
                   15032:                            ' ('.$lt{'adby'}.')';
                   15033:         if ($context eq 'auto') {
                   15034:             $outcome .= $badclass_msg.$linefeed;
1.566     albertel 15035:             $outcome .= '<div class="LC_warning">'.$badclass_msg.$linefeed.'<ul>'."\n";
1.541     raeburn  15036:             foreach my $item (@badclasses) {
                   15037:                 if ($context eq 'auto') {
                   15038:                     $outcome .= " - $item\n";
                   15039:                 } else {
                   15040:                     $outcome .= "<li>$item</li>\n";
                   15041:                 }
                   15042:             }
                   15043:             if ($context eq 'auto') {
                   15044:                 $outcome .= $linefeed;
                   15045:             } else {
1.566     albertel 15046:                 $outcome .= "</ul><br /><br /></div>\n";
1.541     raeburn  15047:             }
                   15048:         } 
1.444     albertel 15049:     }
                   15050:     if ($args->{'no_end_date'}) {
                   15051:         $args->{'endaccess'} = 0;
                   15052:     }
                   15053:     $cenv{'internal.autostart'}=$args->{'enrollstart'};
                   15054:     $cenv{'internal.autoend'}=$args->{'enrollend'};
                   15055:     $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};
                   15056:     $cenv{'default_enrollment_end_date'}=$args->{'endaccess'};
                   15057:     if ($args->{'showphotos'}) {
                   15058:       $cenv{'internal.showphotos'}=$args->{'showphotos'};
                   15059:     }
                   15060:     $cenv{'internal.authtype'} = $args->{'authtype'};
                   15061:     $cenv{'internal.autharg'} = $args->{'autharg'}; 
                   15062:     if ( ($cenv{'internal.authtype'} =~ /^krb/) && ($cenv{'internal.autoadds'} == 1)) {
                   15063:         if (! defined($cenv{'internal.autharg'}) || $cenv{'internal.autharg'}  eq '') {
1.541     raeburn  15064:             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'); 
                   15065:             if ($context eq 'auto') {
                   15066:                 $outcome .= $krb_msg;
                   15067:             } else {
1.566     albertel 15068:                 $outcome .= '<span class="LC_error">'.$krb_msg.'</span>';
1.541     raeburn  15069:             }
                   15070:             $outcome .= $linefeed;
1.444     albertel 15071:         }
                   15072:     }
                   15073:     if (($args->{'ccdomain'}) && ($args->{'ccuname'})) {
                   15074:        if ($args->{'setpolicy'}) {
                   15075:            $cenv{'policy.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   15076:        }
                   15077:        if ($args->{'setcontent'}) {
                   15078:            $cenv{'question.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   15079:        }
                   15080:     }
                   15081:     if ($args->{'reshome'}) {
                   15082: 	$cenv{'reshome'}=$args->{'reshome'}.'/';
                   15083: 	$cenv{'reshome'}=~s/\/+$/\//;
                   15084:     }
                   15085: #
                   15086: # course has keyed access
                   15087: #
                   15088:     if ($args->{'setkeys'}) {
                   15089:        $cenv{'keyaccess'}='yes';
                   15090:     }
                   15091: # if specified, key authority is not course, but user
                   15092: # only active if keyaccess is yes
                   15093:     if ($args->{'keyauth'}) {
1.487     albertel 15094: 	my ($user,$domain) = split(':',$args->{'keyauth'});
                   15095: 	$user = &LONCAPA::clean_username($user);
                   15096: 	$domain = &LONCAPA::clean_username($domain);
1.488     foxr     15097: 	if ($user ne '' && $domain ne '') {
1.487     albertel 15098: 	    $cenv{'keyauth'}=$user.':'.$domain;
1.444     albertel 15099: 	}
                   15100:     }
                   15101: 
1.1166    raeburn  15102: #
1.1167    raeburn  15103: #  generate and store uniquecode (available to course requester), if course should have one.
1.1166    raeburn  15104: #
                   15105:     if ($args->{'uniquecode'}) {
                   15106:         my ($code,$error) = &make_unique_code($$crsudom,$$crsunum);
                   15107:         if ($code) {
                   15108:             $cenv{'internal.uniquecode'} = $code;
1.1167    raeburn  15109:             my %crsinfo =
                   15110:                 &Apache::lonnet::courseiddump($$crsudom,'.',1,'.','.',$$crsunum,undef,undef,'.');
                   15111:             if (ref($crsinfo{$$crsudom.'_'.$$crsunum}) eq 'HASH') {
                   15112:                 $crsinfo{$$crsudom.'_'.$$crsunum}{'uniquecode'} = $code;
                   15113:                 my $putres = &Apache::lonnet::courseidput($$crsudom,\%crsinfo,$crsuhome,'notime');
                   15114:             } 
1.1166    raeburn  15115:             if (ref($coderef)) {
                   15116:                 $$coderef = $code;
                   15117:             }
                   15118:         }
                   15119:     }
                   15120: 
1.444     albertel 15121:     if ($args->{'disresdis'}) {
                   15122:         $cenv{'pch.roles.denied'}='st';
                   15123:     }
                   15124:     if ($args->{'disablechat'}) {
                   15125:         $cenv{'plc.roles.denied'}='st';
                   15126:     }
                   15127: 
                   15128:     # Record we've not yet viewed the Course Initialization Helper for this 
                   15129:     # course
                   15130:     $cenv{'course.helper.not.run'} = 1;
                   15131:     #
                   15132:     # Use new Randomseed
                   15133:     #
                   15134:     $cenv{'rndseed'}=&Apache::lonnet::latest_rnd_algorithm_id();;
                   15135:     $cenv{'receiptalg'}=&Apache::lonnet::latest_receipt_algorithm_id();;
                   15136:     #
                   15137:     # The encryption code and receipt prefix for this course
                   15138:     #
                   15139:     $cenv{'internal.encseed'}=$Apache::lonnet::perlvar{'lonReceipt'}.$$.time.int(rand(9999));
                   15140:     $cenv{'internal.encpref'}=100+int(9*rand(99));
                   15141:     #
                   15142:     # By default, use standard grading
                   15143:     if (!defined($cenv{'grading'})) { $cenv{'grading'} = 'standard'; }
                   15144: 
1.541     raeburn  15145:     $outcome .= $linefeed.&mt('Setting environment').': '.                 
                   15146:           &Apache::lonnet::put('environment',\%cenv,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 15147: #
                   15148: # Open all assignments
                   15149: #
                   15150:     if ($args->{'openall'}) {
                   15151:        my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate';
                   15152:        my %storecontent = ($storeunder         => time,
                   15153:                            $storeunder.'.type' => 'date_start');
                   15154:        
                   15155:        $outcome .= &mt('Opening all assignments').': '.&Apache::lonnet::cput
1.541     raeburn  15156:                  ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 15157:    }
                   15158: #
                   15159: # Set first page
                   15160: #
                   15161:     unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
                   15162: 	    || ($cloneid)) {
1.445     albertel 15163: 	use LONCAPA::map;
1.444     albertel 15164: 	$outcome .= &mt('Setting first resource').': ';
1.445     albertel 15165: 
                   15166: 	my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
                   15167:         my ($errtext,$fatal)=&LONCAPA::map::mapread($map);
                   15168: 
1.444     albertel 15169:         $outcome .= ($fatal?$errtext:'read ok').' - ';
                   15170:         my $title; my $url;
                   15171:         if ($args->{'firstres'} eq 'syl') {
1.690     bisitz   15172: 	    $title=&mt('Syllabus');
1.444     albertel 15173:             $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus';
                   15174:         } else {
1.963     raeburn  15175:             $title=&mt('Table of Contents');
1.444     albertel 15176:             $url='/adm/navmaps';
                   15177:         }
1.445     albertel 15178: 
                   15179:         $LONCAPA::map::resources[1]=$title.':'.$url.':false:start:res';
                   15180: 	(my $outtext,$errtext) = &LONCAPA::map::storemap($map,1);
                   15181: 
                   15182: 	if ($errtext) { $fatal=2; }
1.541     raeburn  15183:         $outcome .= ($fatal?$errtext:'write ok').$linefeed;
1.444     albertel 15184:     }
1.566     albertel 15185: 
                   15186:     return (1,$outcome);
1.444     albertel 15187: }
                   15188: 
1.1166    raeburn  15189: sub make_unique_code {
                   15190:     my ($cdom,$cnum) = @_;
                   15191:     # get lock on uniquecodes db
                   15192:     my $lockhash = {
                   15193:                       $cnum."\0".'uniquecodes' => $env{'user.name'}.
                   15194:                                                   ':'.$env{'user.domain'},
                   15195:                    };
                   15196:     my $tries = 0;
                   15197:     my $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
                   15198:     my ($code,$error);
                   15199:   
                   15200:     while (($gotlock ne 'ok') && ($tries<3)) {
                   15201:         $tries ++;
                   15202:         sleep 1;
                   15203:         $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
                   15204:     }
                   15205:     if ($gotlock eq 'ok') {
                   15206:         my %currcodes = &Apache::lonnet::dump_dom('uniquecodes',$cdom);
                   15207:         my $gotcode;
                   15208:         my $attempts = 0;
                   15209:         while ((!$gotcode) && ($attempts < 100)) {
                   15210:             $code = &generate_code();
                   15211:             if (!exists($currcodes{$code})) {
                   15212:                 $gotcode = 1;
                   15213:                 unless (&Apache::lonnet::newput_dom('uniquecodes',{ $code => $cnum },$cdom) eq 'ok') {
                   15214:                     $error = 'nostore';
                   15215:                 }
                   15216:             }
                   15217:             $attempts ++;
                   15218:         }
                   15219:         my @del_lock = ($cnum."\0".'uniquecodes');
                   15220:         my $dellockoutcome = &Apache::lonnet::del_dom('uniquecodes',\@del_lock,$cdom);
                   15221:     } else {
                   15222:         $error = 'nolock';
                   15223:     }
                   15224:     return ($code,$error);
                   15225: }
                   15226: 
                   15227: sub generate_code {
                   15228:     my $code;
                   15229:     my @letts = qw(B C D G H J K M N P Q R S T V W X Z);
                   15230:     for (my $i=0; $i<6; $i++) {
                   15231:         my $lettnum = int (rand 2);
                   15232:         my $item = '';
                   15233:         if ($lettnum) {
                   15234:             $item = $letts[int( rand(18) )];
                   15235:         } else {
                   15236:             $item = 1+int( rand(8) );
                   15237:         }
                   15238:         $code .= $item;
                   15239:     }
                   15240:     return $code;
                   15241: }
                   15242: 
1.444     albertel 15243: ############################################################
                   15244: ############################################################
                   15245: 
1.953     droeschl 15246: #SD
                   15247: # only Community and Course, or anything else?
1.378     raeburn  15248: sub course_type {
                   15249:     my ($cid) = @_;
                   15250:     if (!defined($cid)) {
                   15251:         $cid = $env{'request.course.id'};
                   15252:     }
1.404     albertel 15253:     if (defined($env{'course.'.$cid.'.type'})) {
                   15254:         return $env{'course.'.$cid.'.type'};
1.378     raeburn  15255:     } else {
                   15256:         return 'Course';
1.377     raeburn  15257:     }
                   15258: }
1.156     albertel 15259: 
1.406     raeburn  15260: sub group_term {
                   15261:     my $crstype = &course_type();
                   15262:     my %names = (
                   15263:                   'Course' => 'group',
1.865     raeburn  15264:                   'Community' => 'group',
1.406     raeburn  15265:                 );
                   15266:     return $names{$crstype};
                   15267: }
                   15268: 
1.902     raeburn  15269: sub course_types {
1.1165    raeburn  15270:     my @types = ('official','unofficial','community','textbook');
1.902     raeburn  15271:     my %typename = (
                   15272:                          official   => 'Official course',
                   15273:                          unofficial => 'Unofficial course',
                   15274:                          community  => 'Community',
1.1165    raeburn  15275:                          textbook   => 'Textbook course',
1.902     raeburn  15276:                    );
                   15277:     return (\@types,\%typename);
                   15278: }
                   15279: 
1.156     albertel 15280: sub icon {
                   15281:     my ($file)=@_;
1.505     albertel 15282:     my $curfext = lc((split(/\./,$file))[-1]);
1.168     albertel 15283:     my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif';
1.156     albertel 15284:     my $embstyle = &Apache::loncommon::fileembstyle($curfext);
1.168     albertel 15285:     if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) {
                   15286: 	if (-e  $Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
                   15287: 	          $Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   15288: 	            $curfext.".gif") {
                   15289: 	    $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   15290: 		$curfext.".gif";
                   15291: 	}
                   15292:     }
1.249     albertel 15293:     return &lonhttpdurl($iconname);
1.154     albertel 15294: } 
1.84      albertel 15295: 
1.575     albertel 15296: sub lonhttpdurl {
1.692     www      15297: #
                   15298: # Had been used for "small fry" static images on separate port 8080.
                   15299: # Modify here if lightweight http functionality desired again.
                   15300: # Currently eliminated due to increasing firewall issues.
                   15301: #
1.575     albertel 15302:     my ($url)=@_;
1.692     www      15303:     return $url;
1.215     albertel 15304: }
                   15305: 
1.213     albertel 15306: sub connection_aborted {
                   15307:     my ($r)=@_;
                   15308:     $r->print(" ");$r->rflush();
                   15309:     my $c = $r->connection;
                   15310:     return $c->aborted();
                   15311: }
                   15312: 
1.221     foxr     15313: #    Escapes strings that may have embedded 's that will be put into
1.222     foxr     15314: #    strings as 'strings'.
                   15315: sub escape_single {
1.221     foxr     15316:     my ($input) = @_;
1.223     albertel 15317:     $input =~ s/\\/\\\\/g;	# Escape the \'s..(must be first)>
1.221     foxr     15318:     $input =~ s/\'/\\\'/g;	# Esacpe the 's....
                   15319:     return $input;
                   15320: }
1.223     albertel 15321: 
1.222     foxr     15322: #  Same as escape_single, but escape's "'s  This 
                   15323: #  can be used for  "strings"
                   15324: sub escape_double {
                   15325:     my ($input) = @_;
                   15326:     $input =~ s/\\/\\\\/g;	# Escape the /'s..(must be first)>
                   15327:     $input =~ s/\"/\\\"/g;	# Esacpe the "s....
                   15328:     return $input;
                   15329: }
1.223     albertel 15330:  
1.222     foxr     15331: #   Escapes the last element of a full URL.
                   15332: sub escape_url {
                   15333:     my ($url)   = @_;
1.238     raeburn  15334:     my @urlslices = split(/\//, $url,-1);
1.369     www      15335:     my $lastitem = &escape(pop(@urlslices));
1.1203    raeburn  15336:     return &HTML::Entities::encode(join('/',@urlslices),"'").'/'.$lastitem;
1.222     foxr     15337: }
1.462     albertel 15338: 
1.820     raeburn  15339: sub compare_arrays {
                   15340:     my ($arrayref1,$arrayref2) = @_;
                   15341:     my (@difference,%count);
                   15342:     @difference = ();
                   15343:     %count = ();
                   15344:     if ((ref($arrayref1) eq 'ARRAY') && (ref($arrayref2) eq 'ARRAY')) {
                   15345:         foreach my $element (@{$arrayref1}, @{$arrayref2}) { $count{$element}++; }
                   15346:         foreach my $element (keys(%count)) {
                   15347:             if ($count{$element} == 1) {
                   15348:                 push(@difference,$element);
                   15349:             }
                   15350:         }
                   15351:     }
                   15352:     return @difference;
                   15353: }
                   15354: 
1.817     bisitz   15355: # -------------------------------------------------------- Initialize user login
1.462     albertel 15356: sub init_user_environment {
1.463     albertel 15357:     my ($r, $username, $domain, $authhost, $form, $args) = @_;
1.462     albertel 15358:     my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
                   15359: 
                   15360:     my $public=($username eq 'public' && $domain eq 'public');
                   15361: 
                   15362: # See if old ID present, if so, remove
                   15363: 
1.1062    raeburn  15364:     my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv);
1.462     albertel 15365:     my $now=time;
                   15366: 
                   15367:     if ($public) {
                   15368: 	my $max_public=100;
                   15369: 	my $oldest;
                   15370: 	my $oldest_time=0;
                   15371: 	for(my $next=1;$next<=$max_public;$next++) {
                   15372: 	    if (-e $lonids."/publicuser_$next.id") {
                   15373: 		my $mtime=(stat($lonids."/publicuser_$next.id"))[9];
                   15374: 		if ($mtime<$oldest_time || !$oldest_time) {
                   15375: 		    $oldest_time=$mtime;
                   15376: 		    $oldest=$next;
                   15377: 		}
                   15378: 	    } else {
                   15379: 		$cookie="publicuser_$next";
                   15380: 		last;
                   15381: 	    }
                   15382: 	}
                   15383: 	if (!$cookie) { $cookie="publicuser_$oldest"; }
                   15384:     } else {
1.463     albertel 15385: 	# if this isn't a robot, kill any existing non-robot sessions
                   15386: 	if (!$args->{'robot'}) {
                   15387: 	    opendir(DIR,$lonids);
                   15388: 	    while ($filename=readdir(DIR)) {
                   15389: 		if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
                   15390: 		    unlink($lonids.'/'.$filename);
                   15391: 		}
1.462     albertel 15392: 	    }
1.463     albertel 15393: 	    closedir(DIR);
1.1204    raeburn  15394: # If there is a undeleted lockfile for the user's paste buffer remove it.
                   15395:             my $namespace = 'nohist_courseeditor';
                   15396:             my $lockingkey = 'paste'."\0".'locked_num';
                   15397:             my %lockhash = &Apache::lonnet::get($namespace,[$lockingkey],
                   15398:                                                 $domain,$username);
                   15399:             if (exists($lockhash{$lockingkey})) {
                   15400:                 my $delresult = &Apache::lonnet::del($namespace,[$lockingkey],$domain,$username);
                   15401:                 unless ($delresult eq 'ok') {
                   15402:                     &Apache::lonnet::logthis("Failed to delete paste buffer locking key in $namespace for ".$username.":".$domain." Result was: $delresult");
                   15403:                 }
                   15404:             }
1.462     albertel 15405: 	}
                   15406: # Give them a new cookie
1.463     albertel 15407: 	my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
1.684     www      15408: 		                   : $now.$$.int(rand(10000)));
1.463     albertel 15409: 	$cookie="$username\_$id\_$domain\_$authhost";
1.462     albertel 15410:     
                   15411: # Initialize roles
                   15412: 
1.1062    raeburn  15413: 	($userroles,$firstaccenv,$timerintenv) = 
                   15414:             &Apache::lonnet::rolesinit($domain,$username,$authhost);
1.462     albertel 15415:     }
                   15416: # ------------------------------------ Check browser type and MathML capability
                   15417: 
1.1194    raeburn  15418:     my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,$clientunicode,
                   15419:         $clientos,$clientmobile,$clientinfo,$clientosversion) = &decode_user_agent($r);
1.462     albertel 15420: 
                   15421: # ------------------------------------------------------------- Get environment
                   15422: 
                   15423:     my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
                   15424:     my ($tmp) = keys(%userenv);
                   15425:     if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   15426:     } else {
                   15427: 	undef(%userenv);
                   15428:     }
                   15429:     if (($userenv{'interface'}) && (!$form->{'interface'})) {
                   15430: 	$form->{'interface'}=$userenv{'interface'};
                   15431:     }
                   15432:     if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
                   15433: 
                   15434: # --------------- Do not trust query string to be put directly into environment
1.817     bisitz   15435:     foreach my $option ('interface','localpath','localres') {
                   15436:         $form->{$option}=~s/[\n\r\=]//gs;
1.462     albertel 15437:     }
                   15438: # --------------------------------------------------------- Write first profile
                   15439: 
                   15440:     {
                   15441: 	my %initial_env = 
                   15442: 	    ("user.name"          => $username,
                   15443: 	     "user.domain"        => $domain,
                   15444: 	     "user.home"          => $authhost,
                   15445: 	     "browser.type"       => $clientbrowser,
                   15446: 	     "browser.version"    => $clientversion,
                   15447: 	     "browser.mathml"     => $clientmathml,
                   15448: 	     "browser.unicode"    => $clientunicode,
                   15449: 	     "browser.os"         => $clientos,
1.1137    raeburn  15450:              "browser.mobile"     => $clientmobile,
1.1141    raeburn  15451:              "browser.info"       => $clientinfo,
1.1194    raeburn  15452:              "browser.osversion"  => $clientosversion,
1.462     albertel 15453: 	     "server.domain"      => $Apache::lonnet::perlvar{'lonDefDomain'},
                   15454: 	     "request.course.fn"  => '',
                   15455: 	     "request.course.uri" => '',
                   15456: 	     "request.course.sec" => '',
                   15457: 	     "request.role"       => 'cm',
                   15458: 	     "request.role.adv"   => $env{'user.adv'},
                   15459: 	     "request.host"       => $ENV{'REMOTE_ADDR'},);
                   15460: 
                   15461:         if ($form->{'localpath'}) {
                   15462: 	    $initial_env{"browser.localpath"}  = $form->{'localpath'};
                   15463: 	    $initial_env{"browser.localres"}   = $form->{'localres'};
                   15464:         }
                   15465: 	
                   15466: 	if ($form->{'interface'}) {
                   15467: 	    $form->{'interface'}=~s/\W//gs;
                   15468: 	    $initial_env{"browser.interface"} = $form->{'interface'};
                   15469: 	    $env{'browser.interface'}=$form->{'interface'};
                   15470: 	}
                   15471: 
1.1157    raeburn  15472:         if ($form->{'iptoken'}) {
                   15473:             my $lonhost = $r->dir_config('lonHostID');
                   15474:             $initial_env{"user.noloadbalance"} = $lonhost;
                   15475:             $env{'user.noloadbalance'} = $lonhost;
                   15476:         }
                   15477: 
1.981     raeburn  15478:         my %is_adv = ( is_adv => $env{'user.adv'} );
1.1016    raeburn  15479:         my %domdef;
                   15480:         unless ($domain eq 'public') {
                   15481:             %domdef = &Apache::lonnet::get_domain_defaults($domain);
                   15482:         }
1.980     raeburn  15483: 
1.1081    raeburn  15484:         foreach my $tool ('aboutme','blog','webdav','portfolio') {
1.724     raeburn  15485:             $userenv{'availabletools.'.$tool} = 
1.980     raeburn  15486:                 &Apache::lonnet::usertools_access($username,$domain,$tool,'reload',
                   15487:                                                   undef,\%userenv,\%domdef,\%is_adv);
1.724     raeburn  15488:         }
                   15489: 
1.1165    raeburn  15490:         foreach my $crstype ('official','unofficial','community','textbook') {
1.765     raeburn  15491:             $userenv{'canrequest.'.$crstype} =
                   15492:                 &Apache::lonnet::usertools_access($username,$domain,$crstype,
1.980     raeburn  15493:                                                   'reload','requestcourses',
                   15494:                                                   \%userenv,\%domdef,\%is_adv);
1.765     raeburn  15495:         }
                   15496: 
1.1092    raeburn  15497:         $userenv{'canrequest.author'} =
                   15498:             &Apache::lonnet::usertools_access($username,$domain,'requestauthor',
                   15499:                                         'reload','requestauthor',
                   15500:                                         \%userenv,\%domdef,\%is_adv);
                   15501:         my %reqauthor = &Apache::lonnet::get('requestauthor',['author_status','author'],
                   15502:                                              $domain,$username);
                   15503:         my $reqstatus = $reqauthor{'author_status'};
                   15504:         if ($reqstatus eq 'approval' || $reqstatus eq 'approved') { 
                   15505:             if (ref($reqauthor{'author'}) eq 'HASH') {
                   15506:                 $userenv{'requestauthorqueued'} = $reqstatus.':'.
                   15507:                                                   $reqauthor{'author'}{'timestamp'};
                   15508:             }
                   15509:         }
                   15510: 
1.462     albertel 15511: 	$env{'user.environment'} = "$lonids/$cookie.id";
1.1062    raeburn  15512: 
1.462     albertel 15513: 	if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
                   15514: 		 &GDBM_WRCREAT(),0640)) {
                   15515: 	    &_add_to_env(\%disk_env,\%initial_env);
                   15516: 	    &_add_to_env(\%disk_env,\%userenv,'environment.');
                   15517: 	    &_add_to_env(\%disk_env,$userroles);
1.1062    raeburn  15518:             if (ref($firstaccenv) eq 'HASH') {
                   15519:                 &_add_to_env(\%disk_env,$firstaccenv);
                   15520:             }
                   15521:             if (ref($timerintenv) eq 'HASH') {
                   15522:                 &_add_to_env(\%disk_env,$timerintenv);
                   15523:             }
1.463     albertel 15524: 	    if (ref($args->{'extra_env'})) {
                   15525: 		&_add_to_env(\%disk_env,$args->{'extra_env'});
                   15526: 	    }
1.462     albertel 15527: 	    untie(%disk_env);
                   15528: 	} else {
1.705     tempelho 15529: 	    &Apache::lonnet::logthis("<span style=\"color:blue;\">WARNING: ".
                   15530: 			   'Could not create environment storage in lonauth: '.$!.'</span>');
1.462     albertel 15531: 	    return 'error: '.$!;
                   15532: 	}
                   15533:     }
                   15534:     $env{'request.role'}='cm';
                   15535:     $env{'request.role.adv'}=$env{'user.adv'};
                   15536:     $env{'browser.type'}=$clientbrowser;
                   15537: 
                   15538:     return $cookie;
                   15539: 
                   15540: }
                   15541: 
                   15542: sub _add_to_env {
                   15543:     my ($idf,$env_data,$prefix) = @_;
1.676     raeburn  15544:     if (ref($env_data) eq 'HASH') {
                   15545:         while (my ($key,$value) = each(%$env_data)) {
                   15546: 	    $idf->{$prefix.$key} = $value;
                   15547: 	    $env{$prefix.$key}   = $value;
                   15548:         }
1.462     albertel 15549:     }
                   15550: }
                   15551: 
1.685     tempelho 15552: # --- Get the symbolic name of a problem and the url
                   15553: sub get_symb {
                   15554:     my ($request,$silent) = @_;
1.726     raeburn  15555:     (my $url=$env{'form.url'}) =~ s-^https?\://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
1.685     tempelho 15556:     my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
                   15557:     if ($symb eq '') {
                   15558:         if (!$silent) {
1.1071    raeburn  15559:             if (ref($request)) { 
                   15560:                 $request->print("Unable to handle ambiguous references:$url:.");
                   15561:             }
1.685     tempelho 15562:             return ();
                   15563:         }
                   15564:     }
                   15565:     &Apache::lonenc::check_decrypt(\$symb);
                   15566:     return ($symb);
                   15567: }
                   15568: 
                   15569: # --------------------------------------------------------------Get annotation
                   15570: 
                   15571: sub get_annotation {
                   15572:     my ($symb,$enc) = @_;
                   15573: 
                   15574:     my $key = $symb;
                   15575:     if (!$enc) {
                   15576:         $key =
                   15577:             &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]);
                   15578:     }
                   15579:     my %annotation=&Apache::lonnet::get('nohist_annotations',[$key]);
                   15580:     return $annotation{$key};
                   15581: }
                   15582: 
                   15583: sub clean_symb {
1.731     raeburn  15584:     my ($symb,$delete_enc) = @_;
1.685     tempelho 15585: 
                   15586:     &Apache::lonenc::check_decrypt(\$symb);
                   15587:     my $enc = $env{'request.enc'};
1.731     raeburn  15588:     if ($delete_enc) {
1.730     raeburn  15589:         delete($env{'request.enc'});
                   15590:     }
1.685     tempelho 15591: 
                   15592:     return ($symb,$enc);
                   15593: }
1.462     albertel 15594: 
1.1181    raeburn  15595: ############################################################
                   15596: ############################################################
                   15597: 
                   15598: =pod
                   15599: 
                   15600: =head1 Routines for building display used to search for courses
                   15601: 
                   15602: 
                   15603: =over 4
                   15604: 
                   15605: =item * &build_filters()
                   15606: 
                   15607: Create markup for a table used to set filters to use when selecting
1.1182    raeburn  15608: courses in a domain.  Used by lonpickcourse.pm, lonmodifycourse.pm
                   15609: and quotacheck.pl
                   15610: 
1.1181    raeburn  15611: 
                   15612: Inputs:
                   15613: 
                   15614: filterlist - anonymous array of fields to include as potential filters 
                   15615: 
                   15616: crstype - course type
                   15617: 
                   15618: roleelement - fifth arg in selectcourse_link() populates fifth arg in javascript: opencrsbrowser() function, used
                   15619:               to pop-open a course selector (will contain "extra element"). 
                   15620: 
                   15621: multelement - if multiple course selections will be allowed, this will be a hidden form element: name: multiple; value: 1
                   15622: 
                   15623: filter - anonymous hash of criteria and their values
                   15624: 
                   15625: action - form action
                   15626: 
                   15627: numfiltersref - ref to scalar (count of number of elements in institutional codes -- e.g., 4 for year, semester, department, and number)
                   15628: 
1.1182    raeburn  15629: caller - caller context (e.g., set to 'modifycourse' when routine is called from lonmodifycourse.pm)
1.1181    raeburn  15630: 
                   15631: cloneruname - username of owner of new course who wants to clone
                   15632: 
                   15633: clonerudom - domain of owner of new course who wants to clone
                   15634: 
                   15635: typeelem - text to use for left column in row containing course type (i.e., Course, Community or Course/Community) 
                   15636: 
                   15637: codetitlesref - reference to array of titles of components in institutional codes (official courses)
                   15638: 
                   15639: codedom - domain
                   15640: 
                   15641: formname - value of form element named "form". 
                   15642: 
                   15643: fixeddom - domain, if fixed.
                   15644: 
                   15645: prevphase - value to assign to form element named "phase" when going back to the previous screen  
                   15646: 
                   15647: cnameelement - name of form element in form on opener page which will receive title of selected course 
                   15648: 
                   15649: cnumelement - name of form element in form on opener page which will receive courseID  of selected course
                   15650: 
                   15651: cdomelement - name of form element in form on opener page which will receive domain of selected course
                   15652: 
                   15653: setroles - includes access constraint identifier when setting a roles-based condition for acces to a portfolio file
                   15654: 
                   15655: clonetext - hidden form elements containing list of courses cloneable by intended course owner when DC creates a course
                   15656: 
                   15657: clonewarning - warning message about missing information for intended course owner when DC creates a course
                   15658: 
1.1182    raeburn  15659: 
1.1181    raeburn  15660: Returns: $output - HTML for display of search criteria, and hidden form elements.
                   15661: 
1.1182    raeburn  15662: 
1.1181    raeburn  15663: Side Effects: None
                   15664: 
                   15665: =cut
                   15666: 
                   15667: # ---------------------------------------------- search for courses based on last activity etc.
                   15668: 
                   15669: sub build_filters {
                   15670:     my ($filterlist,$crstype,$roleelement,$multelement,$filter,$action,
                   15671:         $numtitlesref,$caller,$cloneruname,$clonerudom,$typeelement,
                   15672:         $codetitlesref,$codedom,$formname,$fixeddom,$prevphase,
                   15673:         $cnameelement,$cnumelement,$cdomelement,$setroles,
                   15674:         $clonetext,$clonewarning) = @_;
1.1182    raeburn  15675:     my ($list,$jscript);
1.1181    raeburn  15676:     my $onchange = 'javascript:updateFilters(this)';
                   15677:     my ($domainselectform,$sincefilterform,$createdfilterform,
                   15678:         $ownerdomselectform,$persondomselectform,$instcodeform,
                   15679:         $typeselectform,$instcodetitle);
                   15680:     if ($formname eq '') {
                   15681:         $formname = $caller;
                   15682:     }
                   15683:     foreach my $item (@{$filterlist}) {
                   15684:         unless (($item eq 'descriptfilter') || ($item eq 'instcodefilter') ||
                   15685:                 ($item eq 'sincefilter') || ($item eq 'createdfilter')) {
                   15686:             if ($item eq 'domainfilter') {
                   15687:                 $filter->{$item} = &LONCAPA::clean_domain($filter->{$item});
                   15688:             } elsif ($item eq 'coursefilter') {
                   15689:                 $filter->{$item} = &LONCAPA::clean_courseid($filter->{$item});
                   15690:             } elsif ($item eq 'ownerfilter') {
                   15691:                 $filter->{$item} = &LONCAPA::clean_username($filter->{$item});
                   15692:             } elsif ($item eq 'ownerdomfilter') {
                   15693:                 $filter->{'ownerdomfilter'} =
                   15694:                     &LONCAPA::clean_domain($filter->{$item});
                   15695:                 $ownerdomselectform = &select_dom_form($filter->{'ownerdomfilter'},
                   15696:                                                        'ownerdomfilter',1);
                   15697:             } elsif ($item eq 'personfilter') {
                   15698:                 $filter->{$item} = &LONCAPA::clean_username($filter->{$item});
                   15699:             } elsif ($item eq 'persondomfilter') {
                   15700:                 $persondomselectform = &select_dom_form($filter->{'persondomfilter'},
                   15701:                                                         'persondomfilter',1);
                   15702:             } else {
                   15703:                 $filter->{$item} =~ s/\W//g;
                   15704:             }
                   15705:             if (!$filter->{$item}) {
                   15706:                 $filter->{$item} = '';
                   15707:             }
                   15708:         }
                   15709:         if ($item eq 'domainfilter') {
                   15710:             my $allow_blank = 1;
                   15711:             if ($formname eq 'portform') {
                   15712:                 $allow_blank=0;
                   15713:             } elsif ($formname eq 'studentform') {
                   15714:                 $allow_blank=0;
                   15715:             }
                   15716:             if ($fixeddom) {
                   15717:                 $domainselectform = '<input type="hidden" name="domainfilter"'.
                   15718:                                     ' value="'.$codedom.'" />'.
                   15719:                                     &Apache::lonnet::domain($codedom,'description');
                   15720:             } else {
                   15721:                 $domainselectform = &select_dom_form($filter->{$item},
                   15722:                                                      'domainfilter',
                   15723:                                                       $allow_blank,'',$onchange);
                   15724:             }
                   15725:         } else {
                   15726:             $list->{$item} = &HTML::Entities::encode($filter->{$item},'<>&"');
                   15727:         }
                   15728:     }
                   15729: 
                   15730:     # last course activity filter and selection
                   15731:     $sincefilterform = &timebased_select_form('sincefilter',$filter);
                   15732: 
                   15733:     # course created filter and selection
                   15734:     if (exists($filter->{'createdfilter'})) {
                   15735:         $createdfilterform = &timebased_select_form('createdfilter',$filter);
                   15736:     }
                   15737: 
                   15738:     my %lt = &Apache::lonlocal::texthash(
                   15739:                 'cac' => "$crstype Activity",
                   15740:                 'ccr' => "$crstype Created",
                   15741:                 'cde' => "$crstype Title",
                   15742:                 'cdo' => "$crstype Domain",
                   15743:                 'ins' => 'Institutional Code',
                   15744:                 'inc' => 'Institutional Categorization',
                   15745:                 'cow' => "$crstype Owner/Co-owner",
                   15746:                 'cop' => "$crstype Personnel Includes",
                   15747:                 'cog' => 'Type',
                   15748:              );
                   15749: 
                   15750:     if (($formname eq 'ccrs') || ($formname eq 'requestcrs')) {
                   15751:         my $typeval = 'Course';
                   15752:         if ($crstype eq 'Community') {
                   15753:             $typeval = 'Community';
                   15754:         }
                   15755:         $typeselectform = '<input type="hidden" name="type" value="'.$typeval.'" />';
                   15756:     } else {
                   15757:         $typeselectform =  '<select name="type" size="1"';
                   15758:         if ($onchange) {
                   15759:             $typeselectform .= ' onchange="'.$onchange.'"';
                   15760:         }
                   15761:         $typeselectform .= '>'."\n";
                   15762:         foreach my $posstype ('Course','Community') {
                   15763:             $typeselectform.='<option value="'.$posstype.'"'.
                   15764:                 ($posstype eq $crstype ? ' selected="selected" ' : ''). ">".&mt($posstype)."</option>\n";
                   15765:         }
                   15766:         $typeselectform.="</select>";
                   15767:     }
                   15768: 
                   15769:     my ($cloneableonlyform,$cloneabletitle);
                   15770:     if (exists($filter->{'cloneableonly'})) {
                   15771:         my $cloneableon = '';
                   15772:         my $cloneableoff = ' checked="checked"';
                   15773:         if ($filter->{'cloneableonly'}) {
                   15774:             $cloneableon = $cloneableoff;
                   15775:             $cloneableoff = '';
                   15776:         }
                   15777:         $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>';
                   15778:         if ($formname eq 'ccrs') {
1.1187    bisitz   15779:             $cloneabletitle = &mt('Cloneable for [_1]',$cloneruname.':'.$clonerudom);
1.1181    raeburn  15780:         } else {
                   15781:             $cloneabletitle = &mt('Cloneable by you');
                   15782:         }
                   15783:     }
                   15784:     my $officialjs;
                   15785:     if ($crstype eq 'Course') {
                   15786:         if (exists($filter->{'instcodefilter'})) {
1.1182    raeburn  15787: #            if (($fixeddom) || ($formname eq 'requestcrs') ||
                   15788: #                ($formname eq 'modifycourse') || ($formname eq 'filterpicker')) {
                   15789:             if ($codedom) { 
1.1181    raeburn  15790:                 $officialjs = 1;
                   15791:                 ($instcodeform,$jscript,$$numtitlesref) =
                   15792:                     &Apache::courseclassifier::instcode_selectors($codedom,'filterpicker',
                   15793:                                                                   $officialjs,$codetitlesref);
                   15794:                 if ($jscript) {
1.1182    raeburn  15795:                     $jscript = '<script type="text/javascript">'."\n".
                   15796:                                '// <![CDATA['."\n".
                   15797:                                $jscript."\n".
                   15798:                                '// ]]>'."\n".
                   15799:                                '</script>'."\n";
1.1181    raeburn  15800:                 }
                   15801:             }
                   15802:             if ($instcodeform eq '') {
                   15803:                 $instcodeform =
                   15804:                     '<input type="text" name="instcodefilter" size="10" value="'.
                   15805:                     $list->{'instcodefilter'}.'" />';
                   15806:                 $instcodetitle = $lt{'ins'};
                   15807:             } else {
                   15808:                 $instcodetitle = $lt{'inc'};
                   15809:             }
                   15810:             if ($fixeddom) {
                   15811:                 $instcodetitle .= '<br />('.$codedom.')';
                   15812:             }
                   15813:         }
                   15814:     }
                   15815:     my $output = qq|
                   15816: <form method="post" name="filterpicker" action="$action">
                   15817: <input type="hidden" name="form" value="$formname" />
                   15818: |;
                   15819:     if ($formname eq 'modifycourse') {
                   15820:         $output .= '<input type="hidden" name="phase" value="courselist" />'."\n".
                   15821:                    '<input type="hidden" name="prevphase" value="'.
                   15822:                    $prevphase.'" />'."\n";
1.1198    musolffc 15823:     } elsif ($formname eq 'quotacheck') {
                   15824:         $output .= qq|
                   15825: <input type="hidden" name="sortby" value="" />
                   15826: <input type="hidden" name="sortorder" value="" />
                   15827: |;
                   15828:     } else {
1.1181    raeburn  15829:         my $name_input;
                   15830:         if ($cnameelement ne '') {
                   15831:             $name_input = '<input type="hidden" name="cnameelement" value="'.
                   15832:                           $cnameelement.'" />';
                   15833:         }
                   15834:         $output .= qq|
1.1182    raeburn  15835: <input type="hidden" name="cnumelement" value="$cnumelement" />
                   15836: <input type="hidden" name="cdomelement" value="$cdomelement" />
1.1181    raeburn  15837: $name_input
                   15838: $roleelement
                   15839: $multelement
                   15840: $typeelement
                   15841: |;
                   15842:         if ($formname eq 'portform') {
                   15843:             $output .= '<input type="hidden" name="setroles" value="'.$setroles.'" />'."\n";
                   15844:         }
                   15845:     }
                   15846:     if ($fixeddom) {
                   15847:         $output .= '<input type="hidden" name="fixeddom" value="'.$fixeddom.'" />'."\n";
                   15848:     }
                   15849:     $output .= "<br />\n".&Apache::lonhtmlcommon::start_pick_box();
                   15850:     if ($sincefilterform) {
                   15851:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cac'})
                   15852:                   .$sincefilterform
                   15853:                   .&Apache::lonhtmlcommon::row_closure();
                   15854:     }
                   15855:     if ($createdfilterform) {
                   15856:         $output .= &Apache::lonhtmlcommon::row_title($lt{'ccr'})
                   15857:                   .$createdfilterform
                   15858:                   .&Apache::lonhtmlcommon::row_closure();
                   15859:     }
                   15860:     if ($domainselectform) {
                   15861:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cdo'})
                   15862:                   .$domainselectform
                   15863:                   .&Apache::lonhtmlcommon::row_closure();
                   15864:     }
                   15865:     if ($typeselectform) {
                   15866:         if (($formname eq 'ccrs') || ($formname eq 'requestcrs')) {
                   15867:             $output .= $typeselectform;
                   15868:         } else {
                   15869:             $output .= &Apache::lonhtmlcommon::row_title($lt{'cog'})
                   15870:                       .$typeselectform
                   15871:                       .&Apache::lonhtmlcommon::row_closure();
                   15872:         }
                   15873:     }
                   15874:     if ($instcodeform) {
                   15875:         $output .= &Apache::lonhtmlcommon::row_title($instcodetitle)
                   15876:                   .$instcodeform
                   15877:                   .&Apache::lonhtmlcommon::row_closure();
                   15878:     }
                   15879:     if (exists($filter->{'ownerfilter'})) {
                   15880:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cow'}).
                   15881:                    '<table><tr><td>'.&mt('Username').'<br />'.
                   15882:                    '<input type="text" name="ownerfilter" size="20" value="'.
                   15883:                    $list->{'ownerfilter'}.'" /></td><td>'.&mt('Domain').'<br />'.
                   15884:                    $ownerdomselectform.'</td></tr></table>'.
                   15885:                    &Apache::lonhtmlcommon::row_closure();
                   15886:     }
                   15887:     if (exists($filter->{'personfilter'})) {
                   15888:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cop'}).
                   15889:                    '<table><tr><td>'.&mt('Username').'<br />'.
                   15890:                    '<input type="text" name="personfilter" size="20" value="'.
                   15891:                    $list->{'personfilter'}.'" /></td><td>'.&mt('Domain').'<br />'.
                   15892:                    $persondomselectform.'</td></tr></table>'.
                   15893:                    &Apache::lonhtmlcommon::row_closure();
                   15894:     }
                   15895:     if (exists($filter->{'coursefilter'})) {
                   15896:         $output .= &Apache::lonhtmlcommon::row_title(&mt('LON-CAPA course ID'))
                   15897:                   .'<input type="text" name="coursefilter" size="25" value="'
                   15898:                   .$list->{'coursefilter'}.'" />'
                   15899:                   .&Apache::lonhtmlcommon::row_closure();
                   15900:     }
                   15901:     if ($cloneableonlyform) {
                   15902:         $output .= &Apache::lonhtmlcommon::row_title($cloneabletitle).
                   15903:                    $cloneableonlyform.&Apache::lonhtmlcommon::row_closure();
                   15904:     }
                   15905:     if (exists($filter->{'descriptfilter'})) {
                   15906:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cde'})
                   15907:                   .'<input type="text" name="descriptfilter" size="40" value="'
                   15908:                   .$list->{'descriptfilter'}.'" />'
                   15909:                   .&Apache::lonhtmlcommon::row_closure(1);
                   15910:     }
                   15911:     $output .= &Apache::lonhtmlcommon::end_pick_box().'<p>'.$clonetext."\n".
                   15912:                '<input type="hidden" name="updater" value="" />'."\n".
                   15913:                '<input type="submit" name="gosearch" value="'.
                   15914:                &mt('Search').'" /></p>'."\n".'</form>'."\n".'<hr />'."\n";
                   15915:     return $jscript.$clonewarning.$output;
                   15916: }
                   15917: 
                   15918: =pod 
                   15919: 
                   15920: =item * &timebased_select_form()
                   15921: 
1.1182    raeburn  15922: Create markup for a dropdown list used to select a time-based
1.1181    raeburn  15923: filter e.g., Course Activity, Course Created, when searching for courses
                   15924: or communities
                   15925: 
                   15926: Inputs:
                   15927: 
                   15928: item - name of form element (sincefilter or createdfilter)
                   15929: 
                   15930: filter - anonymous hash of criteria and their values
                   15931: 
                   15932: Returns: HTML for a select box contained a blank, then six time selections,
                   15933:          with value set in incoming form variables currently selected. 
                   15934: 
                   15935: Side Effects: None
                   15936: 
                   15937: =cut
                   15938: 
                   15939: sub timebased_select_form {
                   15940:     my ($item,$filter) = @_;
                   15941:     if (ref($filter) eq 'HASH') {
                   15942:         $filter->{$item} =~ s/[^\d-]//g;
                   15943:         if (!$filter->{$item}) { $filter->{$item}=-1; }
                   15944:         return &select_form(
                   15945:                             $filter->{$item},
                   15946:                             $item,
                   15947:                             {      '-1' => '',
                   15948:                                 '86400' => &mt('today'),
                   15949:                                '604800' => &mt('last week'),
                   15950:                               '2592000' => &mt('last month'),
                   15951:                               '7776000' => &mt('last three months'),
                   15952:                              '15552000' => &mt('last six months'),
                   15953:                              '31104000' => &mt('last year'),
                   15954:                     'select_form_order' =>
                   15955:                            ['-1','86400','604800','2592000','7776000',
                   15956:                             '15552000','31104000']});
                   15957:     }
                   15958: }
                   15959: 
                   15960: =pod
                   15961: 
                   15962: =item * &js_changer()
                   15963: 
                   15964: Create script tag containing Javascript used to submit course search form
1.1183    raeburn  15965: when course type or domain is changed, and also to hide 'Searching ...' on
                   15966: page load completion for page showing search result.
1.1181    raeburn  15967: 
                   15968: Inputs: None
                   15969: 
1.1183    raeburn  15970: Returns: markup containing updateFilters() and hideSearching() javascript functions. 
1.1181    raeburn  15971: 
                   15972: Side Effects: None
                   15973: 
                   15974: =cut
                   15975: 
                   15976: sub js_changer {
                   15977:     return <<ENDJS;
                   15978: <script type="text/javascript">
                   15979: // <![CDATA[
                   15980: function updateFilters(caller) {
                   15981:     if (typeof(caller) != "undefined") {
                   15982:         document.filterpicker.updater.value = caller.name;
                   15983:     }
                   15984:     document.filterpicker.submit();
                   15985: }
1.1183    raeburn  15986: 
                   15987: function hideSearching() {
                   15988:     if (document.getElementById('searching')) {
                   15989:         document.getElementById('searching').style.display = 'none';
                   15990:     }
                   15991:     return;
                   15992: }
                   15993: 
1.1181    raeburn  15994: // ]]>
                   15995: </script>
                   15996: 
                   15997: ENDJS
                   15998: }
                   15999: 
                   16000: =pod
                   16001: 
1.1182    raeburn  16002: =item * &search_courses()
                   16003: 
                   16004: Process selected filters form course search form and pass to lonnet::courseiddump
                   16005: to retrieve a hash for which keys are courseIDs which match the selected filters.
                   16006: 
                   16007: Inputs:
                   16008: 
                   16009: dom - domain being searched 
                   16010: 
                   16011: type - course type ('Course' or 'Community' or '.' if any).
                   16012: 
                   16013: filter - anonymous hash of criteria and their values
                   16014: 
                   16015: numtitles - for institutional codes - number of categories
                   16016: 
                   16017: cloneruname - optional username of new course owner
                   16018: 
                   16019: clonerudom - optional domain of new course owner
                   16020: 
1.1221    raeburn  16021: domcloner - optional "domcloner" flag; has value=1 if user has ccc priv in domain being filtered by, 
1.1182    raeburn  16022:             (used when DC is using course creation form)
                   16023: 
                   16024: codetitles - reference to array of titles of components in institutional codes (official courses).
                   16025: 
1.1221    raeburn  16026: cc_clone - escaped comma separated list of courses for which course cloner has active CC role
                   16027:            (and so can clone automatically)
                   16028: 
                   16029: reqcrsdom - domain of new course, where search_courses is used to identify potential courses to clone
                   16030: 
                   16031: reqinstcode - institutional code of new course, where search_courses is used to identify potential 
                   16032:               courses to clone 
1.1182    raeburn  16033: 
                   16034: Returns: %courses - hash of courses satisfying search criteria, keys = course IDs, values are corresponding colon-separated escaped description, institutional code, owner and type.
                   16035: 
                   16036: 
                   16037: Side Effects: None
                   16038: 
                   16039: =cut
                   16040: 
                   16041: 
                   16042: sub search_courses {
1.1221    raeburn  16043:     my ($dom,$type,$filter,$numtitles,$cloneruname,$clonerudom,$domcloner,$codetitles,
                   16044:         $cc_clone,$reqcrsdom,$reqinstcode) = @_;
1.1182    raeburn  16045:     my (%courses,%showcourses,$cloner);
                   16046:     if (($filter->{'ownerfilter'} ne '') ||
                   16047:         ($filter->{'ownerdomfilter'} ne '')) {
                   16048:         $filter->{'combownerfilter'} = $filter->{'ownerfilter'}.':'.
                   16049:                                        $filter->{'ownerdomfilter'};
                   16050:     }
                   16051:     foreach my $item ('descriptfilter','coursefilter','combownerfilter') {
                   16052:         if (!$filter->{$item}) {
                   16053:             $filter->{$item}='.';
                   16054:         }
                   16055:     }
                   16056:     my $now = time;
                   16057:     my $timefilter =
                   16058:        ($filter->{'sincefilter'}==-1?1:$now-$filter->{'sincefilter'});
                   16059:     my ($createdbefore,$createdafter);
                   16060:     if (($filter->{'createdfilter'} ne '') && ($filter->{'createdfilter'} !=-1)) {
                   16061:         $createdbefore = $now;
                   16062:         $createdafter = $now-$filter->{'createdfilter'};
                   16063:     }
                   16064:     my ($instcodefilter,$regexpok);
                   16065:     if ($numtitles) {
                   16066:         if ($env{'form.official'} eq 'on') {
                   16067:             $instcodefilter =
                   16068:                 &Apache::courseclassifier::instcode_search_str($dom,$numtitles,$codetitles);
                   16069:             $regexpok = 1;
                   16070:         } elsif ($env{'form.official'} eq 'off') {
                   16071:             $instcodefilter = &Apache::courseclassifier::instcode_search_str($dom,$numtitles,$codetitles);
                   16072:             unless ($instcodefilter eq '') {
                   16073:                 $regexpok = -1;
                   16074:             }
                   16075:         }
                   16076:     } else {
                   16077:         $instcodefilter = $filter->{'instcodefilter'};
                   16078:     }
                   16079:     if ($instcodefilter eq '') { $instcodefilter = '.'; }
                   16080:     if ($type eq '') { $type = '.'; }
                   16081: 
                   16082:     if (($clonerudom ne '') && ($cloneruname ne '')) {
                   16083:         $cloner = $cloneruname.':'.$clonerudom;
                   16084:     }
                   16085:     %courses = &Apache::lonnet::courseiddump($dom,
                   16086:                                              $filter->{'descriptfilter'},
                   16087:                                              $timefilter,
                   16088:                                              $instcodefilter,
                   16089:                                              $filter->{'combownerfilter'},
                   16090:                                              $filter->{'coursefilter'},
                   16091:                                              undef,undef,$type,$regexpok,undef,undef,
1.1221    raeburn  16092:                                              undef,undef,$cloner,$cc_clone,
1.1182    raeburn  16093:                                              $filter->{'cloneableonly'},
                   16094:                                              $createdbefore,$createdafter,undef,
1.1221    raeburn  16095:                                              $domcloner,undef,$reqcrsdom,$reqinstcode);
1.1182    raeburn  16096:     if (($filter->{'personfilter'} ne '') && ($filter->{'persondomfilter'} ne '')) {
                   16097:         my $ccrole;
                   16098:         if ($type eq 'Community') {
                   16099:             $ccrole = 'co';
                   16100:         } else {
                   16101:             $ccrole = 'cc';
                   16102:         }
                   16103:         my %rolehash = &Apache::lonnet::get_my_roles($filter->{'personfilter'},
                   16104:                                                      $filter->{'persondomfilter'},
                   16105:                                                      'userroles',undef,
                   16106:                                                      [$ccrole,'in','ad','ep','ta','cr'],
                   16107:                                                      $dom);
                   16108:         foreach my $role (keys(%rolehash)) {
                   16109:             my ($cnum,$cdom,$courserole) = split(':',$role);
                   16110:             my $cid = $cdom.'_'.$cnum;
                   16111:             if (exists($courses{$cid})) {
                   16112:                 if (ref($courses{$cid}) eq 'HASH') {
                   16113:                     if (ref($courses{$cid}{roles}) eq 'ARRAY') {
                   16114:                         if (!grep(/^\Q$courserole\E$/,@{$courses{$cid}{roles}})) {
                   16115:                             push (@{$courses{$cid}{roles}},$courserole);
                   16116:                         }
                   16117:                     } else {
                   16118:                         $courses{$cid}{roles} = [$courserole];
                   16119:                     }
                   16120:                     $showcourses{$cid} = $courses{$cid};
                   16121:                 }
                   16122:             }
                   16123:         }
                   16124:         %courses = %showcourses;
                   16125:     }
                   16126:     return %courses;
                   16127: }
                   16128: 
                   16129: =pod
                   16130: 
1.1181    raeburn  16131: =back
                   16132: 
1.1207    raeburn  16133: =head1 Routines for version requirements for current course.
                   16134: 
                   16135: =over 4
                   16136: 
                   16137: =item * &check_release_required()
                   16138: 
                   16139: Compares required LON-CAPA version with version on server, and
                   16140: if required version is newer looks for a server with the required version.
                   16141: 
                   16142: Looks first at servers in user's owen domain; if none suitable, looks at
                   16143: servers in course's domain are permitted to host sessions for user's domain.
                   16144: 
                   16145: Inputs:
                   16146: 
                   16147: $loncaparev - Version on current server (format: Major.Minor.Subrelease-datestamp)
                   16148: 
                   16149: $courseid - Course ID of current course
                   16150: 
                   16151: $rolecode - User's current role in course (for switchserver query string).
                   16152: 
                   16153: $required - LON-CAPA version needed by course (format: Major.Minor).
                   16154: 
                   16155: 
                   16156: Returns:
                   16157: 
                   16158: $switchserver - query string tp append to /adm/switchserver call (if 
                   16159:                 current server's LON-CAPA version is too old. 
                   16160: 
                   16161: $warning - Message is displayed if no suitable server could be found.
                   16162: 
                   16163: =cut
                   16164: 
                   16165: sub check_release_required {
                   16166:     my ($loncaparev,$courseid,$rolecode,$required) = @_;
                   16167:     my ($switchserver,$warning);
                   16168:     if ($required ne '') {
                   16169:         my ($reqdmajor,$reqdminor) = ($required =~ /^(\d+)\.(\d+)$/);
                   16170:         my ($major,$minor) = ($loncaparev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
                   16171:         if ($reqdmajor ne '' && $reqdminor ne '') {
                   16172:             my $otherserver;
                   16173:             if (($major eq '' && $minor eq '') ||
                   16174:                 (($reqdmajor > $major) || (($reqdmajor == $major) && ($reqdminor > $minor)))) {
                   16175:                 my ($userdomserver) = &Apache::lonnet::choose_server($env{'user.domain'},undef,$required,1);
                   16176:                 my $switchlcrev =
                   16177:                     &Apache::lonnet::get_server_loncaparev($env{'user.domain'},
                   16178:                                                            $userdomserver);
                   16179:                 my ($swmajor,$swminor) = ($switchlcrev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
                   16180:                 if (($swmajor eq '' && $swminor eq '') || ($reqdmajor > $swmajor) ||
                   16181:                     (($reqdmajor == $swmajor) && ($reqdminor > $swminor))) {
                   16182:                     my $cdom = $env{'course.'.$courseid.'.domain'};
                   16183:                     if ($cdom ne $env{'user.domain'}) {
                   16184:                         my ($coursedomserver,$coursehostname) = &Apache::lonnet::choose_server($cdom,undef,$required,1);
                   16185:                         my $serverhomeID = &Apache::lonnet::get_server_homeID($coursehostname);
                   16186:                         my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
                   16187:                         my %defdomdefaults = &Apache::lonnet::get_domain_defaults($serverhomedom);
                   16188:                         my %udomdefaults = &Apache::lonnet::get_domain_defaults($env{'user.domain'});
                   16189:                         my $remoterev = &Apache::lonnet::get_server_loncaparev($serverhomedom,$coursedomserver);
                   16190:                         my $canhost =
                   16191:                             &Apache::lonnet::can_host_session($env{'user.domain'},
                   16192:                                                               $coursedomserver,
                   16193:                                                               $remoterev,
                   16194:                                                               $udomdefaults{'remotesessions'},
                   16195:                                                               $defdomdefaults{'hostedsessions'});
                   16196: 
                   16197:                         if ($canhost) {
                   16198:                             $otherserver = $coursedomserver;
                   16199:                         } else {
                   16200:                             $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.");
                   16201:                         }
                   16202:                     } else {
                   16203:                         $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).");
                   16204:                     }
                   16205:                 } else {
                   16206:                     $otherserver = $userdomserver;
                   16207:                 }
                   16208:             }
                   16209:             if ($otherserver ne '') {
                   16210:                 $switchserver = 'otherserver='.$otherserver.'&amp;role='.$rolecode;
                   16211:             }
                   16212:         }
                   16213:     }
                   16214:     return ($switchserver,$warning);
                   16215: }
                   16216: 
                   16217: =pod
                   16218: 
                   16219: =item * &check_release_result()
                   16220: 
                   16221: Inputs:
                   16222: 
                   16223: $switchwarning - Warning message if no suitable server found to host session.
                   16224: 
                   16225: $switchserver - query string to append to /adm/switchserver containing lonHostID
                   16226:                 and current role.
                   16227: 
                   16228: Returns: HTML to display with information about requirement to switch server.
                   16229:          Either displaying warning with link to Roles/Courses screen or
                   16230:          display link to switchserver.
                   16231: 
1.1181    raeburn  16232: =cut
                   16233: 
1.1207    raeburn  16234: sub check_release_result {
                   16235:     my ($switchwarning,$switchserver) = @_;
                   16236:     my $output = &start_page('Selected course unavailable on this server').
                   16237:                  '<p class="LC_warning">';
                   16238:     if ($switchwarning) {
                   16239:         $output .= $switchwarning.'<br /><a href="/adm/roles">';
                   16240:         if (&show_course()) {
                   16241:             $output .= &mt('Display courses');
                   16242:         } else {
                   16243:             $output .= &mt('Display roles');
                   16244:         }
                   16245:         $output .= '</a>';
                   16246:     } elsif ($switchserver) {
                   16247:         $output .= &mt('This course requires a newer version of LON-CAPA than is installed on this server.').
                   16248:                    '<br />'.
                   16249:                    '<a href="/adm/switchserver?'.$switchserver.'">'.
                   16250:                    &mt('Switch Server').
                   16251:                    '</a>';
                   16252:     }
                   16253:     $output .= '</p>'.&end_page();
                   16254:     return $output;
                   16255: }
                   16256: 
                   16257: =pod
                   16258: 
                   16259: =item * &needs_coursereinit()
                   16260: 
                   16261: Determine if course contents stored for user's session needs to be
                   16262: refreshed, because content has changed since "Big Hash" last tied.
                   16263: 
                   16264: Check for change is made if time last checked is more than 10 minutes ago
                   16265: (by default).
                   16266: 
                   16267: Inputs:
                   16268: 
                   16269: $loncaparev - Version on current server (format: Major.Minor.Subrelease-datestamp)
                   16270: 
                   16271: $interval (optional) - Time which may elapse (in s) between last check for content
                   16272:                        change in current course. (default: 600 s).  
                   16273: 
                   16274: Returns: an array; first element is:
                   16275: 
                   16276: =over 4
                   16277: 
                   16278: 'switch' - if content updates mean user's session
                   16279:            needs to be switched to a server running a newer LON-CAPA version
                   16280:  
                   16281: 'update' - if course session needs to be refreshed (i.e., Big Hash needs to be reloaded)
                   16282:            on current server hosting user's session                
                   16283: 
                   16284: ''       - if no action required.
                   16285: 
                   16286: =back
                   16287: 
                   16288: If first item element is 'switch':
                   16289: 
                   16290: second item is $switchwarning - Warning message if no suitable server found to host session. 
                   16291: 
                   16292: third item is $switchserver - query string to append to /adm/switchserver containing lonHostID
                   16293:                               and current role. 
                   16294: 
                   16295: otherwise: no other elements returned.
                   16296: 
                   16297: =back
                   16298: 
                   16299: =cut
                   16300: 
                   16301: sub needs_coursereinit {
                   16302:     my ($loncaparev,$interval) = @_;
                   16303:     return() unless ($env{'request.course.id'} && $env{'request.course.tied'});
                   16304:     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   16305:     my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
                   16306:     my $now = time;
                   16307:     if ($interval eq '') {
                   16308:         $interval = 600;
                   16309:     }
                   16310:     if (($now-$env{'request.course.timechecked'})>$interval) {
                   16311:         my $lastchange = &Apache::lonnet::get_coursechange($cdom,$cnum);
                   16312:         &Apache::lonnet::appenv({'request.course.timechecked'=>$now});
                   16313:         if ($lastchange > $env{'request.course.tied'}) {
                   16314:             my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
                   16315:             if ($curr_reqd_hash{'internal.releaserequired'} ne '') {
                   16316:                 my $required = $env{'course.'.$cdom.'_'.$cnum.'.internal.releaserequired'};
                   16317:                 if ($curr_reqd_hash{'internal.releaserequired'} ne $required) {
                   16318:                     &Apache::lonnet::appenv({'course.'.$cdom.'_'.$cnum.'.internal.releaserequired' =>
                   16319:                                              $curr_reqd_hash{'internal.releaserequired'}});
                   16320:                     my ($switchserver,$switchwarning) =
                   16321:                         &check_release_required($loncaparev,$cdom.'_'.$cnum,$env{'request.role'},
                   16322:                                                 $curr_reqd_hash{'internal.releaserequired'});
                   16323:                     if ($switchwarning ne '' || $switchserver ne '') {
                   16324:                         return ('switch',$switchwarning,$switchserver);
                   16325:                     }
                   16326:                 }
                   16327:             }
                   16328:             return ('update');
                   16329:         }
                   16330:     }
                   16331:     return ();
                   16332: }
1.1181    raeburn  16333: 
1.1083    raeburn  16334: sub update_content_constraints {
                   16335:     my ($cdom,$cnum,$chome,$cid) = @_;
                   16336:     my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
                   16337:     my ($reqdmajor,$reqdminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'});
                   16338:     my %checkresponsetypes;
                   16339:     foreach my $key (keys(%Apache::lonnet::needsrelease)) {
1.1219    raeburn  16340:         my ($item,$name,$value,$valmatch) = split(/:/,$key);
1.1083    raeburn  16341:         if ($item eq 'resourcetag') {
                   16342:             if ($name eq 'responsetype') {
                   16343:                 $checkresponsetypes{$value} = $Apache::lonnet::needsrelease{$key}
                   16344:             }
                   16345:         }
                   16346:     }
                   16347:     my $navmap = Apache::lonnavmaps::navmap->new();
                   16348:     if (defined($navmap)) {
                   16349:         my %allresponses;
                   16350:         foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_problem() },1,0)) {
                   16351:             my %responses = $res->responseTypes();
                   16352:             foreach my $key (keys(%responses)) {
                   16353:                 next unless(exists($checkresponsetypes{$key}));
                   16354:                 $allresponses{$key} += $responses{$key};
                   16355:             }
                   16356:         }
                   16357:         foreach my $key (keys(%allresponses)) {
                   16358:             my ($major,$minor) = split(/\./,$checkresponsetypes{$key});
                   16359:             if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
                   16360:                 ($reqdmajor,$reqdminor) = ($major,$minor);
                   16361:             }
                   16362:         }
                   16363:         undef($navmap);
                   16364:     }
                   16365:     unless (($reqdmajor eq '') && ($reqdminor eq '')) {
                   16366:         &Apache::lonnet::update_released_required($reqdmajor.'.'.$reqdminor,$cdom,$cnum,$chome,$cid);
                   16367:     }
                   16368:     return;
                   16369: }
                   16370: 
1.1110    raeburn  16371: sub allmaps_incourse {
                   16372:     my ($cdom,$cnum,$chome,$cid) = @_;
                   16373:     if ($cdom eq '' || $cnum eq '' || $chome eq '' || $cid eq '') {
                   16374:         $cid = $env{'request.course.id'};
                   16375:         $cdom = $env{'course.'.$cid.'.domain'};
                   16376:         $cnum = $env{'course.'.$cid.'.num'};
                   16377:         $chome = $env{'course.'.$cid.'.home'};
                   16378:     }
                   16379:     my %allmaps = ();
                   16380:     my $lastchange =
                   16381:         &Apache::lonnet::get_coursechange($cdom,$cnum);
                   16382:     if ($lastchange > $env{'request.course.tied'}) {
                   16383:         my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum");
                   16384:         unless ($ferr) {
                   16385:             &update_content_constraints($cdom,$cnum,$chome,$cid);
                   16386:         }
                   16387:     }
                   16388:     my $navmap = Apache::lonnavmaps::navmap->new();
                   16389:     if (defined($navmap)) {
                   16390:         foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_map() },1,0,1)) {
                   16391:             $allmaps{$res->src()} = 1;
                   16392:         }
                   16393:     }
                   16394:     return \%allmaps;
                   16395: }
                   16396: 
1.1083    raeburn  16397: sub parse_supplemental_title {
                   16398:     my ($title) = @_;
                   16399: 
                   16400:     my ($foldertitle,$renametitle);
                   16401:     if ($title =~ /&amp;&amp;&amp;/) {
                   16402:         $title = &HTML::Entites::decode($title);
                   16403:     }
                   16404:     if ($title =~ m/^(\d+)___&&&___($match_username)___&&&___($match_domain)___&&&___(.*)$/) {
                   16405:         $renametitle=$4;
                   16406:         my ($time,$uname,$udom) = ($1,$2,$3);
                   16407:         $foldertitle=&Apache::lontexconvert::msgtexconverted($4);
                   16408:         my $name =  &plainname($uname,$udom);
                   16409:         $name = &HTML::Entities::encode($name,'"<>&\'');
                   16410:         $renametitle = &HTML::Entities::encode($renametitle,'"<>&\'');
                   16411:         $title='<i>'.&Apache::lonlocal::locallocaltime($time).'</i> '.
                   16412:             $name.': <br />'.$foldertitle;
                   16413:     }
                   16414:     if (wantarray) {
                   16415:         return ($title,$foldertitle,$renametitle);
                   16416:     }
                   16417:     return $title;
                   16418: }
                   16419: 
1.1143    raeburn  16420: sub recurse_supplemental {
                   16421:     my ($cnum,$cdom,$suppmap,$numfiles,$errors) = @_;
                   16422:     if ($suppmap) {
                   16423:         my ($errtext,$fatal) = &LONCAPA::map::mapread('/uploaded/'.$cdom.'/'.$cnum.'/'.$suppmap);
                   16424:         if ($fatal) {
                   16425:             $errors ++;
                   16426:         } else {
                   16427:             if ($#LONCAPA::map::resources > 0) {
                   16428:                 foreach my $res (@LONCAPA::map::resources) {
                   16429:                     my ($title,$src,$ext,$type,$status)=split(/\:/,$res);
                   16430:                     if (($src ne '') && ($status eq 'res')) {
1.1146    raeburn  16431:                         if ($src =~ m{^\Q/uploaded/$cdom/$cnum/\E(supplemental_\d+\.sequence)$}) {
                   16432:                             ($numfiles,$errors) = &recurse_supplemental($cnum,$cdom,$1,$numfiles,$errors);
1.1143    raeburn  16433:                         } else {
                   16434:                             $numfiles ++;
                   16435:                         }
                   16436:                     }
                   16437:                 }
                   16438:             }
                   16439:         }
                   16440:     }
                   16441:     return ($numfiles,$errors);
                   16442: }
                   16443: 
1.1101    raeburn  16444: sub symb_to_docspath {
                   16445:     my ($symb) = @_;
                   16446:     return unless ($symb);
                   16447:     my ($mapurl,$id,$resurl) = &Apache::lonnet::decode_symb($symb);
                   16448:     if ($resurl=~/\.(sequence|page)$/) {
                   16449:         $mapurl=$resurl;
                   16450:     } elsif ($resurl eq 'adm/navmaps') {
                   16451:         $mapurl=$env{'course.'.$env{'request.course.id'}.'.url'};
                   16452:     }
                   16453:     my $mapresobj;
                   16454:     my $navmap = Apache::lonnavmaps::navmap->new();
                   16455:     if (ref($navmap)) {
                   16456:         $mapresobj = $navmap->getResourceByUrl($mapurl);
                   16457:     }
                   16458:     $mapurl=~s{^.*/([^/]+)\.(\w+)$}{$1};
                   16459:     my $type=$2;
                   16460:     my $path;
                   16461:     if (ref($mapresobj)) {
                   16462:         my $pcslist = $mapresobj->map_hierarchy();
                   16463:         if ($pcslist ne '') {
                   16464:             foreach my $pc (split(/,/,$pcslist)) {
                   16465:                 next if ($pc <= 1);
                   16466:                 my $res = $navmap->getByMapPc($pc);
                   16467:                 if (ref($res)) {
                   16468:                     my $thisurl = $res->src();
                   16469:                     $thisurl=~s{^.*/([^/]+)\.\w+$}{$1};
                   16470:                     my $thistitle = $res->title();
                   16471:                     $path .= '&'.
                   16472:                              &Apache::lonhtmlcommon::entity_encode($thisurl).'&'.
1.1146    raeburn  16473:                              &escape($thistitle).
1.1101    raeburn  16474:                              ':'.$res->randompick().
                   16475:                              ':'.$res->randomout().
                   16476:                              ':'.$res->encrypted().
                   16477:                              ':'.$res->randomorder().
                   16478:                              ':'.$res->is_page();
                   16479:                 }
                   16480:             }
                   16481:         }
                   16482:         $path =~ s/^\&//;
                   16483:         my $maptitle = $mapresobj->title();
                   16484:         if ($mapurl eq 'default') {
1.1129    raeburn  16485:             $maptitle = 'Main Content';
1.1101    raeburn  16486:         }
                   16487:         $path .= (($path ne '')? '&' : '').
                   16488:                  &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
1.1146    raeburn  16489:                  &escape($maptitle).
1.1101    raeburn  16490:                  ':'.$mapresobj->randompick().
                   16491:                  ':'.$mapresobj->randomout().
                   16492:                  ':'.$mapresobj->encrypted().
                   16493:                  ':'.$mapresobj->randomorder().
                   16494:                  ':'.$mapresobj->is_page();
                   16495:     } else {
                   16496:         my $maptitle = &Apache::lonnet::gettitle($mapurl);
                   16497:         my $ispage = (($type eq 'page')? 1 : '');
                   16498:         if ($mapurl eq 'default') {
1.1129    raeburn  16499:             $maptitle = 'Main Content';
1.1101    raeburn  16500:         }
                   16501:         $path = &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
1.1146    raeburn  16502:                 &escape($maptitle).':::::'.$ispage;
1.1101    raeburn  16503:     }
                   16504:     unless ($mapurl eq 'default') {
                   16505:         $path = 'default&'.
1.1146    raeburn  16506:                 &escape('Main Content').
1.1101    raeburn  16507:                 ':::::&'.$path;
                   16508:     }
                   16509:     return $path;
                   16510: }
                   16511: 
1.1094    raeburn  16512: sub captcha_display {
                   16513:     my ($context,$lonhost) = @_;
                   16514:     my ($output,$error);
1.1234    raeburn  16515:     my ($captcha,$pubkey,$privkey,$version) = 
                   16516:         &get_captcha_config($context,$lonhost);
1.1095    raeburn  16517:     if ($captcha eq 'original') {
1.1094    raeburn  16518:         $output = &create_captcha();
                   16519:         unless ($output) {
1.1172    raeburn  16520:             $error = 'captcha';
1.1094    raeburn  16521:         }
                   16522:     } elsif ($captcha eq 'recaptcha') {
1.1234    raeburn  16523:         $output = &create_recaptcha($pubkey,$version);
1.1094    raeburn  16524:         unless ($output) {
1.1172    raeburn  16525:             $error = 'recaptcha';
1.1094    raeburn  16526:         }
                   16527:     }
1.1234    raeburn  16528:     return ($output,$error,$captcha,$version);
1.1094    raeburn  16529: }
                   16530: 
                   16531: sub captcha_response {
                   16532:     my ($context,$lonhost) = @_;
                   16533:     my ($captcha_chk,$captcha_error);
1.1234    raeburn  16534:     my ($captcha,$pubkey,$privkey,$version) = &get_captcha_config($context,$lonhost);
1.1095    raeburn  16535:     if ($captcha eq 'original') {
1.1094    raeburn  16536:         ($captcha_chk,$captcha_error) = &check_captcha();
                   16537:     } elsif ($captcha eq 'recaptcha') {
1.1234    raeburn  16538:         $captcha_chk = &check_recaptcha($privkey,$version);
1.1094    raeburn  16539:     } else {
                   16540:         $captcha_chk = 1;
                   16541:     }
                   16542:     return ($captcha_chk,$captcha_error);
                   16543: }
                   16544: 
                   16545: sub get_captcha_config {
                   16546:     my ($context,$lonhost) = @_;
1.1234    raeburn  16547:     my ($captcha,$pubkey,$privkey,$version,$hashtocheck);
1.1094    raeburn  16548:     my $hostname = &Apache::lonnet::hostname($lonhost);
                   16549:     my $serverhomeID = &Apache::lonnet::get_server_homeID($hostname);
                   16550:     my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
1.1095    raeburn  16551:     if ($context eq 'usercreation') {
                   16552:         my %domconfig = &Apache::lonnet::get_dom('configuration',[$context],$serverhomedom);
                   16553:         if (ref($domconfig{$context}) eq 'HASH') {
                   16554:             $hashtocheck = $domconfig{$context}{'cancreate'};
                   16555:             if (ref($hashtocheck) eq 'HASH') {
                   16556:                 if ($hashtocheck->{'captcha'} eq 'recaptcha') {
                   16557:                     if (ref($hashtocheck->{'recaptchakeys'}) eq 'HASH') {
                   16558:                         $pubkey = $hashtocheck->{'recaptchakeys'}{'public'};
                   16559:                         $privkey = $hashtocheck->{'recaptchakeys'}{'private'};
                   16560:                     }
                   16561:                     if ($privkey && $pubkey) {
                   16562:                         $captcha = 'recaptcha';
1.1234    raeburn  16563:                         $version = $hashtocheck->{'recaptchaversion'};
                   16564:                         if ($version ne '2') {
                   16565:                             $version = 1;
                   16566:                         }
1.1095    raeburn  16567:                     } else {
                   16568:                         $captcha = 'original';
                   16569:                     }
                   16570:                 } elsif ($hashtocheck->{'captcha'} ne 'notused') {
                   16571:                     $captcha = 'original';
                   16572:                 }
1.1094    raeburn  16573:             }
1.1095    raeburn  16574:         } else {
                   16575:             $captcha = 'captcha';
                   16576:         }
                   16577:     } elsif ($context eq 'login') {
                   16578:         my %domconfhash = &Apache::loncommon::get_domainconf($serverhomedom);
                   16579:         if ($domconfhash{$serverhomedom.'.login.captcha'} eq 'recaptcha') {
                   16580:             $pubkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_public'};
                   16581:             $privkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_private'};
1.1094    raeburn  16582:             if ($privkey && $pubkey) {
                   16583:                 $captcha = 'recaptcha';
1.1234    raeburn  16584:                 $version = $domconfhash{$serverhomedom.'.login.recaptchaversion'};
                   16585:                 if ($version ne '2') {
                   16586:                     $version = 1; 
                   16587:                 }
1.1095    raeburn  16588:             } else {
                   16589:                 $captcha = 'original';
1.1094    raeburn  16590:             }
1.1095    raeburn  16591:         } elsif ($domconfhash{$serverhomedom.'.login.captcha'} eq 'original') {
                   16592:             $captcha = 'original';
1.1094    raeburn  16593:         }
                   16594:     }
1.1234    raeburn  16595:     return ($captcha,$pubkey,$privkey,$version);
1.1094    raeburn  16596: }
                   16597: 
                   16598: sub create_captcha {
                   16599:     my %captcha_params = &captcha_settings();
                   16600:     my ($output,$maxtries,$tries) = ('',10,0);
                   16601:     while ($tries < $maxtries) {
                   16602:         $tries ++;
                   16603:         my $captcha = Authen::Captcha->new (
                   16604:                                            output_folder => $captcha_params{'output_dir'},
                   16605:                                            data_folder   => $captcha_params{'db_dir'},
                   16606:                                           );
                   16607:         my $md5sum = $captcha->generate_code($captcha_params{'numchars'});
                   16608: 
                   16609:         if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') {
                   16610:             $output = '<input type="hidden" name="crypt" value="'.$md5sum.'" />'."\n".
                   16611:                       &mt('Type in the letters/numbers shown below').'&nbsp;'.
1.1176    raeburn  16612:                       '<input type="text" size="5" name="code" value="" autocomplete="off" />'.
                   16613:                       '<br />'.
                   16614:                       '<img src="'.$captcha_params{'www_output_dir'}.'/'.$md5sum.'.png" alt="captcha" />';
1.1094    raeburn  16615:             last;
                   16616:         }
                   16617:     }
                   16618:     return $output;
                   16619: }
                   16620: 
                   16621: sub captcha_settings {
                   16622:     my %captcha_params = (
                   16623:                            output_dir     => $Apache::lonnet::perlvar{'lonCaptchaDir'},
                   16624:                            www_output_dir => "/captchaspool",
                   16625:                            db_dir         => $Apache::lonnet::perlvar{'lonCaptchaDb'},
                   16626:                            numchars       => '5',
                   16627:                          );
                   16628:     return %captcha_params;
                   16629: }
                   16630: 
                   16631: sub check_captcha {
                   16632:     my ($captcha_chk,$captcha_error);
                   16633:     my $code = $env{'form.code'};
                   16634:     my $md5sum = $env{'form.crypt'};
                   16635:     my %captcha_params = &captcha_settings();
                   16636:     my $captcha = Authen::Captcha->new(
                   16637:                       output_folder => $captcha_params{'output_dir'},
                   16638:                       data_folder   => $captcha_params{'db_dir'},
                   16639:                   );
1.1109    raeburn  16640:     $captcha_chk = $captcha->check_code($code,$md5sum);
1.1094    raeburn  16641:     my %captcha_hash = (
                   16642:                         0       => 'Code not checked (file error)',
                   16643:                        -1      => 'Failed: code expired',
                   16644:                        -2      => 'Failed: invalid code (not in database)',
                   16645:                        -3      => 'Failed: invalid code (code does not match crypt)',
                   16646:     );
                   16647:     if ($captcha_chk != 1) {
                   16648:         $captcha_error = $captcha_hash{$captcha_chk}
                   16649:     }
                   16650:     return ($captcha_chk,$captcha_error);
                   16651: }
                   16652: 
                   16653: sub create_recaptcha {
1.1234    raeburn  16654:     my ($pubkey,$version) = @_;
                   16655:     if ($version >= 2) {
                   16656:         return '<div class="g-recaptcha" data-sitekey="'.$pubkey.'"></div>';
                   16657:     } else {
                   16658:         my $use_ssl;
                   16659:         if ($ENV{'SERVER_PORT'} == 443) {
                   16660:             $use_ssl = 1;
                   16661:         }
                   16662:         my $captcha = Captcha::reCAPTCHA->new;
                   16663:         return $captcha->get_options_setter({theme => 'white'})."\n".
                   16664:                $captcha->get_html($pubkey,undef,$use_ssl).
                   16665:                &mt('If the text is hard to read, [_1] will replace them.',
                   16666:                    '<img src="/res/adm/pages/refresh.gif" alt="reCAPTCHA refresh" />').
                   16667:                '<br /><br />';
                   16668:     }
1.1094    raeburn  16669: }
                   16670: 
                   16671: sub check_recaptcha {
1.1234    raeburn  16672:     my ($privkey,$version) = @_;
1.1094    raeburn  16673:     my $captcha_chk;
1.1234    raeburn  16674:     if ($version >= 2) {
                   16675:         my $ua = LWP::UserAgent->new;
                   16676:         $ua->timeout(10);
                   16677:         my %info = (
                   16678:                      secret   => $privkey, 
                   16679:                      response => $env{'form.g-recaptcha-response'},
                   16680:                      remoteip => $ENV{'REMOTE_ADDR'},
                   16681:                    );
                   16682:         my $response = $ua->post('https://www.google.com/recaptcha/api/siteverify',\%info);
                   16683:         if ($response->is_success)  {
                   16684:             my $data = JSON::DWIW->from_json($response->decoded_content);
                   16685:             if (ref($data) eq 'HASH') {
                   16686:                 if ($data->{'success'}) {
                   16687:                     $captcha_chk = 1;
                   16688:                 }
                   16689:             }
                   16690:         }
                   16691:     } else {
                   16692:         my $captcha = Captcha::reCAPTCHA->new;
                   16693:         my $captcha_result =
                   16694:             $captcha->check_answer(
                   16695:                                     $privkey,
                   16696:                                     $ENV{'REMOTE_ADDR'},
                   16697:                                     $env{'form.recaptcha_challenge_field'},
                   16698:                                     $env{'form.recaptcha_response_field'},
                   16699:                                   );
                   16700:         if ($captcha_result->{is_valid}) {
                   16701:             $captcha_chk = 1;
                   16702:         }
1.1094    raeburn  16703:     }
                   16704:     return $captcha_chk;
                   16705: }
                   16706: 
1.1174    raeburn  16707: sub emailusername_info {
1.1177    raeburn  16708:     my @fields = ('firstname','lastname','institution','web','location','officialemail');
1.1174    raeburn  16709:     my %titles = &Apache::lonlocal::texthash (
                   16710:                      lastname      => 'Last Name',
                   16711:                      firstname     => 'First Name',
                   16712:                      institution   => 'School/college/university',
                   16713:                      location      => "School's city, state/province, country",
                   16714:                      web           => "School's web address",
                   16715:                      officialemail => 'E-mail address at institution (if different)',
                   16716:                  );
                   16717:     return (\@fields,\%titles);
                   16718: }
                   16719: 
1.1161    raeburn  16720: sub cleanup_html {
                   16721:     my ($incoming) = @_;
                   16722:     my $outgoing;
                   16723:     if ($incoming ne '') {
                   16724:         $outgoing = $incoming;
                   16725:         $outgoing =~ s/;/&#059;/g;
                   16726:         $outgoing =~ s/\#/&#035;/g;
                   16727:         $outgoing =~ s/\&/&#038;/g;
                   16728:         $outgoing =~ s/</&#060;/g;
                   16729:         $outgoing =~ s/>/&#062;/g;
                   16730:         $outgoing =~ s/\(/&#040/g;
                   16731:         $outgoing =~ s/\)/&#041;/g;
                   16732:         $outgoing =~ s/"/&#034;/g;
                   16733:         $outgoing =~ s/'/&#039;/g;
                   16734:         $outgoing =~ s/\$/&#036;/g;
                   16735:         $outgoing =~ s{/}{&#047;}g;
                   16736:         $outgoing =~ s/=/&#061;/g;
                   16737:         $outgoing =~ s/\\/&#092;/g
                   16738:     }
                   16739:     return $outgoing;
                   16740: }
                   16741: 
1.1190    musolffc 16742: # Checks for critical messages and returns a redirect url if one exists.
                   16743: # $interval indicates how often to check for messages.
                   16744: sub critical_redirect {
                   16745:     my ($interval) = @_;
                   16746:     if ((time-$env{'user.criticalcheck.time'})>$interval) {
                   16747:         my @what=&Apache::lonnet::dump('critical', $env{'user.domain'}, 
                   16748:                                         $env{'user.name'});
                   16749:         &Apache::lonnet::appenv({'user.criticalcheck.time'=>time});
1.1191    raeburn  16750:         my $redirecturl;
1.1190    musolffc 16751:         if ($what[0]) {
                   16752: 	    if (($what[0] ne 'con_lost') && ($what[0]!~/^error\:/)) {
                   16753: 	        $redirecturl='/adm/email?critical=display';
1.1191    raeburn  16754: 	        my $url=&Apache::lonnet::absolute_url().$redirecturl;
                   16755:                 return (1, $url);
1.1190    musolffc 16756:             }
1.1191    raeburn  16757:         }
                   16758:     } 
                   16759:     return ();
1.1190    musolffc 16760: }
                   16761: 
1.1174    raeburn  16762: # Use:
                   16763: #   my $answer=reply("encrypt:passwd:$udom:$uname:$upass",$tryserver);
                   16764: #
                   16765: ##################################################
                   16766: #          password associated functions         #
                   16767: ##################################################
                   16768: sub des_keys {
                   16769:     # Make a new key for DES encryption.
                   16770:     # Each key has two parts which are returned separately.
                   16771:     # Please note:  Each key must be passed through the &hex function
                   16772:     # before it is output to the web browser.  The hex versions cannot
                   16773:     # be used to decrypt.
                   16774:     my @hexstr=('0','1','2','3','4','5','6','7',
                   16775:                 '8','9','a','b','c','d','e','f');
                   16776:     my $lkey='';
                   16777:     for (0..7) {
                   16778:         $lkey.=$hexstr[rand(15)];
                   16779:     }
                   16780:     my $ukey='';
                   16781:     for (0..7) {
                   16782:         $ukey.=$hexstr[rand(15)];
                   16783:     }
                   16784:     return ($lkey,$ukey);
                   16785: }
                   16786: 
                   16787: sub des_decrypt {
                   16788:     my ($key,$cyphertext) = @_;
                   16789:     my $keybin=pack("H16",$key);
                   16790:     my $cypher;
                   16791:     if ($Crypt::DES::VERSION>=2.03) {
                   16792:         $cypher=new Crypt::DES $keybin;
                   16793:     } else {
                   16794:         $cypher=new DES $keybin;
                   16795:     }
1.1233    raeburn  16796:     my $plaintext='';
                   16797:     my $cypherlength = length($cyphertext);
                   16798:     my $numchunks = int($cypherlength/32);
                   16799:     for (my $j=0; $j<$numchunks; $j++) {
                   16800:         my $start = $j*32;
                   16801:         my $cypherblock = substr($cyphertext,$start,32);
                   16802:         my $chunk =
                   16803:             $cypher->decrypt(unpack("a8",pack("H16",substr($cypherblock,0,16))));
                   16804:         $chunk .=
                   16805:             $cypher->decrypt(unpack("a8",pack("H16",substr($cypherblock,16,16))));
                   16806:         $chunk=substr($chunk,1,ord(substr($chunk,0,1)) );
                   16807:         $plaintext .= $chunk;
                   16808:     }
1.1174    raeburn  16809:     return $plaintext;
                   16810: }
                   16811: 
1.112     bowersj2 16812: 1;
                   16813: __END__;
1.41      ng       16814: 

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