--- loncom/interface/loncommon.pm 2003/10/23 21:01:54 1.133 +++ loncom/interface/loncommon.pm 2004/02/23 21:10:06 1.184 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common routines # -# $Id: loncommon.pm,v 1.133 2003/10/23 21:01:54 albertel Exp $ +# $Id: loncommon.pm,v 1.184 2004/02/23 21:10:06 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,13 +25,6 @@ # # http://www.lon-capa.org/ # -# YEAR=2001 -# 2/13-12/7 Guy Albertelli -# 12/21 Gerd Kortemeyer -# 12/25,12/28 Gerd Kortemeyer -# YEAR=2002 -# 1/4 Gerd Kortemeyer -# 6/24,7/2 H. K. Ng # Makes a table out of the previous attempts # Inputs result_from_symbread, user, domain, course_id @@ -69,14 +62,13 @@ use Apache::Constants qw(:common :http : use Apache::lonmsg(); use Apache::lonmenu(); use Apache::lonlocal; +use HTML::Entities; my $readit; -=pod - -=head1 Global Variables - -=cut +## +## Global Variables +## # ----------------------------------------------- Filetypes/Languages/Copyright my %language; @@ -90,50 +82,19 @@ my %category_extensions; my %designhash; # ---------------------------------------------- Thesaurus variables - -# FIXME: I don't think it's necessary to document these things; -# they're privately used - Jeremy - -=pod - -=over 4 - -=item * %Keywords - -A hash used by &keyword to determine if a word is considered a keyword. - -=item * $thesaurus_db_file - -Scalar containing the full path to the thesaurus database. - -=back - -=cut +# +# %Keywords: +# A hash used by &keyword to determine if a word is considered a keyword. +# $thesaurus_db_file +# Scalar containing the full path to the thesaurus database. my %Keywords; my $thesaurus_db_file; -# ----------------------------------------------------------------------- BEGIN - -# FIXME: I don't think this needs to be documented, it prepares -# private data structures - Jeremy -=pod - -=head1 General Subroutines - -=over 4 - -=item * BEGIN() - -Initialize values from language.tab, copyright.tab, filetypes.tab, -thesaurus.tab, and filecategories.tab. - -=back - -=cut - -# ----------------------------------------------------------------------- BEGIN - +# +# Initialize values from language.tab, copyright.tab, filetypes.tab, +# thesaurus.tab, and filecategories.tab. +# BEGIN { # Variable initialization $thesaurus_db_file = $Apache::lonnet::perlvar{'lonTabDir'}."/thesaurus.db"; @@ -141,32 +102,34 @@ BEGIN { unless ($readit) { # ------------------------------------------------------------------- languages { - my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}. - '/language.tab'); - if ($fh) { - while (<$fh>) { - next if /^\#/; - chomp; - my ($key,$two,$country,$three,$enc,$val,$sup)=(split(/\t/,$_)); - $language{$key}=$val.' - '.$enc; - if ($sup) { - $supported_language{$key}=$sup; - } - } - } + my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}. + '/language.tab'; + if ( open(my $fh,"<$langtabfile") ) { + while (<$fh>) { + next if /^\#/; + chomp; + my ($key,$two,$country,$three,$enc,$val,$sup)=(split(/\t/,$_)); + $language{$key}=$val.' - '.$enc; + if ($sup) { + $supported_language{$key}=$sup; + } + } + close($fh); + } } # ------------------------------------------------------------------ copyrights { - my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonIncludes'}. - '/copyright.tab'); - if ($fh) { - while (<$fh>) { - next if /^\#/; - chomp; - my ($key,$val)=(split(/\s+/,$_,2)); - $cprtag{$key}=$val; - } - } + my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}. + '/copyright.tab'; + if ( open (my $fh,"<$copyrightfile") ) { + while (<$fh>) { + next if /^\#/; + chomp; + my ($key,$val)=(split(/\s+/,$_,2)); + $cprtag{$key}=$val; + } + close($fh); + } } # -------------------------------------------------------------- domain designs @@ -177,15 +140,16 @@ BEGIN { while ($filename=readdir(DIR)) { my ($domain)=($filename=~/^(\w+)\./); { - my $fh=Apache::File->new($designdir.'/'.$filename); - if ($fh) { - while (<$fh>) { - next if /^\#/; - chomp; - my ($key,$val)=(split(/\=/,$_)); - if ($val) { $designhash{$domain.'.'.$key}=$val; } - } - } + 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); + } } } @@ -194,32 +158,35 @@ BEGIN { # ------------------------------------------------------------- file categories { - my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}. - '/filecategories.tab'); - if ($fh) { - while (<$fh>) { - next if /^\#/; - chomp; - my ($extension,$category)=(split(/\s+/,$_,2)); - push @{$category_extensions{lc($category)}},$extension; - } - } + my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}. + '/filecategories.tab'; + if ( open (my $fh,"<$categoryfile") ) { + while (<$fh>) { + next if /^\#/; + chomp; + my ($extension,$category)=(split(/\s+/,$_,2)); + push @{$category_extensions{lc($category)}},$extension; + } + close($fh); + } + } # ------------------------------------------------------------------ file types { - my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}. - '/filetypes.tab'); - if ($fh) { + my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}. + '/filetypes.tab'; + if ( open (my $fh,"<$typesfile") ) { while (<$fh>) { - next if (/^\#/); - chomp; - my ($ending,$emb,$descr)=split(/\s+/,$_,3); - if ($descr ne '') { - $fe{$ending}=lc($emb); - $fd{$ending}=$descr; - } - } - } + next if (/^\#/); + chomp; + my ($ending,$emb,$descr)=split(/\s+/,$_,3); + if ($descr ne '') { + $fe{$ending}=lc($emb); + $fd{$ending}=$descr; + } + } + close($fh); + } } &Apache::lonnet::logthis( "INFO: Read file types"); @@ -245,8 +212,6 @@ containing javascript with two functions C. Returned string does not contain EscriptE tags. -=over 4 - =item * openbrowser(formname,elementname,only,omit) [javascript] inputs: formname, elementname, only, omit @@ -267,15 +232,14 @@ Inputs: formname, elementname formname and elementname specify the name of the html form and the name of the element the selection from the search results will be placed in. -=back - =cut sub browser_and_searcher_javascript { + my $resurl=&lastresurl(); return < $resurl}); + &Apache::lonnet::appenv('environment.lastresurl' => $resurl); + return 1; +} + sub studentbrowser_javascript { unless ( (($ENV{'request.course.id'}) && @@ -514,6 +501,7 @@ function select1_changed() { // in with the nuclear for (i=0;i(Help: $topic) + (Help: $topic) ENDTEMPLATE if ($text ne '') { $template.='' }; return $template; @@ -630,6 +621,97 @@ sub helpLatexCheatsheet { .''; } +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); + + $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"; + } + + # Add the graphic + my $title = &mt('Report a Bug'); + $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'); + $template .= <<"ENDTEMPLATE"; + (FAQ: $topic) +ENDTEMPLATE + if ($text ne '') { $template.='
' }; + return $template; + +} + +############################################################### +############################################################### + =pod =item * csv_translate($text) @@ -639,6 +721,8 @@ format. =cut +############################################################### +############################################################### sub csv_translate { my $text = shift; $text =~ s/\"/\"\"/g; @@ -646,6 +730,60 @@ sub csv_translate { 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 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->{'date'} = $workbook->add_format(num_format=> + 'mmm d yyyy hh:mm AM/PM'); + return $format; +} + +############################################################### +############################################################### + =pod =item * change_content_javascript(): @@ -754,11 +892,40 @@ 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,%domains); + } else { + return &select_form($name,$value,%domains); + } +} + +sub multiple_select_form { + my ($name,$value,%hash)=@_; + my %selected = map { $_ => 1 } ref($value)?@{$value}:($value); + my $output=''; + my $size =(scalar keys %hash<4?scalar keys %hash:4); + $output.="\n\n"; + return $output; +} + #------------------------------------------- =pod @@ -790,6 +957,42 @@ sub select_form { 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; +} #------------------------------------------- @@ -906,6 +1109,8 @@ Outputs: =back +=back + =cut ############################################################### @@ -944,12 +1149,6 @@ sub decode_user_agent { $clientunicode,$clientos,); } -=pod - -=back - -=cut - ############################################################### ## Authentication changing form generation subroutines ## ############################################################### @@ -990,6 +1189,8 @@ See loncreateuser.pm for invocation and =back +=back + =cut #------------------------------------------- @@ -1017,10 +1218,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'; @@ -1080,10 +1301,10 @@ END sub authform_authorwarning{ my $result=''; - $result=<<"END"; -As a general rule, only authors or co-authors should be filesystem -authenticated (which allows access to the server filesystem). -END + $result=''. + &mt('As a general rule, only authors or co-authors should be '. + 'filesystem authenticated '. + '(which allows access to the server filesystem).')."\n"; return $result; } @@ -1093,12 +1314,10 @@ sub authform_nochange{ kerb_def_dom => 'MSU.EDU', @_, ); - my $result=''; - $result.=<<"END"; - -Do not change login data -END + my $result = &mt('[_1] Do not change login data', + ''); return $result; } @@ -1109,24 +1328,35 @@ sub authform_kerberos{ kerb_def_auth => 'krb4', @_, ); - my $result=''; - my $check4; - my $check5; + my ($check4,$check5,$krbarg); if ($in{'kerb_def_auth'} eq 'krb5') { $check5 = " checked=\"on\""; } else { $check4 = " checked=\"on\""; } - $result.=<<"END"; - -Kerberos authenticated with domain - -Version 4 -Version 5 -END + $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; } @@ -1136,15 +1366,25 @@ sub authform_internal{ kerb_def_dom => 'MSU.EDU', @_, ); - my $result=''; - $result.=<<"END"; - -Internally authenticated (with initial password -) -END + + 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; } @@ -1154,15 +1394,24 @@ sub authform_local{ kerb_def_dom => 'MSU.EDU', @_, ); - my $result=''; - $result.=<<"END"; - -Local Authentication with argument - -END + + 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 argument [_2]', + '', + ''); return $result; } @@ -1172,24 +1421,16 @@ sub authform_filesystem{ kerb_def_dom => 'MSU.EDU', @_, ); - my $result=''; - $result.=<<"END"; - -Filesystem authenticated (with initial password -) -END + my $jscall = "javascript:changed_radio('fsys',$in{'formname'});"; + my $result.= &mt + ('[_1] Filesystem Authenticated (with initial password [_2])', + '', + ''); return $result; } -=pod - -=back - -=cut - ############################################################### ## Get Authentication Defaults for Domain ## ############################################################### @@ -1344,7 +1585,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. @@ -1480,8 +1721,9 @@ sub noteswrapper { # ------------------------------------------------------------- Aboutme Wrapper sub aboutmewrapper { - my ($link,$username,$domain)=@_; - return "$link"; + my ($link,$username,$domain,$target)=@_; + return "$link"; } # ------------------------------------------------------------ Syllabus Wrapper @@ -1528,6 +1770,16 @@ sub languagedescription { ($supported_language{$code}?' ('.&mt('interface available').')':''); } +sub plainlanguagedescription { + my $code=shift; + return $language{$code}; +} + +sub supportedlanguagecode { + my $code=shift; + return $supported_language{$code}; +} + =pod =item * copyrightids() @@ -1549,7 +1801,7 @@ returns description of a specified copyr =cut sub copyrightdescription { - return $cprtag{shift(@_)}; + return &mt($cprtag{shift(@_)}); } =pod @@ -1589,6 +1841,14 @@ sub fileembstyle { return $fe{lc(shift(@_))}; } + +sub filecategoryselect { + my ($name,$value)=@_; + return &select_form($name,$value, + '' => &mt('Any category'), + map { $_,$_ } sort(keys(%category_extensions))); +} + =pod =item * filedescription() @@ -1598,7 +1858,7 @@ returns description for a specified file =cut sub filedescription { - return $fd{lc(shift(@_))}; + return &mt($fd{lc(shift(@_))}); } =pod @@ -1612,7 +1872,7 @@ extra formatting sub filedescriptionex { my $ex=shift; - return '.'.$ex.' '.$fd{lc($ex)}; + return '.'.$ex.' '.&mt($fd{lc($ex)}); } # End of .tab access @@ -1647,13 +1907,13 @@ 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'}) { @languages=(@languages,split(/\s*(\,|\;|\:)\s*/, $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)); @@ -1764,7 +2024,7 @@ sub get_previous_attempt { } else { $value=$returnhash{$version.':'.$_}; } - $prevattempts.=''.$value.' '; + $prevattempts.=''.&Apache::lonnet::unescape($value).' '; } } } @@ -1776,6 +2036,7 @@ sub get_previous_attempt { } else { $value=$lasthash{$_}; } + $value=&Apache::lonnet::unescape($value); if ($_ =~/$regexp$/ && (defined &$gradesub)) {$value = &$gradesub($value)} $prevattempts.=''.$value.' '; } @@ -1939,21 +2200,6 @@ sub maketime { $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; -} - ######################################### sub findallcourses { @@ -2025,7 +2271,7 @@ sub domainlogo { my $lonhttpdPort=$Apache::lonnet::perlvar{'lonhttpdPort'}; if (!defined($lonhttpdPort)) { $lonhttpdPort='8080'; } return ''; + '/adm/lonDomLogos/'.$domain.'.gif" alt="'.$domain.'" />'; } elsif(exists($Apache::lonnet::domaindescription{$domain})) { return $Apache::lonnet::domaindescription{$domain}; } else { @@ -2113,19 +2359,7 @@ other decorations will be returned. sub bodytag { my ($title,$function,$addentries,$bodyonly,$domain,$forcereg)=@_; $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 $tabbg=&designparm($function.'.tabbg',$domain); @@ -2136,8 +2370,9 @@ sub bodytag { my $sidebg=&designparm($function.'.sidebg',$domain); # Accessibility font enhance unless ($addentries) { $addentries=''; } + my $addstyle=''; if ($ENV{'browser.fontenhance'} eq 'on') { - $addentries.=' style="font-size: x-large"'; + $addstyle=' font-size: x-large;'; } # role and realm my ($role,$realm) @@ -2155,11 +2390,15 @@ sub bodytag { if (!defined($lonhttpdPort)) { $lonhttpdPort='8080'; } # construct main body tag my $bodytag = < +h1, h2, h3, th { font-family: Arial, Helvetica, sans-serif } +a:focus { color: red; background: yellow } + +style="margin-top: 0px;$addstyle" $addentries> END my $upperleft=''; + $lonhttpdPort.$img.'" alt="'.$function.'" />'; if ($bodyonly) { return $bodytag; } elsif ($ENV{'browser.interface'} eq 'textual') { @@ -2171,7 +2410,7 @@ END # No Remote return $bodytag.&Apache::lonmenu::menubuttons($forcereg,'web', $forcereg). - ' - +
'.$title. + '
'.$title. '
'; } @@ -2187,9 +2426,9 @@ $upperleft
$title - -$title + + $ENV{'environment.firstname'} $ENV{'environment.middlename'} $ENV{'environment.lastname'} @@ -2198,16 +2437,43 @@ $upperleft
-$role  +$role 
$realm 
$realm 

ENDBODY } ############################################### +=pod + +=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; +} + +############################################### + sub get_posted_cgi { my $r=shift; @@ -2342,11 +2608,16 @@ sub 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) = @_; + 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 @@ -2378,6 +2649,32 @@ sub add_to_env { =pod +=item * get_env_multiple($name) + +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 + +=cut + +sub get_env_multiple { + my ($name) = @_; + my @values; + if (defined($ENV{$name})) { + # exists is it an array + if (ref($ENV{$name})) { + @values=@{ $ENV{$name} }; + } else { + $values[0]=$ENV{$name}; + } + } + return(@values); +} + + +=pod + =back =head1 CSV Upload/Handling functions @@ -2402,9 +2699,12 @@ sub upfile_store { my $datatoken=$ENV{'user.name'}.'_'.$ENV{'user.domain'}. '_enroll_'.$ENV{'request.course.id'}.'_'.time.'_'.$$; { - my $fh=Apache::File->new('>'.$r->dir_config('lonDaemons'). - '/tmp/'.$datatoken.'.tmp'); - print $fh $ENV{'form.upfile'}; + my $datafile = $r->dir_config('lonDaemons'). + '/tmp/'.$datatoken.'.tmp'; + if ( open(my $fh,">$datafile") ) { + print $fh $ENV{'form.upfile'}; + close($fh); + } } return $datatoken; } @@ -2423,11 +2723,12 @@ sub load_tmp_file { my $r=shift; my @studentdata=(); { - my $fh; - if ($fh=Apache::File->new($r->dir_config('lonDaemons'). - '/tmp/'.$ENV{'form.datatoken'}.'.tmp')) { - @studentdata=<$fh>; - } + my $studentfile = $r->dir_config('lonDaemons'). + '/tmp/'.$ENV{'form.datatoken'}.'.tmp'; + if ( open(my $fh,"<$studentfile") ) { + @studentdata=<$fh>; + close($fh); + } } $ENV{'form.upfile'}=join('',@studentdata); } @@ -2472,7 +2773,7 @@ sub record_sep { } } elsif ($ENV{'form.upfiletype'} eq 'tab') { my $i=0; - foreach (split(/\t+/,$record)) { + foreach (split(/\t/,$record)) { my $field=$_; $field=~s/^(\"|\')//; $field=~s/(\"|\')$//; @@ -2501,26 +2802,39 @@ sub record_sep { return %components; } +###################################################### +###################################################### + =pod =item * upfile_select_html() -return HTML code to select file and specify its type +Return HTML code to select a file from the users machine and specify +the file type. =cut +###################################################### +###################################################### sub upfile_select_html { - return (<<'ENDUPFORM'); - -
Type: -ENDUPFORM + my %Types = ( + csv => &mt('CSV (comma separated values, spreadsheet)'), + space => &mt('Space separated'), + tab => &mt('Tabulator separated'), +# xml => &mt('HTML/XML'), + ); + my $Str = ''. + '
Type: \n"; + return $Str; } +###################################################### +###################################################### + =pod =item * csv_print_samples($r,$records) @@ -2531,15 +2845,18 @@ Apache Request ref, $records is an array =cut +###################################################### +###################################################### 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]);} - - $r->print('Samples
'); - foreach (sort({$a <=> $b} keys(%sone))) { $r->print(''); } + # + $r->print(&mt('Samples').'
Column '.($_+1).'
'); + foreach (sort({$a <=> $b} keys(%sone))) { + $r->print(''); } $r->print(''); foreach my $hash (\%sone,\%stwo,\%sthree) { $r->print(''); @@ -2553,32 +2870,42 @@ sub csv_print_samples { $r->print('
'.&mt('Column [_1]',($_+1)).'

'."\n"); } +###################################################### +###################################################### + =pod =item * csv_print_select_table($r,$records,$d) Prints a table to create associations between values and table columns. + $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 +###################################################### +###################################################### sub csv_print_select_table { my ($r,$records,$d) = @_; my $i=0;my %sone; %sone=&record_sep($$records[0]); - $r->print('Associate columns with student attributes.'."\n". - ''."\n"); + $r->print(&mt('Associate columns with student attributes.')."\n". + '
AttributeColumn
'. + ''. + ''."\n"); foreach (@$d) { - my ($value,$display)=@{ $_ }; + my ($value,$display,$defaultcol)=@{ $_ }; $r->print(''); $r->print(''."\n"); $i++; @@ -2587,6 +2914,9 @@ sub csv_print_select_table { return $i; } +###################################################### +###################################################### + =pod =item * csv_samples_select_table($r,$records,$d) @@ -2599,22 +2929,27 @@ $d is an array of 2 element arrays (inte =cut +###################################################### +###################################################### sub csv_samples_select_table { my ($r,$records,$d) = @_; my %sone; my %stwo; my %sthree; my $i=0; - - $r->print('
'.&mt('Attribute').''.&mt('Column').'
'.$display.'
'); + # + $r->print('
FieldSamples
'); %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) { - $r->print('
'. + &mt('Field').''.&mt('Samples').'
'); if (defined($sone{$_})) { $r->print($sone{$_}."
\n"); } @@ -2627,6 +2962,9 @@ sub csv_samples_select_table { return($i); } +###################################################### +###################################################### + =pod =item clean_excel_name($name) @@ -2635,6 +2973,8 @@ Returns a replacement for $name which do =cut +###################################################### +###################################################### sub clean_excel_name { my ($name) = @_; $name =~ s/[:\*\?\/\\]//g; @@ -2665,30 +3005,103 @@ sub check_if_partid_hidden { my ($id,$symb,$udom,$uname) = @_; my $hiddenparts=&Apache::lonnet::EXT('resource.0.hiddenparts', $symb,$udom,$uname); + my $truth=1; + #if the string starts with !, then the list is the list to show not hide + if ($hiddenparts=~s/^\s*!//) { $truth=undef; } my @hiddenlist=split(/,/,$hiddenparts); foreach my $checkid (@hiddenlist) { - if ($checkid =~ /^\s*\Q$id\E\s*$/) { return 1; } + if ($checkid =~ /^\s*\Q$id\E\s*$/) { return $truth; } } - return undef; + return !$truth; } + ############################################################ ############################################################ =pod -=item DrawGraph +=back + +=head1 cgi-bin script and graphing routines -Returns a link to cgi-bin/graph +=over 4 + +=item get_cgi_id + +Inputs: none + +Returns an id which can be used to pass environment variables +to various cgi-bin scripts. These environment variables will +be removed from the users environment after a given time by +the routine &Apache::lonnet::transfer_profile_to_env. =cut ############################################################ ############################################################ -sub DrawGraph { - my ($Title,$xlabel,$ylabel,$Max,$colors,@Values)=@_; +my $uniq=0; +sub get_cgi_id { + $uniq=($uniq+1)%100000; + return (time.'_'.$uniq); +} + +############################################################ +############################################################ + +=pod + +=item DrawBarGraph + +Facilitates the plotting of data in a (stacked) bar graph. +Puts plot definition data into the users environment in order for +graph.png to plot it. Returns an tag for the plot. +The bars on the plot are labeled '1','2',...,'n'. + +Inputs: + +=over 4 + +=item $Title: string, the title of the plot + +=item $xlabel: string, text describing the X-axis of the plot + +=item $ylabel: string, text describing the Y-axis of the plot + +=item $Max: scalar, the maximum Y value to use in the plot +If $Max is < any data point, the graph will not be rendered. + +=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. + +=back + +Returns: + +An tag which references graph.png and the appropriate identifying +information for the plot. + +=cut + +############################################################ +############################################################ +sub DrawBarGraph { + my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_; + # + if (! defined($colors)) { + $colors = ['#33ff00', + '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933', + '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66', + ]; + } # - my $identifier = time.'_'.int(rand(1000)); + my $identifier = &get_cgi_id(); + my $id = 'cgi.'.$identifier; if (! @Values || ref($Values[0]) ne 'ARRAY') { return ''; } @@ -2697,31 +3110,61 @@ sub DrawGraph { my $NumSets=1; foreach my $array (@Values) { next if (! ref($array)); - $ValuesHash{'cgi.'.$identifier.'.data.'.$NumSets++} = + $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array); } # - $Title = '' if (! defined($Title)); - $xlabel = '' if (! defined($xlabel)); - $ylabel = '' if (! defined($ylabel)); - $Title = &Apache::lonnet::escape($Title); - $xlabel = &Apache::lonnet::escape($xlabel); - $ylabel = &Apache::lonnet::escape($ylabel); + my ($height,$width,$xskip,$bar_width) = (200,120,1,15); + if ($NumBars < 10) { + $width = 120+$NumBars*15; + $xskip = 1; + $bar_width = 15; + } elsif ($NumBars <= 25) { + $width = 120+$NumBars*11; + $xskip = 5; + $bar_width = 8; + } elsif ($NumBars <= 50) { + $width = 120+$NumBars*8; + $xskip = 5; + $bar_width = 4; + } else { + $width = 120+$NumBars*8; + $xskip = 5; + $bar_width = 4; + } + # + my @Labels; + if (defined($labels)) { + @Labels = @$labels; + } else { + for (my $i=0;$i<@{$Values[0]};$i++) { + push (@Labels,$i+1); + } + } # $Max = 1 if ($Max < 1); if ( int($Max) < $Max ) { $Max++; $Max = int($Max); } + $Title = '' if (! defined($Title)); + $xlabel = '' if (! defined($xlabel)); + $ylabel = '' if (! defined($ylabel)); + $ValuesHash{$id.'.title'} = &Apache::lonnet::escape($Title); + $ValuesHash{$id.'.xlabel'} = &Apache::lonnet::escape($xlabel); + $ValuesHash{$id.'.ylabel'} = &Apache::lonnet::escape($ylabel); + $ValuesHash{$id.'.y_max_value'} = $Max; + $ValuesHash{$id.'.NumBars'} = $NumBars; + $ValuesHash{$id.'.NumSets'} = $NumSets; + $ValuesHash{$id.'.PlotType'} = 'bar'; + $ValuesHash{$id.'.Colors'} = join(',',@{$colors}); + $ValuesHash{$id.'.height'} = $height; + $ValuesHash{$id.'.width'} = $width; + $ValuesHash{$id.'.xskip'} = $xskip; + $ValuesHash{$id.'.bar_width'} = $bar_width; + $ValuesHash{$id.'.labels'} = join(',',@Labels); # - &Apache::lonnet::appenv('cgi.'.$identifier.'.title' => $Title, - 'cgi.'.$identifier.'.xlabel' => $xlabel, - 'cgi.'.$identifier.'.ylabel' => $ylabel, - 'cgi.'.$identifier.'.Max' => $Max, - 'cgi.'.$identifier.'.NumBars' => $NumBars, - 'cgi.'.$identifier.'.NumSets' => $NumSets, - 'cgi.'.$identifier.'.Colors' => join(',',@{$colors}), - %ValuesHash); + &Apache::lonnet::appenv(%ValuesHash); return ''; } @@ -2730,6 +3173,371 @@ sub DrawGraph { =pod +=item DrawXYGraph + +Facilitates the plotting of data in an XY graph. +Puts plot definition data into the users environment in order for +graph.png to plot it. Returns an tag for the plot. + +Inputs: + +=over 4 + +=item $Title: string, the title of the plot + +=item $xlabel: string, text describing the X-axis of the plot + +=item $ylabel: string, text describing the Y-axis of the plot + +=item $Max: scalar, the maximum Y value to use in the plot +If $Max is < any data point, the graph will not be rendered. + +=item $colors: Array ref containing the hex color codes for the data to be +plotted in. If undefined, default values will be used. + +=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. + +=item %Values: hash indicating or overriding any default values which are +passed to graph.png. +Possible values are: width, xskip, x_ticks, x_tick_offset, among others. + +=back + +Returns: + +An tag which references graph.png and the appropriate identifying +information for the plot. + +=cut + +############################################################ +############################################################ +sub DrawXYGraph { + my ($Title,$xlabel,$ylabel,$Max,$colors,$Xlabels,$Ydata,%Values)=@_; + # + # Create the identifier for the graph + my $identifier = &get_cgi_id(); + my $id = 'cgi.'.$identifier; + # + $Title = '' if (! defined($Title)); + $xlabel = '' if (! defined($xlabel)); + $ylabel = '' if (! defined($ylabel)); + my %ValuesHash = + ( + $id.'.title' => &Apache::lonnet::escape($Title), + $id.'.xlabel' => &Apache::lonnet::escape($xlabel), + $id.'.ylabel' => &Apache::lonnet::escape($ylabel), + $id.'.y_max_value'=> $Max, + $id.'.labels' => join(',',@$Xlabels), + $id.'.PlotType' => 'XY', + ); + # + if (defined($colors) && ref($colors) eq 'ARRAY') { + $ValuesHash{$id.'.Colors'} = join(',',@{$colors}); + } + # + if (! ref($Ydata) || ref($Ydata) ne 'ARRAY') { + return ''; + } + my $NumSets=1; + foreach my $array (@{$Ydata}){ + next if (! ref($array)); + $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array); + } + $ValuesHash{$id.'.NumSets'} = $NumSets-1; + # + # Deal with other parameters + while (my ($key,$value) = each(%Values)) { + $ValuesHash{$id.'.'.$key} = $value; + } + # + &Apache::lonnet::appenv(%ValuesHash); + return ''; +} + +############################################################ +############################################################ + +=pod + +=item DrawXYYGraph + +Facilitates the plotting of data in an XY graph with two Y axes. +Puts plot definition data into the users environment in order for +graph.png to plot it. Returns an tag for the plot. + +Inputs: + +=over 4 + +=item $Title: string, the title of the plot + +=item $xlabel: string, text describing the X-axis of the plot + +=item $ylabel: string, text describing the Y-axis of the plot + +=item $colors: Array ref containing the hex color codes for the data to be +plotted in. If undefined, default values will be used. + +=item $Xlabels: Array ref containing the labels to be used for the X-axis. + +=item $Ydata1: The first data set + +=item $Min1: The minimum value of the left Y-axis + +=item $Max1: The maximum value of the left Y-axis + +=item $Ydata2: The second data set + +=item $Min2: The minimum value of the right Y-axis + +=item $Max2: The maximum value of the left Y-axis + +=item %Values: hash indicating or overriding any default values which are +passed to graph.png. +Possible values are: width, xskip, x_ticks, x_tick_offset, among others. + +=back + +Returns: + +An tag which references graph.png and the appropriate identifying +information for the plot. + +=cut + +############################################################ +############################################################ +sub DrawXYYGraph { + my ($Title,$xlabel,$ylabel,$colors,$Xlabels,$Ydata1,$Min1,$Max1, + $Ydata2,$Min2,$Max2,%Values)=@_; + # + # Create the identifier for the graph + my $identifier = &get_cgi_id(); + my $id = 'cgi.'.$identifier; + # + $Title = '' if (! defined($Title)); + $xlabel = '' if (! defined($xlabel)); + $ylabel = '' if (! defined($ylabel)); + my %ValuesHash = + ( + $id.'.title' => &Apache::lonnet::escape($Title), + $id.'.xlabel' => &Apache::lonnet::escape($xlabel), + $id.'.ylabel' => &Apache::lonnet::escape($ylabel), + $id.'.labels' => join(',',@$Xlabels), + $id.'.PlotType' => 'XY', + $id.'.NumSets' => 2, + $id.'.two_axes' => 1, + $id.'.y1_max_value' => $Max1, + $id.'.y1_min_value' => $Min1, + $id.'.y2_max_value' => $Max2, + $id.'.y2_min_value' => $Min2, + ); + # + if (defined($colors) && ref($colors) eq 'ARRAY') { + $ValuesHash{$id.'.Colors'} = join(',',@{$colors}); + } + # + if (! ref($Ydata1) || ref($Ydata1) ne 'ARRAY' || + ! ref($Ydata2) || ref($Ydata2) ne 'ARRAY'){ + return ''; + } + my $NumSets=1; + foreach my $array ($Ydata1,$Ydata2){ + next if (! ref($array)); + $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array); + } + # + # Deal with other parameters + while (my ($key,$value) = each(%Values)) { + $ValuesHash{$id.'.'.$key} = $value; + } + # + &Apache::lonnet::appenv(%ValuesHash); + return ''; +} + +############################################################ +############################################################ + +=pod + +=back + +=head1 Statistics helper routines? + +Bad place for them but what the hell. + +=over 4 + +=item &chartlink + +Returns a link to the chart for a specific student. + +Inputs: + +=over 4 + +=item $linktext: The text of the link + +=item $sname: The students username + +=item $sdomain: The students domain + +=back + +=back + +=cut + +############################################################ +############################################################ +sub chartlink { + my ($linktext, $sname, $sdomain) = @_; + my $link = ''.$linktext.''; +} + +####################################################### +####################################################### + +=pod + +=head1 Course Environment Routines + +=over 4 + +=item &restore_course_settings + +=item &store_course_settings + +Restores/Store indicated form parameters from the course environment. +Will not overwrite existing values of the form parameters. + +Inputs: +a scalar describing the data (e.g. 'chart', 'problem_analysis') + +a hash ref describing the data to be stored. For example: + +%Save_Parameters = ('Status' => 'scalar', + 'chartoutputmode' => 'scalar', + 'chartoutputdata' => 'scalar', + 'Section' => 'array', + 'StudentData' => 'array', + 'Maps' => 'array'); + +Returns: both routines return nothing + +=cut + +####################################################### +####################################################### +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 ($prefix,$Settings) = @_; + my %SaveHash; + my %AppHash; + while (my ($setting,$type) = each(%$Settings)) { + my $basename = 'internal.'.$prefix.'.'.$setting; + my $envname = 'course.'.$courseid.'.'.$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}; + } elsif ($type eq 'array') { + my $stored_form; + if (ref($ENV{'form.'.$setting})) { + $stored_form = join(',', + map { + &Apache::lonnet::escape($_); + } sort(@{$ENV{'form.'.$setting}})); + } else { + $stored_form = + &Apache::lonnet::escape($ENV{'form.'.$setting}); + } + # Determine if the array contents are the same. + if ($stored_form ne $ENV{$envname}) { + $SaveHash{$basename} = $stored_form; + $AppHash{$envname} = $stored_form; + } + } + } + } + my $put_result = &Apache::lonnet::put('environment',\%SaveHash, + $coursedom, + $ENV{'course.'.$courseid.'.num'}); + if ($put_result !~ /^(ok|delayed)/) { + &Apache::lonnet::logthis('unable to save form parameters, '. + 'got error:'.$put_result); + } + # Make sure these settings stick around in this session, too + &Apache::lonnet::appenv(%AppHash); + return; +} + +sub restore_course_settings { + 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.'.internal.'.$prefix. + '.'.$setting; + if (exists($ENV{$envname})) { + if ($type eq 'scalar') { + $ENV{'form.'.$setting} = $ENV{$envname}; + } elsif ($type eq 'array') { + $ENV{'form.'.$setting} = [ + map { + &Apache::lonnet::unescape($_); + } split(',',$ENV{$envname}) + ]; + } + } + } +} + +############################################################ +############################################################ + +sub propath { + my ($udom,$uname)=@_; + $udom=~s/\W//g; + $uname=~s/\W//g; + my $subdir=$uname.'__'; + $subdir =~ s/(.)(.)(.).*/$1\/$2\/$3/; + my $proname="$Apache::lonnet::perlvar{'lonUsersDir'}/$udom/$subdir/$uname"; + return $proname; +} + +sub icon { + my ($file)=@_; + my $curfext = (split(/\./,$file))[-1]; + my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif'; + my $embstyle = &Apache::loncommon::fileembstyle($curfext); + 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 $iconname; +} + +=pod + =back =cut