--- loncom/interface/loncommon.pm 2003/12/15 19:23:03 1.159 +++ loncom/interface/loncommon.pm 2006/03/21 20:36:15 1.317 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common routines # -# $Id: loncommon.pm,v 1.159 2003/12/15 19:23:03 www Exp $ +# $Id: loncommon.pm,v 1.317 2006/03/21 20:36:15 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -55,11 +55,10 @@ redundancy from other modules and increa package Apache::loncommon; use strict; -use Apache::lonnet(); +use Apache::lonnet; use GDBM_File; use POSIX qw(strftime mktime); use Apache::Constants qw(:common :http :methods); -use Apache::lonmsg(); use Apache::lonmenu(); use Apache::lonlocal; use HTML::Entities; @@ -74,6 +73,7 @@ my $readit; my %language; my %supported_language; my %cprtag; +my %scprtag; my %fe; my %fd; my %category_extensions; @@ -131,27 +131,42 @@ BEGIN { close($fh); } } - -# -------------------------------------------------------------- domain designs - - my $filename; - my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors'; - opendir(DIR,$designdir); - while ($filename=readdir(DIR)) { - my ($domain)=($filename=~/^(\w+)\./); +# ------------------------------------------------------------------ source copyrights { - my $designfile = $designdir.'/'.$filename; - if ( open (my $fh,"<$designfile") ) { + my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}. + '/source_copyright.tab'; + if ( open (my $fh,"<$sourcecopyrightfile") ) { while (<$fh>) { next if /^\#/; chomp; - my ($key,$val)=(split(/\=/,$_)); - if ($val) { $designhash{$domain.'.'.$key}=$val; } + my ($key,$val)=(split(/\s+/,$_,2)); + $scprtag{$key}=$val; } close($fh); } } +# -------------------------------------------------------------- domain designs + + my $filename; + my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors'; + opendir(DIR,$designdir); + while ($filename=readdir(DIR)) { + if ($filename!~/\.tab$/) { next; } + my ($domain)=($filename=~/^(\w+)\./); + { + my $designfile = $designdir.'/'.$filename; + if ( open (my $fh,"<$designfile") ) { + while (<$fh>) { + next if /^\#/; + chomp; + my ($key,$val)=(split(/\=/,$_)); + if ($val) { $designhash{$domain.'.'.$key}=$val; } + } + close($fh); + } + } + } closedir(DIR); @@ -220,10 +235,10 @@ formname and elementname indicate the na the element that the results of the browsing selection are to be placed in. Specifying 'only' will restrict the browser to displaying only files -with the given extension. Can be a comma seperated list. +with the given extension. Can be a comma separated list. Specifying 'omit' will restrict the browser to NOT displaying files -with the given extension. Can be a comma seperated list. +with the given extension. Can be a comma separated list. =item * opensearcher(formname, elementname) [javascript] @@ -235,28 +250,38 @@ of the element the selection from the se =cut sub browser_and_searcher_javascript { + my ($mode)=@_; + if (!defined($mode)) { $mode='edit'; } + my $resurl=&lastresurl(); return < END } +sub lastresurl { + if ($env{'environment.lastresurl'}) { + return $env{'environment.lastresurl'} + } else { + return '/res'; + } +} + +sub storeresurl { + my $resurl=&Apache::lonnet::clutter(shift); + unless ($resurl=~/^\/res/) { return 0; } + $resurl=~s/\/$//; + &Apache::lonnet::put('environment',{'lastresurl' => $resurl}); + &Apache::lonnet::appenv('environment.lastresurl' => $resurl); + return 1; +} + sub studentbrowser_javascript { unless ( - (($ENV{'request.course.id'}) && - (&Apache::lonnet::allowed('srm',$ENV{'request.course.id'}))) - || ($ENV{'request.role'}=~/^(au|dc|su)/) + (($env{'request.course.id'}) && + (&Apache::lonnet::allowed('srm',$env{'request.course.id'}) + || &Apache::lonnet::allowed('srm',$env{'request.course.id'}. + '/'.$env{'request.course.sec'}) + )) + || ($env{'request.role'}=~/^(au|dc|su)/) ) { return ''; } return (<<'ENDSTDBRW'); + (Help Menu) +ENDTEMPLATE + if ($component_help) { + if (!$text) { + $template=&help_open_topic($component_help,undef,$stayOnPage, + $width,$height).' '.$template; + } else { + my $help_text; + $help_text=&Apache::lonnet::unescape($topic); + $template='
'. + &help_open_topic($component_help,$help_text,$stayOnPage, + $width,$height).''.$template. + '
'; + } + } + if ($text ne '') { $template.='' }; + return $template; +} -=item * csv_translate($text) +sub help_open_bug { + my ($topic, $text, $stayOnPage, $width, $height) = @_; + unless ($env{'user.adv'}) { return ''; } + unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; } + $text = "" if (not defined $text); + $stayOnPage = 0 if (not defined $stayOnPage); + if ($env{'browser.interface'} eq 'textual' || + $env{'environment.remote'} eq 'off' ) { + $stayOnPage=1; + } + $width = 600 if (not defined $width); + $height = 600 if (not defined $height); -Translate $text to allow it to be output as a 'comma seperated values' -format. + $topic=~s/\W+/\+/g; + my $link=''; + my $template=''; + my $url=$Apache::lonnet::perlvar{'BugzillaHost'}.'enter_bug.cgi?product=LON-CAPA&bug_file_loc='. + &Apache::lonnet::escape($ENV{'REQUEST_URI'}).'&component='.$topic; + if (!$stayOnPage) + { + $link = "javascript:void(open('$url', 'Bugzilla', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))"; + } + else + { + $link = $url; + } + # Add the text + if ($text ne "") + { + $template .= + "". + "
$text"; + } -=cut + # Add the graphic + my $title = &mt('Report a Bug'); + my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif"); + $template .= <<"ENDTEMPLATE"; + (Bug: $topic) +ENDTEMPLATE + if ($text ne '') { $template.='
' }; + return $template; + +} + +sub help_open_faq { + my ($topic, $text, $stayOnPage, $width, $height) = @_; + unless ($env{'user.adv'}) { return ''; } + unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; } + $text = "" if (not defined $text); + $stayOnPage = 0 if (not defined $stayOnPage); + if ($env{'browser.interface'} eq 'textual' || + $env{'environment.remote'} eq 'off' ) { + $stayOnPage=1; + } + $width = 350 if (not defined $width); + $height = 400 if (not defined $height); + + $topic=~s/\W+/\+/g; + my $link=''; + my $template=''; + my $url=$Apache::lonnet::perlvar{'FAQHost'}.'/fom/cache/'.$topic.'.html'; + if (!$stayOnPage) + { + $link = "javascript:void(open('$url', 'FAQ-O-Matic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))"; + } + else + { + $link = $url; + } + + # Add the text + if ($text ne "") + { + $template .= + "". + "
$text"; + } + + # Add the graphic + my $title = &mt('View the FAQ'); + my $faqicon=&lonhttpdurl("/adm/lonMisc/smallFAQ.gif"); + $template .= <<"ENDTEMPLATE"; + (FAQ: $topic) +ENDTEMPLATE + if ($text ne '') { $template.='
' }; + return $template; -sub csv_translate { - my $text = shift; - $text =~ s/\"/\"\"/g; - $text =~ s/\n//g; - return $text; } +############################################################### +############################################################### + =pod =item * change_content_javascript(): @@ -650,8 +899,8 @@ pretty much any HTML. sub change_content_javascript { # If we're on Netscape 4, we need to use Layer-based code - if ($ENV{'browser.type'} eq 'netscape' && - $ENV{'browser.version'} =~ /^4\./) { + if ($env{'browser.type'} eq 'netscape' && + $env{'browser.version'} =~ /^4\./) { return (<$origContent"; } else { @@ -701,6 +950,174 @@ sub changable_area { =back +=head1 Excel and CSV file utility routines + +=over 4 + +=cut + +############################################################### +############################################################### + +=pod + +=item * csv_translate($text) + +Translate $text to allow it to be output as a 'comma separated values' +format. + +=cut + +############################################################### +############################################################### +sub csv_translate { + my $text = shift; + $text =~ s/\"/\"\"/g; + $text =~ s/\n/ /g; + return $text; +} + +############################################################### +############################################################### + +=pod + +=item * define_excel_formats + +Define some commonly used Excel cell formats. + +Currently supported formats: + +=over 4 + +=item header + +=item bold + +=item h1 + +=item h2 + +=item h3 + +=item h4 + +=item i + +=item date + +=back + +Inputs: $workbook + +Returns: $format, a hash reference. + +=cut + +############################################################### +############################################################### +sub define_excel_formats { + my ($workbook) = @_; + my $format; + $format->{'header'} = $workbook->add_format(bold => 1, + bottom => 1, + align => 'center'); + $format->{'bold'} = $workbook->add_format(bold=>1); + $format->{'h1'} = $workbook->add_format(bold=>1, size=>18); + $format->{'h2'} = $workbook->add_format(bold=>1, size=>16); + $format->{'h3'} = $workbook->add_format(bold=>1, size=>14); + $format->{'h4'} = $workbook->add_format(bold=>1, size=>12); + $format->{'i'} = $workbook->add_format(italic=>1); + $format->{'date'} = $workbook->add_format(num_format=> + 'mm/dd/yyyy hh:mm:ss'); + return $format; +} + +############################################################### +############################################################### + +=pod + +=item * create_workbook + +Create an Excel worksheet. If it fails, output message on the +request object and return undefs. + +Inputs: Apache request object + +Returns (undef) on failure, + Excel worksheet object, scalar with filename, and formats + from &Apache::loncommon::define_excel_formats on success + +=cut + +############################################################### +############################################################### +sub create_workbook { + my ($r) = @_; + # + # Create the excel spreadsheet + my $filename = '/prtspool/'. + $env{'user.name'}.'_'.$env{'user.domain'}.'_'. + time.'_'.rand(1000000000).'.xls'; + my $workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename); + if (! defined($workbook)) { + $r->log_error("Error creating excel spreadsheet $filename: $!"); + $r->print('

'.&mt("Unable to create new Excel file. ". + "This error has been logged. ". + "Please alert your LON-CAPA administrator"). + '

'); + return (undef); + } + # + $workbook->set_tempdir('/home/httpd/perl/tmp'); + # + my $format = &Apache::loncommon::define_excel_formats($workbook); + return ($workbook,$filename,$format); +} + +############################################################### +############################################################### + +=pod + +=item * create_text_file + +Create a file to write to and eventually make available to the usre. +If file creation fails, outputs an error message on the request object and +return undefs. + +Inputs: Apache request object, and file suffix + +Returns (undef) on failure, + Filehandle and filename on success. + +=cut + +############################################################### +############################################################### +sub create_text_file { + my ($r,$suffix) = @_; + if (! defined($suffix)) { $suffix = 'txt'; }; + my $fh; + my $filename = '/prtspool/'. + $env{'user.name'}.'_'.$env{'user.domain'}.'_'. + time.'_'.rand(1000000000).'.'.$suffix; + $fh = Apache::File->new('>/home/httpd'.$filename); + if (! defined($fh)) { + $r->log_error("Couldn't open $filename for output $!"); + $r->print("Problems occured in creating the output file. ". + "This error has been logged. ". + "Please alert your LON-CAPA administrator."); + } + return ($fh,$filename) +} + + +=pod + +=back + =cut ############################################################### @@ -726,11 +1143,68 @@ sub get_domains { my @domains; my %seen; foreach (sort values(%Apache::lonnet::hostdom)) { - push (@domains,$_) unless $seen{$_}++; + push (@domains,$_) unless $seen{$_}++; } return @domains; } +# ------------------------------------------ + +sub domain_select { + my ($name,$value,$multiple)=@_; + my %domains=map { + $_ => $_.' '.$Apache::lonnet::domaindescription{$_} + } &get_domains; + if ($multiple) { + $domains{''}=&mt('Any domain'); + return &multiple_select_form($name,$value,4,\%domains); + } else { + return &select_form($name,$value,%domains); + } +} + +#------------------------------------------- + +=pod + +=item * multiple_select_form($name,$value,$size,$hash,$order) + +Returns a string containing a element + $value - sclara or array ref of values that should already be selected + $size - number of rows long the select element is + $hash - the elements should be 'option' => 'shown text' + (shown text should already have been &mt()) + $order - (optional) array ref of the order to show the elments in + +=cut + +#------------------------------------------- +sub multiple_select_form { + my ($name,$value,$size,$hash,$order)=@_; + my %selected = map { $_ => 1 } ref($value)?@{$value}:($value); + my $output=''; + if (! defined($size)) { + $size = 4; + if (scalar(keys(%$hash))<4) { + $size = scalar(keys(%$hash)); + } + } + $output.="\n\n"; + return $output; +} + #------------------------------------------- =pod @@ -755,13 +1229,49 @@ sub select_form { } foreach (@keys) { $selectform.="\n"; } $selectform.=""; return $selectform; } +sub gradeleveldescription { + my $gradelevel=shift; + my %gradelevels=(0 => 'Not specified', + 1 => 'Grade 1', + 2 => 'Grade 2', + 3 => 'Grade 3', + 4 => 'Grade 4', + 5 => 'Grade 5', + 6 => 'Grade 6', + 7 => 'Grade 7', + 8 => 'Grade 8', + 9 => 'Grade 9', + 10 => 'Grade 10', + 11 => 'Grade 11', + 12 => 'Grade 12', + 13 => 'Grade 13', + 14 => '100 Level', + 15 => '200 Level', + 16 => '300 Level', + 17 => '400 Level', + 18 => 'Graduate Level'); + return &mt($gradelevels{$gradelevel}); +} + +sub select_level_form { + my ($deflevel,$name)=@_; + unless ($deflevel) { $deflevel=0; } + my $selectform = ""; + return $selectform; +} #------------------------------------------- @@ -786,7 +1296,7 @@ sub select_dom_form { my $selectdomain = ""; @@ -885,9 +1395,11 @@ Outputs: ############################################################### ############################################################### sub decode_user_agent { + my ($r)=@_; my @browsertype=split(/\&/,$Apache::lonnet::perlvar{"lonBrowsDet"}); my %mathcap=split(/\&/,$$Apache::lonnet::perlvar{"lonMathML"}); my $httpbrowser=$ENV{"HTTP_USER_AGENT"}; + if (!$httpbrowser && $r) { $httpbrowser=$r->header_in('User-Agent'); } my $clientbrowser='unknown'; my $clientversion='0'; my $clientmathml=''; @@ -987,10 +1499,30 @@ END $Javascript_toUpperCase = ""; } + my $radioval = "'nochange'"; + if (exists($in{'curr_authtype'}) && + defined($in{'curr_authtype'}) && + $in{'curr_authtype'} ne '') { + $radioval = "'$in{'curr_authtype'}arg'"; + } + my $argfield = 'null'; + if ( grep/^mode$/,(keys %in) ) { + if ($in{'mode'} eq 'modifycourse') { + if ( grep/^curr_authtype$/,(keys %in) ) { + $radioval = "'$in{'curr_authtype'}'"; + } + if ( grep/^curr_autharg$/,(keys %in) ) { + unless ($in{'curr_autharg'} eq '') { + $argfield = "'$in{'curr_autharg'}'"; + } + } + } + } + $result.=<<"END"; var current = new Object(); -current.radiovalue = 'nochange'; -current.argfield = null; +current.radiovalue = $radioval; +current.argfield = $argfield; function changed_radio(choice,currentform) { var choicearg = choice + 'arg'; @@ -1063,10 +1595,11 @@ sub authform_nochange{ kerb_def_dom => 'MSU.EDU', @_, ); - my $result = &mt('[_1] Do not change login data', + my $result = ''; return $result; } @@ -1077,23 +1610,36 @@ sub authform_kerberos{ kerb_def_auth => 'krb4', @_, ); - my ($check4,$check5); + my ($check4,$check5,$krbarg); if ($in{'kerb_def_auth'} eq 'krb5') { $check5 = " checked=\"on\""; } else { $check4 = " checked=\"on\""; } + $krbarg = $in{'kerb_def_dom'}; + + my $krbcheck = ""; + if ( grep/^curr_authtype$/,(keys %in) ) { + if ($in{'curr_authtype'} =~ m/^krb/) { + $krbcheck = " checked=\"on\""; + if ( grep/^curr_autharg$/,(keys %in) ) { + $krbarg = $in{'curr_autharg'}; + } + } + } + my $jscall = "javascript:changed_radio('krb',$in{'formname'});"; my $result .= &mt ('[_1] Kerberos authenticated with domain [_2] '. - '[_3] Version 4 [_4] Version 5', - '', - '', + '', - '', - ''); + ''); return $result; } @@ -1103,13 +1649,25 @@ sub authform_internal{ kerb_def_dom => 'MSU.EDU', @_, ); + + my $intcheck = ""; + my $intarg = 'value=""'; + if ( grep/^curr_authtype$/,(keys %args) ) { + if ($args{'curr_authtype'} eq 'int') { + $intcheck = " checked=\"on\""; + if ( grep/^curr_autharg$/,(keys %args) ) { + $intarg = "value=\"$args{'curr_autharg'}\""; + } + } + } + my $jscall = "javascript:changed_radio('int',$args{'formname'});"; my $result.=&mt ('[_1] Internally authenticated (with initial password [_2])', - '', - ''); + ''); return $result; } @@ -1119,12 +1677,24 @@ sub authform_local{ kerb_def_dom => 'MSU.EDU', @_, ); + + my $loccheck = ""; + my $locarg = 'value=""'; + if ( grep/^curr_authtype$/,(keys %in) ) { + if ($in{'curr_authtype'} eq 'loc') { + $loccheck = " checked=\"on\""; + if ( grep/^curr_autharg$/,(keys %in) ) { + $locarg = "value=\"$in{'curr_autharg'}\""; + } + } + } + my $jscall = "javascript:changed_radio('loc',$in{'formname'});"; - my $result.=&mt('[_1] Local Authentication with arguement [_2]', - '', - ''); + my $result.=&mt('[_1] Local Authentication with argument [_2]', + ''); return $result; } @@ -1137,9 +1707,9 @@ sub authform_filesystem{ my $jscall = "javascript:changed_radio('fsys',$in{'formname'});"; my $result.= &mt ('[_1] Filesystem Authenticated (with initial password [_2])', - '', - ''); return $result; } @@ -1298,7 +1868,7 @@ sub keyword { =item * get_related_words -Look up a word in the thesaurus. Takes a scalar arguement and returns +Look up a word in the thesaurus. Takes a scalar argument and returns an array of words. If the keyword is not in the thesaurus, an empty array will be returned. The order of the words returned is determined by the database which holds them. @@ -1346,23 +1916,28 @@ sub get_related_words { =over 4 -=item * plainname($uname,$udom) +=item * plainname($uname,$udom,$first) Takes a users logon name and returns it as a string in -"first middle last generation" form +"first middle last generation" form +if $first is set to 'lastname' then it returns it as +'lastname generation, firstname middlename' if their is a lastname =cut + ############################################################### sub plainname { - my ($uname,$udom)=@_; - my %names=&Apache::lonnet::get('environment', - ['firstname','middlename','lastname','generation'], - $udom,$uname); - my $name=$names{'firstname'}.' '.$names{'middlename'}.' '. - $names{'lastname'}.' '.$names{'generation'}; + my ($uname,$udom,$first)=@_; + my %names=&getnames($uname,$udom); + my $name=&Apache::lonnet::format_name($names{'firstname'}, + $names{'middlename'}, + $names{'lastname'}, + $names{'generation'},$first); + $name=~s/^\s+//; $name=~s/\s+$//; $name=~s/\s+/ /g; + if ($name !~ /\S/) { $name=$uname.'@'.$udom; } return $name; } @@ -1385,8 +1960,7 @@ if the user does not sub nickname { my ($uname,$udom)=@_; - my %names=&Apache::lonnet::get('environment', - ['nickname','firstname','middlename','lastname','generation'],$udom,$uname); + my %names=&getnames($uname,$udom); my $name=$names{'nickname'}; if ($name) { $name='"'.$name.'"'; @@ -1399,6 +1973,20 @@ sub nickname { return $name; } +sub getnames { + my ($uname,$udom)=@_; + my $id=$uname.':'.$udom; + my ($names,$cached)=&Apache::lonnet::is_cached_new('namescache',$id); + if ($cached) { + return %{$names}; + } else { + my %loadnames=&Apache::lonnet::get('environment', + ['firstname','middlename','lastname','generation','nickname'], + $udom,$uname); + &Apache::lonnet::do_cache_new('namescache',$id,\%loadnames); + return %loadnames; + } +} # ------------------------------------------------------------------ Screenname @@ -1412,17 +2000,21 @@ Gets a users screenname and returns it a sub screenname { my ($uname,$udom)=@_; - my %names= - &Apache::lonnet::get('environment',['screenname'],$udom,$uname); + if ($uname eq $env{'user.name'} && + $udom eq $env{'user.domain'}) {return $env{'environment.screenname'};} + my %names=&Apache::lonnet::get('environment',['screenname'],$udom,$uname); return $names{'screenname'}; } + # ------------------------------------------------------------- Message Wrapper sub messagewrapper { - my ($link,$un,$do)=@_; + my ($link,$username,$domain)=@_; return -"$link"; + ''.$link.''; } # --------------------------------------------------------------- Notes Wrapper @@ -1434,8 +2026,9 @@ sub noteswrapper { # ------------------------------------------------------------- Aboutme Wrapper sub aboutmewrapper { - my ($link,$username,$domain)=@_; - return "$link"; + my ($link,$username,$domain,$target)=@_; + return ''.$link.''; } # ------------------------------------------------------------ Syllabus Wrapper @@ -1446,7 +2039,25 @@ sub syllabuswrapper { if ($fontcolor) { $linktext=''.$linktext.''; } - return "$linktext"; + return qq{$linktext}; +} + +sub track_student_link { + my ($linktext,$sname,$sdom,$target,$start) = @_; + my $link ="/adm/trackstudent?"; + my $title = 'View recent activity'; + if (defined($sname) && $sname !~ /^\s*$/ && + defined($sdom) && $sdom !~ /^\s*$/) { + $link .= "selected_student=$sname:$sdom"; + $title .= ' of this student'; + } + if (defined($target) && $target !~ /^\s*$/) { + $target = qq{target="$target"}; + } else { + $target = ''; + } + if ($start) { $link.='&start='.$start; } + return qq{$linktext}; } =pod @@ -1513,7 +2124,31 @@ returns description of a specified copyr =cut sub copyrightdescription { - return $cprtag{shift(@_)}; + return &mt($cprtag{shift(@_)}); +} + +=pod + +=item * source_copyrightids() + +returns list of all source copyrights + +=cut + +sub source_copyrightids { + return sort(keys(%scprtag)); +} + +=pod + +=item * source_copyrightdescription() + +returns description of a specified source copyright id + +=cut + +sub source_copyrightdescription { + return &mt($scprtag{shift(@_)}); } =pod @@ -1553,6 +2188,14 @@ sub fileembstyle { return $fe{lc(shift(@_))}; } + +sub filecategoryselect { + my ($name,$value)=@_; + return &select_form($value,$name, + '' => &mt('Any category'), + map { $_,$_ } sort(keys(%category_extensions))); +} + =pod =item * filedescription() @@ -1562,7 +2205,9 @@ returns description for a specified file =cut sub filedescription { - return $fd{lc(shift(@_))}; + my $file_description = $fd{lc(shift())}; + $file_description =~ s:([\[\]]):~$1:g; + return &mt($file_description); } =pod @@ -1576,7 +2221,9 @@ extra formatting sub filedescriptionex { my $ex=shift; - return '.'.$ex.' '.$fd{lc($ex)}; + my $file_description = $fd{lc($ex)}; + $file_description =~ s:([\[\]]):~$1:g; + return '.'.$ex.' '.&mt($file_description); } # End of .tab access @@ -1601,8 +2248,8 @@ sub display_languages { $languages{$_}=1; } &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']); - if ($ENV{'form.displaylanguage'}) { - foreach (split(/\s*(\,|\;|\:)\s*/,$ENV{'form.displaylanguage'})) { + if ($env{'form.displaylanguage'}) { + foreach (split(/\s*(\,|\;|\:)\s*/,$env{'form.displaylanguage'})) { $languages{$_}=1; } } @@ -1611,24 +2258,24 @@ sub display_languages { sub preferred_languages { my @languages=(); - if ($ENV{'environment.languages'}) { - @languages=split(/\s*(\,|\;|\:)\s*/,$ENV{'environment.languages'}); - } - if ($ENV{'course.'.$ENV{'request.course.id'}.'.languages'}) { + if ($env{'course.'.$env{'request.course.id'}.'.languages'}) { @languages=(@languages,split(/\s*(\,|\;|\:)\s*/, - $ENV{'course.'.$ENV{'request.course.id'}.'.languages'})); + $env{'course.'.$env{'request.course.id'}.'.languages'})); + } + if ($env{'environment.languages'}) { + @languages=split(/\s*(\,|\;|\:)\s*/,$env{'environment.languages'}); } my $browser=(split(/\;/,$ENV{'HTTP_ACCEPT_LANGUAGE'}))[0]; if ($browser) { @languages=(@languages,split(/\s*(\,|\;|\:)\s*/,$browser)); } - if ($Apache::lonnet::domain_lang_def{$ENV{'user.domain'}}) { + if ($Apache::lonnet::domain_lang_def{$env{'user.domain'}}) { @languages=(@languages, - $Apache::lonnet::domain_lang_def{$ENV{'user.domain'}}); + $Apache::lonnet::domain_lang_def{$env{'user.domain'}}); } - if ($Apache::lonnet::domain_lang_def{$ENV{'request.role.domain'}}) { + if ($Apache::lonnet::domain_lang_def{$env{'request.role.domain'}}) { @languages=(@languages, - $Apache::lonnet::domain_lang_def{$ENV{'request.role.domain'}}); + $Apache::lonnet::domain_lang_def{$env{'request.role.domain'}}); } if ($Apache::lonnet::domain_lang_def{ $Apache::lonnet::perlvar{'lonDefDomain'}}) { @@ -1797,22 +2444,19 @@ show a snapshot of what student was look =cut sub get_student_view { - my ($symb,$username,$domain,$courseid,$target) = @_; + my ($symb,$username,$domain,$courseid,$target,$moreenv) = @_; my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb); - my (%old,%moreenv); + my (%form); my @elements=('symb','courseid','domain','username'); foreach my $element (@elements) { - $old{$element}=$ENV{'form.grade_'.$element}; - $moreenv{'form.grade_'.$element}=eval '$'.$element #' + $form{'grade_'.$element}=eval '$'.$element #' } - if ($target eq 'tex') {$moreenv{'form.grade_target'} = 'tex';} - &Apache::lonnet::appenv(%moreenv); - $feedurl=&Apache::lonnet::clutter($feedurl); - my $userview=&Apache::lonnet::ssi_body($feedurl); - &Apache::lonnet::delenv('form.grade_'); - foreach my $element (@elements) { - $ENV{'form.grade_'.$element}=$old{$element}; + if (defined($moreenv)) { + %form=(%form,%{$moreenv}); } + if (defined($target)) { $form{'grade_target'} = $target; } + $feedurl=&Apache::lonnet::clutter($feedurl); + my $userview=&Apache::lonnet::ssi_body($feedurl,%form); $userview=~s/\]*\>//gi; $userview=~s/\<\/body\>//gi; $userview=~s/\//gi; @@ -1835,19 +2479,14 @@ show a snapshot of how student was answe sub get_student_answers { my ($symb,$username,$domain,$courseid,%form) = @_; my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb); - my (%old,%moreenv); + my (%moreenv); my @elements=('symb','courseid','domain','username'); foreach my $element (@elements) { - $old{$element}=$ENV{'form.grade_'.$element}; - $moreenv{'form.grade_'.$element}=eval '$'.$element #' - } - $moreenv{'form.grade_target'}='answer'; - &Apache::lonnet::appenv(%moreenv); - my $userview=&Apache::lonnet::ssi('/res/'.$feedurl,%form); - &Apache::lonnet::delenv('form.grade_'); - foreach my $element (@elements) { - $ENV{'form.grade_'.$element}=$old{$element}; + $moreenv{'grade_'.$element}=eval '$'.$element #' } + $moreenv{'grade_target'}='answer'; + %moreenv=(%form,%moreenv); + my $userview=&Apache::lonnet::ssi('/res/'.$feedurl,%moreenv); return $userview; } @@ -1855,7 +2494,7 @@ sub get_student_answers { =item * &submlink() -Inputs: $text $uname $udom $symb +Inputs: $text $uname $udom $symb $target Returns: A link to grades.pm such as to see the SUBM view of a student @@ -1863,15 +2502,64 @@ Returns: A link to grades.pm such as to ############################################### sub submlink { - my ($text,$uname,$udom,$symb)=@_; + my ($text,$uname,$udom,$symb,$target)=@_; if (!($uname && $udom)) { (my $cursymb, my $courseid,$udom,$uname)= &Apache::lonxml::whichuser($symb); if (!$symb) { $symb=$cursymb; } } - if (!$symb) { $symb=&symbread(); } - return ''.$text.''; + if (!$symb) { $symb=&Apache::lonnet::symbread(); } + $symb=&Apache::lonnet::escape($symb); + if ($target) { $target="target=\"$target\""; } + return ''.$text.''; +} +############################################## + +=pod + +=item * &pgrdlink() + +Inputs: $text $uname $udom $symb $target + +Returns: A link to grades.pm such as to see the PGRD view of a student + +=cut + +############################################### +sub pgrdlink { + my $link=&submlink(@_); + $link=~s/(&command=submission)/$1&showgrading=yes/; + return $link; +} +############################################## + +=pod + +=item * &pprmlink() + +Inputs: $text $uname $udom $symb $target + +Returns: A link to parmset.pm such as to see the PPRM view of a +student and a specific resource + +=cut + +############################################### +sub pprmlink { + my ($text,$uname,$udom,$symb,$target)=@_; + if (!($uname && $udom)) { + (my $cursymb, my $courseid,$udom,$uname)= + &Apache::lonxml::whichuser($symb); + if (!$symb) { $symb=$cursymb; } + } + if (!$symb) { $symb=&Apache::lonnet::symbread(); } + $symb=&Apache::lonnet::escape($symb); + if ($target) { $target="target=\"$target\""; } + return ''.$text.''; } ############################################## @@ -1901,22 +2589,7 @@ sub maketime { my %th=@_; return POSIX::mktime( ($th{'seconds'},$th{'minutes'},$th{'hours'}, - $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,$th{'dlsav'})); -} - - -######################################### -# -# Retro-fixing of un-backward-compatible time format - -sub unsqltime { - my $timestamp=shift; - if ($timestamp=~/^(\d+)\-(\d+)\-(\d+)\s+(\d+)\:(\d+)\:(\d+)$/) { - $timestamp=&maketime( - 'year'=>$1,'month'=>$2,'day'=>$3, - 'hours'=>$4,'minutes'=>$5,'seconds'=>$6); - } - return $timestamp; + $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1)); } ######################################### @@ -1924,9 +2597,9 @@ sub unsqltime { sub findallcourses { my %courses=(); my $now=time; - foreach (keys %ENV) { + foreach (keys %env) { if ($_=~/^user\.role\.\w+\.\/(\w+)\/(\w+)/) { - my ($starttime,$endtime)=$ENV{$_}; + my ($starttime,$endtime)=$env{$_}; my $active=1; if ($starttime) { if ($now<$starttime) { $active=0; } @@ -1963,9 +2636,9 @@ sub determinedomain { if (! $domain) { # Determine domain if we have not been given one $domain = $Apache::lonnet::perlvar{'lonDefDomain'}; - if ($ENV{'user.domain'}) { $domain=$ENV{'user.domain'}; } - if ($ENV{'request.role.domain'}) { - $domain=$ENV{'request.role.domain'}; + if ($env{'user.domain'}) { $domain=$env{'user.domain'}; } + if ($env{'request.role.domain'}) { + $domain=$env{'request.role.domain'}; } } return $domain; @@ -1987,10 +2660,8 @@ sub domainlogo { my $domain = &determinedomain(shift); # See if there is a logo if (-e '/home/httpd/html/adm/lonDomLogos/'.$domain.'.gif') { - my $lonhttpdPort=$Apache::lonnet::perlvar{'lonhttpdPort'}; - if (!defined($lonhttpdPort)) { $lonhttpdPort='8080'; } - return ''.$domain.''; + my $logo=&lonhttpdurl("/adm/lonDomLogos/$domain.gif"); + return ''.$domain.''; } elsif(exists($Apache::lonnet::domaindescription{$domain})) { return $Apache::lonnet::domaindescription{$domain}; } else { @@ -2012,7 +2683,7 @@ Returns: value of designparamter $which ############################################## sub designparm { my ($which,$domain)=@_; - if ($ENV{'browser.blackwhite'} eq 'on') { + if ($env{'browser.blackwhite'} eq 'on') { if ($which=~/\.(font|alink|vlink|link)$/) { return '#000000'; } @@ -2023,8 +2694,8 @@ sub designparm { return '#CCCCCC'; } } - if ($ENV{'environment.color.'.$which}) { - return $ENV{'environment.color.'.$which}; + if ($env{'environment.color.'.$which}) { + return $env{'environment.color.'.$which}; } $domain=&determinedomain($domain); if ($designhash{$domain.'.'.$which}) { @@ -2066,6 +2737,13 @@ Inputs: =item * $forcereg, if page should register as content page (relevant for text interface only) +=item * $customtitle, overrides the $title in some way ???? + +=item * $notopbar, if true, keep the 'what is this' info but remove the + navigational links + +=item * $bgcolor, used to override the bg coor on a webpage to a specific value + =back Returns: A uniform header for LON-CAPA web pages. @@ -2076,23 +2754,12 @@ other decorations will be returned. =cut sub bodytag { - my ($title,$function,$addentries,$bodyonly,$domain,$forcereg)=@_; + my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,$customtitle, + $notopbar,$bgcolor)=@_; $title=&mt($title); - unless ($function) { - $function='student'; - if ($ENV{'request.role'}=~/^(cc|in|ta|ep)/) { - $function='coordinator'; - } - if ($ENV{'request.role'}=~/^(su|dc|ad|li)/) { - $function='admin'; - } - if (($ENV{'request.role'}=~/^(au|ca)/) || - ($ENV{'REQUEST_URI'}=~/^(\/priv|\~)/)) { - $function='author'; - } - } + $function = &get_users_function() if (!$function); my $img=&designparm($function.'.img',$domain); - my $pgbg=&designparm($function.'.pgbg',$domain); + my $pgbg= $bgcolor || &designparm($function.'.pgbg',$domain); my $tabbg=&designparm($function.'.tabbg',$domain); my $font=&designparm($function.'.font',$domain); my $link=&designparm($function.'.link',$domain); @@ -2102,16 +2769,16 @@ sub bodytag { # Accessibility font enhance unless ($addentries) { $addentries=''; } my $addstyle=''; - if ($ENV{'browser.fontenhance'} eq 'on') { + if ($env{'browser.fontenhance'} eq 'on') { $addstyle=' font-size: x-large;'; } # role and realm my ($role,$realm) - =&Apache::lonnet::plaintext((split(/\./,$ENV{'request.role'}))[0]); + =&Apache::lonnet::plaintext((split(/\./,$env{'request.role'}))[0]); # realm - if ($ENV{'request.course.id'}) { + if ($env{'request.course.id'}) { $realm= - $ENV{'course.'.$ENV{'request.course.id'}.'.description'}; + $env{'course.'.$env{'request.course.id'}.'.description'}; } unless ($realm) { $realm=' '; } # Set messages @@ -2121,33 +2788,122 @@ sub bodytag { if (!defined($lonhttpdPort)) { $lonhttpdPort='8080'; } # construct main body tag my $bodytag = < + END + &Apache::lontexconvert::jsMath_reset(); + if ($env{'environment.texengine'} eq 'jsMath') { + $bodytag.=&Apache::lontexconvert::jsMath_header(); + } + my $upperleft=''.$function.''; if ($bodyonly) { return $bodytag; - } elsif ($ENV{'browser.interface'} eq 'textual') { + } elsif ($env{'browser.interface'} eq 'textual') { # Accessibility + return $bodytag.&Apache::lonmenu::menubuttons($forcereg,'web', $forcereg). '

LON-CAPA: '.$title.'

'; - } elsif ($ENV{'environment.remote'} eq 'off') { + } elsif ($env{'environment.remote'} eq 'off') { # No Remote - return $bodytag.&Apache::lonmenu::menubuttons($forcereg,'web', - $forcereg). - '
'.$title. -'
'; + my $roleinfo=(< + + $env{'environment.firstname'} + $env{'environment.middlename'} + $env{'environment.lastname'} + $env{'environment.generation'} +   +
+$role  +
+$realm  + +ENDROLE + my $titleinfo = ''.$title.''; + if ($customtitle) { + $titleinfo = $customtitle; + } + + if ($env{'request.state'} eq 'construct') { + my ($uname,$thisdisfn)= + ($env{'request.filename'} =~ m|^/home/([^/]+)/public_html/(.*)|); + my $formaction='/priv/'.$uname.'/'.$thisdisfn; + $formaction=~s/\/+/\//g; + unless ($customtitle) { #this is for resources; directories have customtitle, and crumbs and select recent are created in lonpubdir.pm + my $parentpath = ''; + my $lastitem = ''; + if ($thisdisfn =~ m-(.+/)([^/]*)$-) { + $parentpath = $1; + $lastitem = $2; + } else { + $lastitem = $thisdisfn; + } + $titleinfo = &Apache::loncommon::help_open_menu('','','','',3,'Authoring'). + 'Construction Space: '. + '
' + .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv','','+1',1)."$lastitem
" + .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()') + .'
' + .&Apache::lonmenu::constspaceform(); + + } + $forcereg=1; + } + my $titletable = ''. + ''.$roleinfo.'
'. + $titleinfo.'
'; + if ($env{'request.state'} eq 'construct') { + if ($notopbar) { + $bodytag .= $titletable; + } else { + $bodytag .= &Apache::lonmenu::menubuttons($forcereg,'web',$forcereg,$titletable); + } + } else { + if ($notopbar) { + $bodytag .= $titletable; + } else { + $bodytag .= &Apache::lonmenu::menubuttons($forcereg,'web',$forcereg). + $titletable; + } + } + return $bodytag; } # # Top frame rendering, Remote is up # + my $titleinfo = ' '.$title.''; + if ($customtitle) { + $titleinfo = $customtitle; + } + # + # Extra info if you are the DC + my $dc_info = ''; + if ($env{'user.adv'} && exists($env{'user.role.dc./'. + $env{'course.'.$env{'request.course.id'}. + '.domain'}.'/'})) { + my $cid = $env{'request.course.id'}; + $dc_info.= $cid.' '.$env{'course.'.$cid.'.internal.coursecode'}; + $dc_info = '('.$dc_info.')'; + } + # Explicit link to get inline menu + my $menu='
 '.&mt('Switch to Inline Menu Mode').''; + # return(< @@ -2157,13 +2913,13 @@ $upperleft - $title - +$titleinfo $dc_info $menu + - $ENV{'environment.firstname'} - $ENV{'environment.middlename'} - $ENV{'environment.lastname'} - $ENV{'environment.generation'} + $env{'environment.firstname'} + $env{'environment.middlename'} + $env{'environment.lastname'} + $env{'environment.generation'}   @@ -2172,18 +2928,662 @@ $upperleft $realm  -
+
ENDBODY } ############################################### +############################################### + +=pod + +=back + +=head1 HTML Helpers + +=over 4 + +=item * &endbodytag() + +Returns a uniform footer for LON-CAPA web pages. + +Inputs: none + +=back + +=cut + +sub endbodytag { + my $endbodytag=''; + $endbodytag=&Apache::lontexconvert::jsMath_process()."\n".$endbodytag; + if ( exists( $env{'internal.head.redirect'} ) ) { + $endbodytag= + "
". + &mt('Continue').''. + $endbodytag; + } + return $endbodytag; +} + +=pod + +=over 4 + +=item * &headtag() + +Returns a uniform footer for LON-CAPA web pages. + +Inputs: $title - optional title for the head + $head_extra - optional extra HTML to put inside the + $args - optional arguments + redirect - array ref of seconds before redirect occurs + url to redirect to + (side effect of setting + $env{'internal.head.redirect'} to the url + redirected too) +=back + +=cut + +sub headtag { + my ($title,$head_extra,$args) = @_; + + my $result = + ''. + &Apache::lonxml::fontsettings(). + &Apache::lonhtmlcommon::htmlareaheaders(); + + if (ref($args->{'redirect'})) { + my ($time,$url) = @{$args->{'redirect'}}; + $url = &Apache::lonenc::check_encrypt($url); + $env{'internal.head.redirect'} = $url; + $result.=< + +ADDMETA + } + if (!defined($title)) { + $title = 'The LearningOnline Network with CAPA'; + } + + $result .= ' LON-CAPA '.&mt($title).''.$head_extra; + + return $result; +} + +=pod + +=over 4 + +=item * &endheadtag() + +Returns a uniform for LON-CAPA web pages. + +Inputs: none + +=back + +=cut + +sub endheadtag { + return ''; +} + +=pod + +=over 4 + +=item * &head() + +Returns a uniform complete .. section for LON-CAPA web pages. + +Inputs: $title - optional title for the page + $head_extra - optional extra HTML to put inside the +=back + +=cut + +sub head { + my ($title,$head_extra) = @_; + return &headtag($title,$head_extra).&endheadtag(); +} + +=pod + +=over 4 + +=item * &start_page() + +Returns a complete .. section for LON-CAPA web pages. + +Inputs: $title - optional title for the page + $head_extra - optional extra HTML to incude inside the + $args - additional optional args supported are: + only_body -> is true will set &bodytag() onlybodytag + arg on + no_nav_bar -> is true will set &bodytag() notopbar arg on + add_entries -> additional attributes to add to the + domain -> force to color decorate a page for a + specific domain + function -> force usage of a specific rolish color + scheme + redirect -> see &headtag() + bgcolor -> override the default page bg color + js_ready -> return a string ready for being used in + a javascript writeln + force_register -> if is true will turn on the &bodytag() + $forcereg arg + +=back + +=cut + +sub start_page { + my ($title,$head_extra,$args) = @_; + my %head_args; + if (defined($args->{'redirect'})) { + $head_args{'redirect'} = $args->{'redirect'}; + } + $env{'internal.start_page'}++; + my $result = + &Apache::lonxml::xmlbegin(). + &headtag($title,$head_extra,\%head_args).&endheadtag(). + &bodytag($title, + $args->{'function'}, $args->{'add_entries'}, + $args->{'only_body'}, $args->{'domain'}, + $args->{'force_register'}, undef, + $args->{'no_nav_bar'}, $args->{'bgcolor'}); + if ($args->{'js_ready'}) { + $result = &js_ready($result); + } + return $result; +} + +=pod + +=over 4 + +=item * &head() + +Returns a complete section for LON-CAPA web pages. + +Inputs: $args - additional optional args supported are: + js_ready -> return a string ready for being used in + a javascript writeln +=back + +=cut + +sub end_page { + my ($args) = @_; + $env{'internal.end_page'}++; + my $result = &endbodytag()."\n"; + if ($args->{'js_ready'}) { + $result = &js_ready($result); + } + return $result; +} + +sub js_ready { + my ($result) = @_; + $result = &HTML::Entities::encode($result,'<>&"'); + + $result =~ s/[\n\r]/ /g; + $result =~ s/'/\\'/g; + + return $result; +} + +sub validate_page { + if ( exists($env{'internal.start_page'}) + && $env{'internal.start_page'} > 1) { + &Apache::lonnet::logthis('start_page called multiple times '. + $ENV{'request.filename'}); + } + if ( exists($env{'internal.end_page'}) + && $env{'internal.end_page'} > 1) { + &Apache::lonnet::logthis('end_page called multiple times '. + $env{'request.filename'}); + } + if ( exists($env{'internal.start_page'}) + && ! exists($env{'internal.end_page'})) { + &Apache::lonnet::logthis('start_page called without end_page '. + $env{'request.filename'}); + } + if ( ! exists($env{'internal.start_page'}) + && exists($env{'internal.end_page'})) { + &Apache::lonnet::logthis('end_page called without start_page'. + $env{'request.filename'}); + } +} + +############################################### + +=pod + +=over 4 + +=item get_users_function + +Used by &bodytag to determine the current users primary role. +Returns either 'student','coordinator','admin', or 'author'. + +=cut + +############################################### +sub get_users_function { + my $function = 'student'; + if ($env{'request.role'}=~/^(cc|in|ta|ep)/) { + $function='coordinator'; + } + if ($env{'request.role'}=~/^(su|dc|ad|li)/) { + $function='admin'; + } + if (($env{'request.role'}=~/^(au|ca)/) || + ($ENV{'REQUEST_URI'}=~/^(\/priv|\~)/)) { + $function='author'; + } + return $function; +} + +############################################### + +=pod + +=item check_user_status + +Determines current status of supplied role for a +specific user. Roles can be active, previous or future. + +Inputs: +user's domain, user's username, course's domain, +course's number, optional section/group. + +Outputs: +role status: active, previous or future. + +=cut + +sub check_user_status { + my ($udom,$uname,$cdom,$crs,$role,$secgrp) = @_; + my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname); + my @uroles = keys %userinfo; + my $srchstr; + my $active_chk = 'none'; + if (@uroles > 0) { + if (($role eq 'cc') || ($secgrp eq '') || (!defined($secgrp))) { + $srchstr = '/'.$cdom.'/'.$crs.'_'.$role; + } else { + $srchstr = '/'.$cdom.'/'.$crs.'/'.$secgrp.'_'.$role; } + if (grep/^$srchstr$/,@uroles) { + my $role_end = 0; + my $role_start = 0; + $active_chk = 'active'; + if ($userinfo{$srchstr} =~ m/^($role)_(\d+)/) { + $role_end = $2; + if ($userinfo{$srchstr} =~ m/^($role)_($role_end)_(\d+)$/) { + $role_start = $3; + } + } + if ($role_start > 0) { + if (time < $role_start) { + $active_chk = 'future'; + } + } + if ($role_end > 0) { + if (time > $role_end) { + $active_chk = 'previous'; + } + } + } + } + return $active_chk; +} + +############################################### + +=pod + +=item get_sections + +Determines all the sections for a course including +sections with students and sections containing other roles. +Incoming parameters: domain, course number, reference to +section hash (keys to be section/group IDs), reference to +array containing roles for which sections should be gathered +(optional). If the fourth argument is undefined, sections +are gathered for any role. + +Returns number of sections. + +=cut + +############################################### +sub get_sections { + my ($cdom,$cnum,$sectioncount,$possible_roles) = @_; + if (!($cdom && $cnum)) { return 0; } + my $numsections = 0; + + if (!defined($possible_roles) || (grep/^st$/,@$possible_roles)) { + my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum); + my $sec_index = &Apache::loncoursedata::CL_SECTION(); + my $status_index = &Apache::loncoursedata::CL_STATUS(); + while (my ($student,$data) = each %$classlist) { + my ($section,$status) = ($data->[$sec_index], + $data->[$status_index]); + unless ($section eq '-1' || $section =~ /^\s*$/) { + if (!defined($$sectioncount{$section})) { $numsections++; } + $$sectioncount{$section}++; + } + } + } + my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum); + foreach my $user (sort(keys(%courseroles))) { + if ($user !~ /^(\w{2})/) { next; } + my ($role) = ($user =~ /^(\w{2})/); + if ($possible_roles && !(grep(/^$role$/,@$possible_roles))) { next; } + my $section; + if ($role eq 'cr' && + $user =~ m-^$role/[^/]*/[^/]*/[^/]*:[^:]*:[^:]*:(\w+)-) { + $section=$1; + } + if ($user =~ /^$role:[^:]*:[^:]*:(\w+)/) { $section=$1; } + if (!defined($section) || $section eq '-1') { next; } + if (!defined($$sectioncount{$section})) { $numsections++; } + $$sectioncount{$section}++; + } + return $numsections; +} + +############################################### + +=pod + +=item coursegroups + +Retrieve information about groups in a course, + +Input: +1. Reference to hash to populate with group information. +2. Optional course domain +3. Optional course number +4. Optional group name + +Course domain and number will be taken from user's +environment if not supplied. Optional group name will' +be passed to lonnet::get_coursegroups() as a regexp to +use in the call to the dump function. + +Output +Returns number of groups in the course (subject to the +optional group name filter). + +Side effects: +Populates the referenced curr_groups hash, with key, +value pairs. Keys are group names, corresponding values +are scalars containing group information in XML. This +can be sent to &get_group_settings() to be parsed. + +=cut + +############################################### + +sub coursegroups { + my ($curr_groups,$cdom,$cnum,$group) = @_; + my $numgroups; + if (!defined($cdom) || !defined($cnum)) { + my $cid = $env{'request.course.id'}; + $cdom = $env{'course.'.$cid.'.domain'}; + $cnum = $env{'course.'.$cid.'.num'}; + } + %{$curr_groups} = &Apache::lonnet::get_coursegroups($cdom,$cnum,$group); + my ($tmp) = keys(%{$curr_groups}); + if ($tmp=~/^error:/) { + unless ($tmp eq 'error: 2 tie(GDBM) Failed while attempting dump') { + &logthis('Error retrieving groups: '.$tmp.' in '.$cnum.':'. + $cdom); + } + $numgroups = 0; + } else { + $numgroups = keys(%{$curr_groups}); + } + return $numgroups; +} + +############################################### + +=pod + +=item get_group_settings + +Uses TokeParser to extract group information from the +XML used to describe course groups. + +Input: +Scalar containing XML - as retrieved from &coursegroups(). + +Output: +Hash containing group information as key=values for (a), and +hash of hashes for (b) + +Keys (in two categories): +(a) groupname, creator, creation, modified, startdate,enddate. +Corresponding values are name of the group, creator of the group +(username:domain), UNIX time for date group was created, and +settings were last modified, and default start and end access +times for group members. + +(b) functions returned in hash of hashes. +Outer hash key is functions. +Inner hash keys are chat,discussion,email,files,homepage,roster. +Corresponding values are either on or off, depending on +whether this type of functionality is available for the group. + +=cut + +############################################### + +sub get_group_settings { + my ($groupinfo)=@_; + my $parser=HTML::TokeParser->new(\$groupinfo); + my $token; + my $tool = ''; + my $role = ''; + my %content=(); + while ($token=$parser->get_token) { + if ($token->[0] eq 'S') { + my $entry=$token->[1]; + if ($entry eq 'functions' || $entry eq 'autosec') { + %{$content{$entry}} = (); + $tool = $entry; + } elsif ($entry eq 'role') { + if ($tool eq 'autosec') { + $role = $token->[2]{id}; + } + } else { + my $value=$parser->get_text('/'.$entry); + if ($entry eq 'name') { + if ($tool eq 'functions') { + my $function = $token->[2]{id}; + $content{$tool}{$function} = $value; + } + } elsif ($entry eq 'groupname') { + $content{$entry}=&Apache::lonnet::unescape($value); + } elsif (($entry eq 'roles') || ($entry eq 'types') || + ($entry eq 'sectionpick') || ($entry eq 'defpriv')) { + push(@{$content{$entry}},$value); + } elsif ($entry eq 'section') { + if ($tool eq 'autosec' && $role ne '') { + push(@{$content{$tool}{$role}},$value); + } + } else { + $content{$entry}=$value; + } + } + } elsif ($token->[0] eq 'E') { + if ($token->[1] eq 'functions' || $token->[1] eq 'autosec') { + $tool = ''; + } elsif ($token->[1] eq 'role') { + $role = ''; + } + + } + } + return %content; +} + +sub check_group_access { + my ($group) = @_; + my $access = 1; + my $now = time; + my ($start,$end) = split(/\./,$env{'user.role.gr/'.$env{'request.course,id'}.'/'.$group}); + if (($end!=0) && ($end<$now)) { $access = 0; } + if (($start!=0) && ($start>$now)) { $access=0; } + return $access; +} + +############################################### + +=pod + +=item get_course_users + +Retrieves usernames:domains for users in the specified course +with specific role(s), and access status. + +Incoming parameters: +1. course domain +2. course number +3. access status: users must have - either active, +previous, future, or all. +4. reference to array of permissible roles +5. reference to array of section restrictions (optional) +6. reference to results object (hash of hashes). +7. reference to optional userdata hash +Keys of top level hash are roles. +Keys of inner hashes are username:domain, with +values set to access type. +Optional userdata hash returns an array with arguments in the +same order as loncoursedata::get_classlist() for student data. + +Entries for end, start, section and status are blank because +of the possibility of multiple values for non-student roles. + +=cut + +############################################### + +sub get_course_users { + my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata) = @_; + my %idx = (); + + $idx{udom} = &Apache::loncoursedata::CL_SDOM(); + $idx{uname} = &Apache::loncoursedata::CL_SNAME(); + $idx{end} = &Apache::loncoursedata::CL_END(); + $idx{start} = &Apache::loncoursedata::CL_START(); + $idx{id} = &Apache::loncoursedata::CL_ID(); + $idx{section} = &Apache::loncoursedata::CL_SECTION(); + $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME(); + $idx{status} = &Apache::loncoursedata::CL_STATUS(); + + if (grep(/^st$/,@{$roles})) { + my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist($cdom,$cnum); + my $now = time; + foreach my $student (keys(%{$classlist})) { + my $match = 0; + if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) { + unless(grep(/^\Q$$classlist{$student}[$idx{section}]\E$/, + @{$sections})) { + next; + } + } + if (defined($$types{'active'})) { + if ($$classlist{$student}[$idx{status}] eq 'Active') { + push(@{$$users{st}{$student}},'active'); + $match = 1; + } + } + if (defined($$types{'previous'})) { + if ($$classlist{$student}[$idx{end}] <= $now) { + push(@{$$users{st}{$student}},'previous'); + $match = 1; + } + } + if (defined($$types{'future'})) { + if (($$classlist{$student}[$idx{start}] > $now) && ($$classlist{$student}[$idx{end}] > $now) || ($$classlist{$student}[$idx{end}] == 0) || ($$classlist{$student}[$idx{end}] eq '')) { + push(@{$$users{st}{$student}},'future'); + $match = 1; + } + } + if ($match && defined($userdata)) { + $$userdata{$student} = $$classlist{$student}; + } + } + } + if ((@{$roles} > 0) && (@{$roles} ne "st")) { + my @coursepersonnel = &Apache::lonnet::getkeys('nohist_userroles',$cdom,$cnum); + foreach my $person (@coursepersonnel) { + my $match = 0; + my ($role,$user) = ($person =~ /^([^:]*):([^:]+:[^:]+)/); + $user =~ s/:$//; + if (($role) && (grep(/^\Q$role\E$/,@{$roles}))) { + my ($uname,$udom,$usec) = split(/:/,$user); + if ($usec ne '' && (ref($sections) eq 'ARRAY') && + @{$sections} > 0) { + unless(grep(/^\Q$usec\E$/,@{$sections})) { + next; + } + } + if ($uname ne '' && $udom ne '') { + my $status = &check_user_status($udom,$uname,$cdom,$cnum,$role); + foreach my $type (keys(%{$types})) { + if ($status eq $type) { + @{$$users{$role}{$user}} = $type; + $match = 1; + } + } + if ($match && defined($userdata) && + !exists($$userdata{$uname.':'.$udom})) { + &get_user_info($udom,$uname,\%idx,$userdata); + } + } + } + } + if (grep(/^ow$/,@{$roles})) { + if ((defined($cdom)) && (defined($cnum))) { + my %csettings = &Apache::lonnet::get('environment',['internal.courseowner'],$cdom,$cnum); + if ( defined($csettings{'internal.courseowner'}) ) { + my $owner = $csettings{'internal.courseowner'}; + @{$$users{'ow'}{$owner.':'.$cdom}} = 'any'; + if (defined($userdata) && + !exists($$userdata{$owner.':'.$cdom})) { + &get_user_info($cdom,$owner,\%idx,$userdata); + } + } + } + } + } + return; +} + +sub get_user_info { + my ($udom,$uname,$idx,$userdata) = @_; + $$userdata{$uname.':'.$udom}[$$idx{fullname}] = + &plainname($uname,$udom,'lastname'); + $$userdata{$uname.':'.$udom}[$$idx{uname}] = $uname; + $$userdata{$uname.':'.$udom}[$$idx{udom}] = $udom; + return; +} + +############################################### sub get_posted_cgi { my $r=shift; my $buffer; - - $r->read($buffer,$r->header_in('Content-length'),0); + if ($r->header_in('Content-length')) { + $r->read($buffer,$r->header_in('Content-length'),0); + } unless ($buffer=~/^(\-+\w+)\s+Content\-Disposition\:\s*form\-data/si) { my @pairs=split(/&/,$buffer); my $pair; @@ -2208,8 +3608,8 @@ sub get_posted_cgi { if ($name) { chomp($value); if ($fname) { - $ENV{"form.$name.filename"}=$fname; - $ENV{"form.$name.mimetype"}=$fmime; + $env{"form.$name.filename"}=$fname; + $env{"form.$name.mimetype"}=$fmime; } else { $value=~s/\s+$//s; } @@ -2241,7 +3641,7 @@ sub get_posted_cgi { } } } - $ENV{'request.method'}=$ENV{'REQUEST_METHOD'}; + $env{'request.method'}=$ENV{'REQUEST_METHOD'}; $r->method_number(M_GET); $r->method('GET'); $r->headers_in->unset('Content-length'); @@ -2251,14 +3651,14 @@ sub get_posted_cgi { =item * get_unprocessed_cgi($query,$possible_names) -Modify the %ENV hash to contain unprocessed CGI form parameters held in +Modify the %env hash to contain unprocessed CGI form parameters held in $query. The parameters listed in $possible_names (an array reference), -will be set in $ENV{'form.name'} if they do not already exist. +will be set in $env{'form.name'} if they do not already exist. Typically called with $ENV{'QUERY_STRING'} as the first parameter. $possible_names is an ref to an array of form element names. As an example: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']); -will result in $ENV{'form.uname'} and $ENV{'form.udom'} being set. +will result in $env{'form.uname'} and $env{'form.udom'} being set. =cut @@ -2271,8 +3671,7 @@ sub get_unprocessed_cgi { if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) { $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; - &Apache::lonxml::debug("Seting :$name: to :$value:"); - unless (defined($ENV{'form.'.$name})) { &add_to_env('form.'.$name,$value) }; + unless (defined($env{'form.'.$name})) { &add_to_env('form.'.$name,$value) }; } } } @@ -2286,12 +3685,12 @@ returns cache-controlling header code =cut sub cacheheader { - unless ($ENV{'request.method'} eq 'GET') { return ''; } - my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime); - my $output .=' + unless ($env{'request.method'} eq 'GET') { return ''; } + my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime); + my $output .=' '; - return $output; + return $output; } =pod @@ -2303,27 +3702,38 @@ specifies header code to not have cache =cut sub no_cache { - my ($r) = @_; - unless ($ENV{'request.method'} eq 'GET') { return ''; } - #my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime); - $r->no_cache(1); - $r->header_out("Pragma" => "no-cache"); - #$r->header_out("Expires" => $date); + my ($r) = @_; + if ($ENV{'REQUEST_METHOD'} ne 'GET' && + $env{'request.method'} ne 'GET') { return ''; } + my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime(time)); + $r->no_cache(1); + $r->header_out("Expires" => $date); + $r->header_out("Pragma" => "no-cache"); } sub content_type { - my ($r,$type,$charset) = @_; - unless ($charset) { - $charset=&Apache::lonlocal::current_encoding; - } - $r->content_type($type.($charset?'; charset='.$charset:'')); + my ($r,$type,$charset) = @_; + if ($r) { + # Note that printout.pl calls this with undef for $r. + &no_cache($r); + } + if ($env{'browser.mathml'} && $type eq 'text/html') { $type='text/xml'; } + unless ($charset) { + $charset=&Apache::lonlocal::current_encoding; + } + if ($charset) { $type.='; charset='.$charset; } + if ($r) { + $r->content_type($type); + } else { + print("Content-type: $type\n\n"); + } } =pod =item * add_to_env($name,$value) -adds $name to the %ENV hash with value +adds $name to the %env hash with value $value, if $name already exists, the entry is converted to an array reference and $value is added to the array. @@ -2331,18 +3741,18 @@ reference and $value is added to the arr sub add_to_env { my ($name,$value)=@_; - if (defined($ENV{$name})) { - if (ref($ENV{$name})) { + if (defined($env{$name})) { + if (ref($env{$name})) { #already have multiple values - push(@{ $ENV{$name} },$value); + push(@{ $env{$name} },$value); } else { #first time seeing multiple values, convert hash entry to an arrayref - my $first=$ENV{$name}; - undef($ENV{$name}); - push(@{ $ENV{$name} },$first,$value); + my $first=$env{$name}; + undef($env{$name}); + push(@{ $env{$name} },$first,$value); } } else { - $ENV{$name}=$value; + $env{$name}=$value; } } @@ -2350,7 +3760,7 @@ sub add_to_env { =item * get_env_multiple($name) -gets $name from the %ENV hash, it seemlessly handles the cases where multiple +gets $name from the %env hash, it seemlessly handles the cases where multiple values may be defined and end up as an array ref. returns an array of values @@ -2360,12 +3770,12 @@ returns an array of values sub get_env_multiple { my ($name) = @_; my @values; - if (defined($ENV{$name})) { + if (defined($env{$name})) { # exists is it an array - if (ref($ENV{$name})) { - @values=@{ $ENV{$name} }; + if (ref($env{$name})) { + @values=@{ $env{$name} }; } else { - $values[0]=$ENV{$name}; + $values[0]=$env{$name}; } } return(@values); @@ -2383,25 +3793,25 @@ sub get_env_multiple { =item * upfile_store($r) Store uploaded file, $r should be the HTTP Request object, -needs $ENV{'form.upfile'} +needs $env{'form.upfile'} returns $datatoken to be put into hidden field =cut sub upfile_store { my $r=shift; - $ENV{'form.upfile'}=~s/\r/\n/gs; - $ENV{'form.upfile'}=~s/\f/\n/gs; - $ENV{'form.upfile'}=~s/\n+/\n/gs; - $ENV{'form.upfile'}=~s/\n+$//gs; + $env{'form.upfile'}=~s/\r/\n/gs; + $env{'form.upfile'}=~s/\f/\n/gs; + $env{'form.upfile'}=~s/\n+/\n/gs; + $env{'form.upfile'}=~s/\n+$//gs; - my $datatoken=$ENV{'user.name'}.'_'.$ENV{'user.domain'}. - '_enroll_'.$ENV{'request.course.id'}.'_'.time.'_'.$$; + my $datatoken=$env{'user.name'}.'_'.$env{'user.domain'}. + '_enroll_'.$env{'request.course.id'}.'_'.time.'_'.$$; { my $datafile = $r->dir_config('lonDaemons'). '/tmp/'.$datatoken.'.tmp'; if ( open(my $fh,">$datafile") ) { - print $fh $ENV{'form.upfile'}; + print $fh $env{'form.upfile'}; close($fh); } } @@ -2413,8 +3823,8 @@ sub upfile_store { =item * load_tmp_file($r) Load uploaded file from tmp, $r should be the HTTP Request object, -needs $ENV{'form.datatoken'}, -sets $ENV{'form.upfile'} to the contents of the file +needs $env{'form.datatoken'}, +sets $env{'form.upfile'} to the contents of the file =cut @@ -2423,13 +3833,13 @@ sub load_tmp_file { my @studentdata=(); { my $studentfile = $r->dir_config('lonDaemons'). - '/tmp/'.$ENV{'form.datatoken'}.'.tmp'; + '/tmp/'.$env{'form.datatoken'}.'.tmp'; if ( open(my $fh,"<$studentfile") ) { @studentdata=<$fh>; close($fh); } } - $ENV{'form.upfile'}=join('',@studentdata); + $env{'form.upfile'}=join('',@studentdata); } =pod @@ -2438,14 +3848,19 @@ sub load_tmp_file { Separate uploaded file into records returns array of records, -needs $ENV{'form.upfile'} and $ENV{'form.upfiletype'} +needs $env{'form.upfile'} and $env{'form.upfiletype'} =cut sub upfile_record_sep { - if ($ENV{'form.upfiletype'} eq 'xml') { + if ($env{'form.upfiletype'} eq 'xml') { } else { - return split(/\n/,$ENV{'form.upfile'}); + my @records; + foreach my $line (split(/\n/,$env{'form.upfile'})) { + if ($line=~/^\s*$/) { next; } + push(@records,$line); + } + return @records; } } @@ -2453,30 +3868,35 @@ sub upfile_record_sep { =item * record_sep($record) -Separate a record into fields $record should be an item from the upfile_record_sep(), needs $ENV{'form.upfiletype'} +Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'} =cut +sub takeleft { + my $index=shift; + return substr('0000'.$index,-4,4); +} + sub record_sep { my $record=shift; my %components=(); - if ($ENV{'form.upfiletype'} eq 'xml') { - } elsif ($ENV{'form.upfiletype'} eq 'space') { + if ($env{'form.upfiletype'} eq 'xml') { + } elsif ($env{'form.upfiletype'} eq 'space') { my $i=0; foreach (split(/\s+/,$record)) { my $field=$_; $field=~s/^(\"|\')//; $field=~s/(\"|\')$//; - $components{$i}=$field; + $components{&takeleft($i)}=$field; $i++; } - } elsif ($ENV{'form.upfiletype'} eq 'tab') { + } elsif ($env{'form.upfiletype'} eq 'tab') { my $i=0; - foreach (split(/\t+/,$record)) { + foreach (split(/\t/,$record)) { my $field=$_; $field=~s/^(\"|\')//; $field=~s/(\"|\')$//; - $components{$i}=$field; + $components{&takeleft($i)}=$field; $i++; } } else { @@ -2494,7 +3914,7 @@ sub record_sep { $field=~s/^\s*$delimiter//; $field=~s/$delimiter\s*$//; } - $components{$i}=$field; + $components{&takeleft($i)}=$field; $i++; } } @@ -2531,6 +3951,22 @@ sub upfile_select_html { return $Str; } +sub get_samples { + my ($records,$toget) = @_; + my @samples=({}); + my $got=0; + foreach my $rec (@$records) { + my %temp = &record_sep($rec); + if (! grep(/\S/, values(%temp))) { next; } + if (%temp) { + $samples[$got]=\%temp; + $got++; + if ($got == $toget) { last; } + } + } + return \@samples; +} + ###################################################### ###################################################### @@ -2548,18 +3984,15 @@ Apache Request ref, $records is an array ###################################################### sub csv_print_samples { my ($r,$records) = @_; - my (%sone,%stwo,%sthree); - %sone=&record_sep($$records[0]); - if (defined($$records[1])) {%stwo=&record_sep($$records[1]);} - if (defined($$records[2])) {%sthree=&record_sep($$records[2]);} - # + my $samples = &get_samples($records,3); + $r->print(&mt('Samples').'
'); - foreach (sort({$a <=> $b} keys(%sone))) { + foreach (sort({$a <=> $b} keys(%{ $samples->[0] }))) { $r->print(''); } $r->print(''); - foreach my $hash (\%sone,\%stwo,\%sthree) { + foreach my $hash (@$samples) { $r->print(''); - foreach (sort({$a <=> $b} keys(%sone))) { + foreach (sort({$a <=> $b} keys(%{ $samples->[0] }))) { $r->print(''); @@ -2580,7 +4013,7 @@ Prints a table to create associations be $r is an Apache Request ref, $records is an arrayref from &Apache::loncommon::upfile_record_sep, -$d is an array of 2 element arrays (internal name, displayed name) +$d is an array of 2 element arrays (internal name, displayed name,defaultcol) =cut @@ -2588,21 +4021,23 @@ $d is an array of 2 element arrays (inte ###################################################### sub csv_print_select_table { my ($r,$records,$d) = @_; - my $i=0;my %sone; - %sone=&record_sep($$records[0]); + my $i=0; + my $samples = &get_samples($records,1); $r->print(&mt('Associate columns with student attributes.')."\n". '
'.&mt('Column [_1]',($_+1)).'
'); if (defined($$hash{$_})) { $r->print($$hash{$_}); } $r->print('
'. ''. ''."\n"); foreach (@$d) { - my ($value,$display)=@{ $_ }; + my ($value,$display,$defaultcol)=@{ $_ }; $r->print(''); $r->print(''."\n"); $i++; @@ -2630,26 +4065,27 @@ $d is an array of 2 element arrays (inte ###################################################### sub csv_samples_select_table { my ($r,$records,$d) = @_; - my %sone; my %stwo; my %sthree; my $i=0; # + my $samples = &get_samples($records,3); $r->print('
'.&mt('Attribute').''.&mt('Column').'
'.$display.'
'); - %sone=&record_sep($$records[0]); - if (defined($$records[1])) {%stwo=&record_sep($$records[1]);} - if (defined($$records[2])) {%sthree=&record_sep($$records[2]);} - # - foreach (sort keys %sone) { + + foreach my $key (sort(keys(%{ $samples->[0] }))) { $r->print(''); $i++; } @@ -2738,7 +4174,7 @@ the routine &Apache::lonnet::transfer_pr my $uniq=0; sub get_cgi_id { $uniq=($uniq+1)%100000; - return (time.'_'.$uniq); + return (time.'_'.$$.'_'.$uniq); } ############################################################ @@ -2769,9 +4205,14 @@ If $Max is < any data point, the graph w =item $colors: array ref holding the colors to be used for the data sets when they are plotted. If undefined, default values will be used. +=item $labels: array ref holding the labels to use on the x-axis for the bars. + =item @Values: An array of array references. Each array reference holds data to be plotted in a stacked bar chart. +=item If the final element of @Values is a hash reference the key/value +pairs will be added to the graph definition. + =back Returns: @@ -2784,7 +4225,7 @@ information for the plot. ############################################################ ############################################################ sub DrawBarGraph { - my ($Title,$xlabel,$ylabel,$Max,$colors,@Values)=@_; + my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_; # if (! defined($colors)) { $colors = ['#33ff00', @@ -2792,13 +4233,28 @@ sub DrawBarGraph { '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66', ]; } + my $extra_settings = {}; + if (ref($Values[-1]) eq 'HASH') { + $extra_settings = pop(@Values); + } # my $identifier = &get_cgi_id(); my $id = 'cgi.'.$identifier; if (! @Values || ref($Values[0]) ne 'ARRAY') { return ''; } + # + my @Labels; + if (defined($labels)) { + @Labels = @$labels; + } else { + for (my $i=0;$i<@{$Values[0]};$i++) { + push (@Labels,$i+1); + } + } + # my $NumBars = scalar(@{$Values[0]}); + if ($NumBars < scalar(@Labels)) { $NumBars = scalar(@Labels); } my %ValuesHash; my $NumSets=1; foreach my $array (@Values) { @@ -2808,7 +4264,15 @@ sub DrawBarGraph { } # my ($height,$width,$xskip,$bar_width) = (200,120,1,15); - if ($NumBars < 10) { + if ($NumBars < 3) { + $width = 120+$NumBars*32; + $xskip = 1; + $bar_width = 30; + } elsif ($NumBars < 5) { + $width = 120+$NumBars*20; + $xskip = 1; + $bar_width = 20; + } elsif ($NumBars < 10) { $width = 120+$NumBars*15; $xskip = 1; $bar_width = 15; @@ -2826,11 +4290,6 @@ sub DrawBarGraph { $bar_width = 4; } # - my @Labels; - for (my $i=0;$i<@{$Values[0]};$i++) { - push (@Labels,$i+1); - } - # $Max = 1 if ($Max < 1); if ( int($Max) < $Max ) { $Max++; @@ -2853,6 +4312,11 @@ sub DrawBarGraph { $ValuesHash{$id.'.bar_width'} = $bar_width; $ValuesHash{$id.'.labels'} = join(',',@Labels); # + # Deal with other parameters + while (my ($key,$value) = each(%$extra_settings)) { + $ValuesHash{$id.'.'.$key} = $value; + } + # &Apache::lonnet::appenv(%ValuesHash); return ''; } @@ -2887,7 +4351,7 @@ plotted in. If undefined, default value =item $Xlabels: Array ref containing the labels to be used for the X-axis. =item $Ydata: Array ref containing Array refs. -Each of the contained arrays will be plotted as a seperate curve. +Each of the contained arrays will be plotted as a separate curve. =item %Values: hash indicating or overriding any default values which are passed to graph.png. @@ -3087,8 +4551,8 @@ Inputs: sub chartlink { my ($linktext, $sname, $sdomain) = @_; my $link = ''.$linktext.''; } @@ -3129,34 +4593,35 @@ Returns: both routines return nothing sub store_course_settings { # save to the environment # appenv the same items, just to be safe - my $courseid = $ENV{'request.course.id'}; - my $coursedom = $ENV{'course.'.$courseid.'.domain'}; + my $courseid = $env{'request.course.id'}; + my $udom = $env{'user.domain'}; + my $uname = $env{'user.name'}; my ($prefix,$Settings) = @_; my %SaveHash; my %AppHash; while (my ($setting,$type) = each(%$Settings)) { - my $basename = 'env.internal.'.$prefix.'.'.$setting; - my $envname = 'course.'.$courseid.'.'.$basename; - if (exists($ENV{'form.'.$setting})) { + my $basename = join('.','internal',$courseid,$prefix,$setting); + my $envname = 'environment.'.$basename; + if (exists($env{'form.'.$setting})) { # Save this value away if ($type eq 'scalar' && - (! exists($ENV{$envname}) || - $ENV{$envname} ne $ENV{'form.'.$setting})) { - $SaveHash{$basename} = $ENV{'form.'.$setting}; - $AppHash{$envname} = $ENV{'form.'.$setting}; + (! exists($env{$envname}) || + $env{$envname} ne $env{'form.'.$setting})) { + $SaveHash{$basename} = $env{'form.'.$setting}; + $AppHash{$envname} = $env{'form.'.$setting}; } elsif ($type eq 'array') { my $stored_form; - if (ref($ENV{'form.'.$setting})) { + if (ref($env{'form.'.$setting})) { $stored_form = join(',', map { &Apache::lonnet::escape($_); - } sort(@{$ENV{'form.'.$setting}})); + } sort(@{$env{'form.'.$setting}})); } else { $stored_form = - &Apache::lonnet::escape($ENV{'form.'.$setting}); + &Apache::lonnet::escape($env{'form.'.$setting}); } # Determine if the array contents are the same. - if ($stored_form ne $ENV{$envname}) { + if ($stored_form ne $env{$envname}) { $SaveHash{$basename} = $stored_form; $AppHash{$envname} = $stored_form; } @@ -3164,8 +4629,7 @@ sub store_course_settings { } } my $put_result = &Apache::lonnet::put('environment',\%SaveHash, - $coursedom, - $ENV{'course.'.$courseid.'.num'}); + $udom,$uname); if ($put_result !~ /^(ok|delayed)/) { &Apache::lonnet::logthis('unable to save form parameters, '. 'got error:'.$put_result); @@ -3176,20 +4640,20 @@ sub store_course_settings { } sub restore_course_settings { - my $courseid = $ENV{'request.course.id'}; + my $courseid = $env{'request.course.id'}; my ($prefix,$Settings) = @_; while (my ($setting,$type) = each(%$Settings)) { - next if (exists($ENV{'form.'.$setting})); - my $envname = 'course.'.$courseid.'.env.internal.'.$prefix. + next if (exists($env{'form.'.$setting})); + my $envname = 'environment.internal.'.$courseid.'.'.$prefix. '.'.$setting; - if (exists($ENV{$envname})) { + if (exists($env{$envname})) { if ($type eq 'scalar') { - $ENV{'form.'.$setting} = $ENV{$envname}; + $env{'form.'.$setting} = $env{$envname}; } elsif ($type eq 'array') { - $ENV{'form.'.$setting} = [ + $env{'form.'.$setting} = [ map { &Apache::lonnet::unescape($_); - } split(',',$ENV{$envname}) + } split(',',$env{$envname}) ]; } } @@ -3211,16 +4675,59 @@ sub propath { sub icon { my ($file)=@_; - my @file_ext = split(/\./,$file); - my $curfext = $file_ext[-1]; - my $iconname="unknown.gif"; + my $curfext = (split(/\./,$file))[-1]; + my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif'; my $embstyle = &Apache::loncommon::fileembstyle($curfext); - # The unless conditional that follows is a bit of overkill - $iconname = $curfext.".gif" unless - (!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn'); - return $Apache::lonnet::perlvar{'lonIconsURL'}."/$iconname"; + if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) { + if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/'. + $Apache::lonnet::perlvar{'lonIconsURL'}.'/'. + $curfext.".gif") { + $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/'. + $curfext.".gif"; + } + } + return &lonhttpdurl($iconname); } +sub lonhttpdurl { + my ($url)=@_; + my $lonhttpd_port=$Apache::lonnet::perlvar{'lonhttpdPort'}; + if (!defined($lonhttpd_port)) { $lonhttpd_port='8080'; } + return 'http://'.$ENV{'SERVER_NAME'}.':'.$lonhttpd_port.$url; +} + +sub connection_aborted { + my ($r)=@_; + $r->print(" ");$r->rflush(); + my $c = $r->connection; + return $c->aborted(); +} + +# Escapes strings that may have embedded 's that will be put into +# strings as 'strings'. +sub escape_single { + my ($input) = @_; + $input =~ s/\\/\\\\/g; # Escape the \'s..(must be first)> + $input =~ s/\'/\\\'/g; # Esacpe the 's.... + return $input; +} + +# Same as escape_single, but escape's "'s This +# can be used for "strings" +sub escape_double { + my ($input) = @_; + $input =~ s/\\/\\\\/g; # Escape the /'s..(must be first)> + $input =~ s/\"/\\\"/g; # Esacpe the "s.... + return $input; +} + +# Escapes the last element of a full URL. +sub escape_url { + my ($url) = @_; + my @urlslices = split(/\//, $url,-1); + my $lastitem = &Apache::lonnet::escape(pop(@urlslices)); + return join('/',@urlslices).'/'.$lastitem; +} =pod =back
'. &mt('Field').''.&mt('Samples').'
'); - if (defined($sone{$_})) { $r->print($sone{$_}."
\n"); } - if (defined($stwo{$_})) { $r->print($stwo{$_}."
\n"); } - if (defined($sthree{$_})) { $r->print($sthree{$_}."
\n"); } + foreach my $line (0..2) { + if (defined($samples->[$line]{$key})) { + $r->print($samples->[$line]{$key}."
\n"); + } + } $r->print('