--- loncom/interface/loncommon.pm 2003/08/20 18:18:45 1.112 +++ loncom/interface/loncommon.pm 2003/11/04 19:01:01 1.142 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common routines # -# $Id: loncommon.pm,v 1.112 2003/08/20 18:18:45 bowersj2 Exp $ +# $Id: loncommon.pm,v 1.142 2003/11/04 19:01:01 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -68,6 +68,9 @@ use POSIX qw(strftime mktime); use Apache::Constants qw(:common :http :methods); use Apache::lonmsg(); use Apache::lonmenu(); +use Apache::lonlocal; +use HTML::Entities; + my $readit; =pod @@ -78,6 +81,7 @@ my $readit; # ----------------------------------------------- Filetypes/Languages/Copyright my %language; +my %supported_language; my %cprtag; my %fe; my %fd; my %category_extensions; @@ -144,8 +148,11 @@ BEGIN { while (<$fh>) { next if /^\#/; chomp; - my ($key,$two,$country,$three,$enc,$val)=(split(/\t/,$_)); + my ($key,$two,$country,$three,$enc,$val,$sup)=(split(/\t/,$_)); $language{$key}=$val.' - '.$enc; + if ($sup) { + $supported_language{$key}=$sup; + } } } } @@ -268,7 +275,7 @@ of the element the selection from the se sub browser_and_searcher_javascript { return <<END; var editbrowser = null; - function openbrowser(formname,elementname,only,omit) { + function openbrowser(formname,elementname,only,omit,titleelement) { var url = '/res/?'; if (editbrowser == null) { url += 'launch=1&'; @@ -282,6 +289,9 @@ sub browser_and_searcher_javascript { if (omit != null) { url += 'omit=' + omit + '&'; } + if (titleelement != null) { + url += 'titleelement=' + titleelement + '&'; + } url += 'element=' + elementname + ''; var title = 'Browser'; var options = 'scrollbars=1,resizable=1,menubar=0'; @@ -290,7 +300,7 @@ sub browser_and_searcher_javascript { editbrowser.focus(); } var editsearcher; - function opensearcher(formname,elementname) { + function opensearcher(formname,elementname,titleelement) { var url = '/adm/searchcat?'; if (editsearcher == null) { url += 'launch=1&'; @@ -298,6 +308,9 @@ sub browser_and_searcher_javascript { url += 'catalogmode=interactive&'; url += 'mode=edit&'; url += 'form=' + formname + '&'; + if (titleelement != null) { + url += 'titleelement=' + titleelement + '&'; + } url += 'element=' + elementname + ''; var title = 'Search'; var options = 'scrollbars=1,resizable=1,menubar=0'; @@ -346,17 +359,18 @@ sub selectstudent_link { return ''; } return "<a href='".'javascript:openstdbrowser("'.$form.'","'.$unameele. - '","'.$udomele.'");'."'>Select User</a>"; + '","'.$udomele.'");'."'>".&mt('Select User')."</a>"; } if ($ENV{'request.role'}=~/^(au|dc|su)/) { return "<a href='".'javascript:openstdbrowser("'.$form.'","'.$unameele. - '","'.$udomele.'",1);'."'>Select User</a>"; + '","'.$udomele.'",1);'."'>".&mt('Select User')."</a>"; } return ''; } sub coursebrowser_javascript { - return (<<'ENDSTDBRW'); + my ($domainfilter)=@_; + return (<<ENDSTDBRW); <script type="text/javascript" language="Javascript" > var stdeditbrowser; function opencrsbrowser(formname,uname,udom) { @@ -367,6 +381,12 @@ sub coursebrowser_javascript { url += 'filter='+filter+'&'; } } + var domainfilter='$domainfilter'; + if (domainfilter != null) { + if (domainfilter != '') { + url += 'domainfilter='+domainfilter+'&'; + } + } url += 'form=' + formname + '&cnumelement='+uname+ '&cdomelement='+udom; var title = 'Course_Browser'; @@ -382,7 +402,7 @@ ENDSTDBRW sub selectcourse_link { my ($form,$unameele,$udomele)=@_; return "<a href='".'javascript:opencrsbrowser("'.$form.'","'.$unameele. - '","'.$udomele.'");'."'>Select Course</a>"; + '","'.$udomele.'");'."'>".&mt('Select Course')."</a>"; } =pod @@ -514,7 +534,7 @@ END foreach my $value (sort(keys(%$hashref))) { $result.=" <option value=\"$value\" "; $result.=" selected=\"true\" " if ($value eq $firstdefault); - $result.=">$hashref->{$value}->{'text'}</option>\n"; + $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n"; } $result .= "</select>\n"; my %select2 = %{$hashref->{$firstdefault}->{'select2'}}; @@ -524,7 +544,7 @@ END foreach my $value (sort(keys(%select2))) { $result.=" <option value=\"$value\" "; $result.=" selected=\"true\" " if ($value eq $seconddefault); - $result.=">$select2{$value}</option>\n"; + $result.=">".&mt($select2{$value})."</option>\n"; } $result .= "</select>\n"; # return $debug; @@ -624,8 +644,6 @@ sub helpLatexCheatsheet { Translate $text to allow it to be output as a 'comma seperated values' format. -=back - =cut sub csv_translate { @@ -635,6 +653,91 @@ sub csv_translate { return $text; } +=pod + +=item * change_content_javascript(): + +This and the next function allow you to create small sections of an +otherwise static HTML page that you can update on the fly with +Javascript, even in Netscape 4. + +The Javascript fragment returned by this function (no E<lt>scriptE<gt> tag) +must be written to the HTML page once. It will prove the Javascript +function "change(name, content)". Calling the change function with the +name of the section +you want to update, matching the name passed to C<changable_area>, and +the new content you want to put in there, will put the content into +that area. + +B<Note>: Netscape 4 only reserves enough space for the changable area +to contain room for the original contents. You need to "make space" +for whatever changes you wish to make, and be B<sure> to check your +code in Netscape 4. This feature in Netscape 4 is B<not> powerful; +it's adequate for updating a one-line status display, but little more. +This script will set the space to 100% width, so you only need to +worry about height in Netscape 4. + +Modern browsers are much less limiting, and if you can commit to the +user not using Netscape 4, this feature may be used freely with +pretty much any HTML. + +=cut + +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\./) { + return (<<NETSCAPE4); + function change(name, content) { + doc = document.layers[name+"___escape"].layers[0].document; + doc.open(); + doc.write(content); + doc.close(); + } +NETSCAPE4 + } else { + # Otherwise, we need to use semi-standards-compliant code + # (technically, "innerHTML" isn't standard but the equivalent + # is really scary, and every useful browser supports it + return (<<DOMBASED); + function change(name, content) { + element = document.getElementById(name); + element.innerHTML = content; + } +DOMBASED + } +} + +=pod + +=item * changable_area($name, $origContent): + +This provides a "changable area" that can be modified on the fly via +the Javascript code provided in C<change_content_javascript>. $name is +the name you will use to reference the area later; do not repeat the +same name on a given HTML page more then once. $origContent is what +the area will originally contain, which can be left blank. + +=cut + +sub changable_area { + my ($name, $origContent) = @_; + + if ($ENV{'browser.type'} eq 'netscape' && + $ENV{'browser.version'} =~ /^4\./) { + # If this is netscape 4, we need to use the Layer tag + return "<ilayer width='100%' id='${name}___escape' overflow='none'><layer width='100%' id='$name' overflow='none'>$origContent</layer></ilayer>"; + } else { + return "<span id='$name'>$origContent</span>"; + } +} + +=pod + +=back + +=cut + ############################################################### ## Home server <option> list generating code ## ############################################################### @@ -679,10 +782,16 @@ See lonrights.pm for an example invocati sub select_form { my ($def,$name,%hash) = @_; my $selectform = "<select name=\"$name\" size=\"1\">\n"; - foreach (sort keys %hash) { + my @keys; + if (exists($hash{'select_form_order'})) { + @keys=@{$hash{'select_form_order'}}; + } else { + @keys=sort(keys(%hash)); + } + foreach (@keys) { $selectform.="<option value=\"$_\" ". ($_ eq $def ? 'selected' : ''). - ">".$hash{$_}."</option>\n"; + ">".&mt($hash{$_})."</option>\n"; } $selectform.="</select>"; return $selectform; @@ -1420,7 +1529,10 @@ returns description of a specified langu =cut sub languagedescription { - return $language{shift(@_)}; + my $code=shift; + return ($supported_language{$code}?'* ':''). + $language{$code}. + ($supported_language{$code}?' ('.&mt('interface available').')':''); } =pod @@ -1528,16 +1640,8 @@ sub fileextensions { sub display_languages { my %languages=(); - if ($ENV{'environment.languages'}) { - foreach (split(/\s*(\,|\;|\:)\s*/,$ENV{'environment.languages'})) { - $languages{$_}=1; - } - } - if ($ENV{'course.'.$ENV{'request.course.id'}.'.languages'}) { - foreach (split(/\s*(\,|\;|\:)\s*/, - $ENV{'course.'.$ENV{'request.course.id'}.'.languages'})) { - $languages{$_}=1; - } + foreach (&preferred_languages()) { + $languages{$_}=1; } &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']); if ($ENV{'form.displaylanguage'}) { @@ -1548,6 +1652,45 @@ sub display_languages { return %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'})); + } + 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'}}) { + @languages=(@languages, + $Apache::lonnet::domain_lang_def{$ENV{'user.domain'}}); + } + if ($Apache::lonnet::domain_lang_def{$ENV{'request.role.domain'}}) { + @languages=(@languages, + $Apache::lonnet::domain_lang_def{$ENV{'request.role.domain'}}); + } + if ($Apache::lonnet::domain_lang_def{ + $Apache::lonnet::perlvar{'lonDefDomain'}}) { + @languages=(@languages, + $Apache::lonnet::domain_lang_def{ + $Apache::lonnet::perlvar{'lonDefDomain'}}); + } +# turn "en-ca" into "en-ca,en" + my @genlanguages; + foreach (@languages) { + unless ($_=~/\w/) { next; } + push (@genlanguages,$_); + if ($_=~/(\-|\_)/) { + push (@genlanguages,(split(/(\-|\_)/,$_))[0]); + } + } + return @genlanguages; +} + ############################################################### ## Student Answer Attempts ## ############################################################### @@ -1628,7 +1771,7 @@ sub get_previous_attempt { } else { $value=$returnhash{$version.':'.$_}; } - $prevattempts.='<td>'.$value.' </td>'; + $prevattempts.='<td>'.&Apache::lonnet::unescape($value).' </td>'; } } } @@ -1640,6 +1783,7 @@ sub get_previous_attempt { } else { $value=$lasthash{$_}; } + $value=&Apache::lonnet::unescape($value); if ($_ =~/$regexp$/ && (defined &$gradesub)) {$value = &$gradesub($value)} $prevattempts.='<td>'.$value.' </td>'; } @@ -1697,7 +1841,7 @@ show a snapshot of what student was look sub get_student_view { my ($symb,$username,$domain,$courseid,$target) = @_; - my ($map,$id,$feedurl) = split(/___/,$symb); + my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb); my (%old,%moreenv); my @elements=('symb','courseid','domain','username'); foreach my $element (@elements) { @@ -1733,7 +1877,7 @@ show a snapshot of how student was answe sub get_student_answers { my ($symb,$username,$domain,$courseid,%form) = @_; - my ($map,$id,$feedurl) = split(/___/,$symb); + my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb); my (%old,%moreenv); my @elements=('symb','courseid','domain','username'); foreach my $element (@elements) { @@ -1752,6 +1896,30 @@ sub get_student_answers { =pod +=item * &submlink() + +Inputs: $text $uname $udom $symb + +Returns: A link to grades.pm such as to see the SUBM view of a student + +=cut + +############################################### +sub submlink { + my ($text,$uname,$udom,$symb)=@_; + if (!($uname && $udom)) { + (my $cursymb, my $courseid,$udom,$uname)= + &Apache::lonxml::whichuser($symb); + if (!$symb) { $symb=$cursymb; } + } + if (!$symb) { $symb=&symbread(); } + return '<a href="/adm/grades?symb='.$symb.'&student='.$uname. + '&userdom='.$udom.'&command=submission">'.$text.'</a>'; +} +############################################## + +=pod + =back =cut @@ -1952,6 +2120,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)/) { @@ -2180,6 +2349,14 @@ sub no_cache { #$r->header_out("Expires" => $date); } +sub content_type { + my ($r,$type,$charset) = @_; + unless ($charset) { + $charset=&Apache::lonlocal::current_encoding; + } + $r->content_type($type.($charset?'; charset='.$charset:'')); +} + =pod =item * add_to_env($name,$value) @@ -2460,6 +2637,23 @@ sub csv_samples_select_table { =pod +=item clean_excel_name($name) + +Returns a replacement for $name which does not contain any illegal characters. + +=cut + +sub clean_excel_name { + my ($name) = @_; + $name =~ s/[:\*\?\/\\]//g; + if (length($name) > 31) { + $name = substr($name,0,31); + } + return $name; +} + +=pod + =item * check_if_partid_hidden($id,$symb,$udom,$uname) Returns either 1 or undef @@ -2477,15 +2671,392 @@ $uname, optional the username of the use sub check_if_partid_hidden { my ($id,$symb,$udom,$uname) = @_; - my $hiddenparts=&Apache::lonnet::EXT('resource.0.parameter_hiddenparts', + 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 + +=head1 cgi-bin script and graphing routines + +=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 get_cgi_id { + return (time.'_'.int(rand(1000))); +} + +############################################################ +############################################################ + +=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 <img> 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 @Values: An array of array references. Each array reference holds data +to be plotted in a stacked bar chart. + +=back + +Returns: + +An <img> tag which references graph.png and the appropriate identifying +information for the plot. + +=cut + +############################################################ +############################################################ +sub DrawBarGraph { + my ($Title,$xlabel,$ylabel,$Max,$colors,@Values)=@_; + # + if (! defined($colors)) { + $colors = ['#33ff00', + '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933', + '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66', + ]; + } + # + my $identifier = &get_cgi_id(); + my $id = 'cgi.'.$identifier; + if (! @Values || ref($Values[0]) ne 'ARRAY') { + return ''; + } + my $NumBars = scalar(@{$Values[0]}); + my %ValuesHash; + my $NumSets=1; + foreach my $array (@Values) { + next if (! ref($array)); + $ValuesHash{$id.'.data.'.$NumSets++} = + join(',',@$array); + } + # + 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; + 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(%ValuesHash); + return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />'; +} + +############################################################ +############################################################ + +=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 <img> 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 <img> 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 '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />'; +} + +############################################################ +############################################################ + +=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 <img> 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 <img> 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 '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />'; +} + +############################################################ +############################################################ + +=pod + +=head1 Statistics helper routines? + +Bad place for them but what the hell. + +=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 + +=cut + +############################################################ +############################################################ +sub chartlink { + my ($linktext, $sname, $sdomain) = @_; + my $link = '<a href="/adm/statistics?reportSelected=student_assessment'. + '&SelectedStudent='.&Apache::lonnet::escape($sname.':'.$sdomain). + '&chartoutputmode='.HTML::Entities::encode('html, with all links'). + '">'.$linktext.'</a>'; +} + +############################################################ +############################################################ + =pod =back