--- loncom/xml/lonxml.pm 2007/08/17 21:24:21 1.451
+++ loncom/xml/lonxml.pm 2008/12/11 22:19:45 1.484.4.1
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# XML Parser Module
#
-# $Id: lonxml.pm,v 1.451 2007/08/17 21:24:21 albertel Exp $
+# $Id: lonxml.pm,v 1.484.4.1 2008/12/11 22:19:45 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -92,8 +92,10 @@ use Apache::loncacc();
use Apache::lonmaxima();
use Apache::lonlocal;
-#================================================== Main subroutine: xmlparse
+#==================================== Main subroutine: xmlparse
+
#debugging control, to turn on debugging modify the correct handler
+
$Apache::lonxml::debug=0;
# keeps count of the number of warnings and errors generated in a parse
@@ -135,6 +137,16 @@ $Apache::lonxml::request='';
$Apache::lonxml::counter=1;
$Apache::lonxml::counter_changed=0;
+# Part counter hash. In analysis mode, the
+# problems can use this to record which parts increment the counter
+# by how much. The counter subs will maintain this hash via
+# their optional part parameters. Note that the assumption is that
+# analysis is done in one request and therefore it is not necessary to
+# save this information request-to-request.
+
+
+%Apache::lonxml::counters_per_part = ();
+
#internal check on whether to look at style defs
$Apache::lonxml::usestyle=1;
@@ -349,8 +361,8 @@ sub xmlparse {
$safeeval,\%style_for_target,1);
if (@stack) {
- &warning("At end of file some tags were still left unclosed, ".
- '<'.join('>, <',reverse(@stack)).
+ &warning(&mt('At end of file some tags were still left unclosed:').
+ ' <'.join('>, <',reverse(@stack)).
'>');
}
if ($env{'request.uri'}) {
@@ -365,8 +377,12 @@ sub xmlparse {
$finaloutput .= join('',@script_var_displays);
undef(@script_var_displays);
}
-
+ &init_state();
if ($env{'form.return_only_error_and_warning_counts'}) {
+ if ($env{'request.filename'}=~/\.(html|htm|xml)$/i) {
+ my $error=&verify_html($content_file_string);
+ if ($error) { $errorcount++; }
+ }
return "$errorcount:$warningcount";
}
return $finaloutput;
@@ -470,10 +486,10 @@ sub inner_xmlparse {
while ($token->[1] ne $$stack['-1'] && ($#$stack > -1)) {
my $lasttag=$$stack[-1];
if ($token->[1] =~ /^\Q$lasttag\E$/i) {
- &Apache::lonxml::warning('Using tag </'.$token->[1].'> on line '.$token->[3].' as end tag to <'.$$stack[-1].'>');
+ &Apache::lonxml::warning(&mt('Using tag [_1] on line [_2] as end tag to [_3]','</'.$token->[1].'>','.$token->[3].','<'.$$stack[-1].'>'));
last;
} else {
- &Apache::lonxml::warning('Found tag </'.$token->[1].'> on line '.$token->[3].' when looking for </'.$$stack[-1].'> in file');
+ &Apache::lonxml::warning(&mt('Found tag [_1] on line [_2] when looking for [_3] in file.','</'.$token->[1].'>',$token->[3],'</'.$$stack[-1].'>'));
&end_tag($stack,$parstack,$token);
}
}
@@ -619,14 +635,36 @@ sub callsub {
return $currentstring;
}
+{
+ my %state;
+
+ sub init_state {
+ undef(%state);
+ }
+
+ sub set_state {
+ my ($key,$value) = @_;
+ $state{$key} = $value;
+ return $value;
+ }
+ sub get_state {
+ my ($key) = @_;
+ return $state{$key};
+ }
+}
+
sub setup_globals {
my ($request,$target)=@_;
$Apache::lonxml::request=$request;
$errorcount=0;
$warningcount=0;
+ $Apache::lonxml::internal_error=0;
$Apache::lonxml::default_homework_loaded=0;
$Apache::lonxml::usestyle=1;
&init_counter();
+ &clear_bubble_lines_for_part();
+ &init_state();
+ &set_state('target',$target);
@Apache::lonxml::pwd=();
@Apache::lonxml::extlinks=();
@script_var_displays=();
@@ -681,6 +719,7 @@ sub init_safespace {
$safeeval->permit(":base_math");
$safeeval->permit("sort");
$safeeval->permit("time");
+ $safeeval->permit("caller");
$safeeval->deny("rand");
$safeeval->deny("srand");
$safeeval->deny(":base_io");
@@ -690,6 +729,7 @@ sub init_safespace {
$safehole->wrap(\&Apache::chemresponse::chem_standard_order,$safeeval,
'&chem_standard_order');
$safehole->wrap(\&Apache::response::check_status,$safeeval,'&check_status');
+ $safehole->wrap(\&Apache::response::implicit_multiplication,$safeeval,'&implicit_multiplication');
$safehole->wrap(\&Apache::lonmaxima::maxima_eval,$safeeval,'&maxima_eval');
$safehole->wrap(\&Apache::lonmaxima::maxima_check,$safeeval,'&maxima_check');
@@ -699,6 +739,9 @@ sub init_safespace {
$safehole->wrap(\&Apache::caparesponse::capa_formula_fix,$safeeval,
'&capa_formula_fix');
+ $safehole->wrap(\&Apache::lonlocal::locallocaltime,$safeeval,
+ '&locallocaltime');
+
$safehole->wrap(\&Math::Cephes::asin,$safeeval,'&asin');
$safehole->wrap(\&Math::Cephes::acos,$safeeval,'&acos');
$safehole->wrap(\&Math::Cephes::atan,$safeeval,'&atan');
@@ -800,6 +843,7 @@ sub init_safespace {
$safehole->wrap(\&Math::Random::random_set_seed_from_phrase,$safeeval,'&random_set_seed_from_phrase');
$safehole->wrap(\&Math::Random::random_get_seed,$safeeval,'&random_get_seed');
$safehole->wrap(\&Math::Random::random_set_seed,$safeeval,'&random_set_seed');
+ $safehole->wrap(\&Apache::loncommon::languages,$safeeval,'&languages');
$safehole->wrap(\&Apache::lonxml::error,$safeeval,'&LONCAPA_INTERNAL_ERROR');
$safehole->wrap(\&Apache::lonxml::debug,$safeeval,'&LONCAPA_INTERNAL_DEBUG');
$safehole->wrap(\&Apache::lonnet::logthis,$safeeval,'&LONCAPA_INTERNAL_LOGTHIS');
@@ -908,6 +952,9 @@ sub endredirection {
}
pop @Apache::lonxml::outputstack;
}
+sub in_redirection {
+ return ($Apache::lonxml::redirection > 0)
+}
sub end_tag {
my ($tagstack,$parstack,$token)=@_;
@@ -1005,8 +1052,15 @@ sub get_all_text_unbalanced {
}
}
return $result
+
}
+#########################################################################
+# #
+# bubble line counter management #
+# #
+#########################################################################
+
=pod
For bubble grading mode and exam bubble printing mode, the tracking of
@@ -1022,16 +1076,32 @@ Increments the internal counter environm
Optional Arguments:
$increment - amount to increment by (defaults to 1)
+ Also 1 if the value is negative or zero.
+ $part_response - A concatenation of the part and response id
+ identifying exactly what is being 'answered'.
+
=cut
sub increment_counter {
- my ($increment) = @_;
- if (defined($increment) && $increment gt 0) {
- $Apache::lonxml::counter+=$increment;
- } else {
- $Apache::lonxml::counter++;
+ my ($increment, $part_response) = @_;
+ if ($env{'form.grade_noincrement'}) { return; }
+ if (!defined($increment) || $increment le 0) {
+ $increment = 1;
}
+ $Apache::lonxml::counter += $increment;
+
+ # If the caller supplied the response_id parameter,
+ # Maintain its counter.. creating if necessary.
+
+ if (defined($part_response)) {
+ if (!defined($Apache::lonxml::counters_per_part{$part_response})) {
+ $Apache::lonxml::counters_per_part{$part_response} = 0;
+ }
+ $Apache::lonxml::counters_per_part{$part_response} += $increment;
+ my $new_value = $Apache::lonxml::counters_per_part{$part_response};
+ }
+
$Apache::lonxml::counter_changed=1;
}
@@ -1057,7 +1127,7 @@ sub init_counter {
}
sub store_counter {
- &Apache::lonnet::appenv(('form.counter' => $Apache::lonxml::counter));
+ &Apache::lonnet::appenv({'form.counter' => $Apache::lonxml::counter});
$Apache::lonxml::counter_changed=0;
return '';
}
@@ -1078,7 +1148,7 @@ sub store_counter {
sub restore_problem_counter {
if (defined($state)) {
- &Apache::lonnet::appenv(('form.counter' => $state));
+ &Apache::lonnet::appenv({'form.counter' => $state});
}
}
sub get_problem_counter {
@@ -1088,6 +1158,73 @@ sub store_counter {
}
}
+=pod
+
+=item bubble_lines_for_part(part_response)
+
+Returns the number of lines required to get a response for
+$part_response (this is just $Apache::lonxml::counters_per_part{$part_response}
+
+=cut
+
+sub bubble_lines_for_part {
+ my ($part_response) = @_;
+
+ if (!defined($Apache::lonxml::counters_per_part{$part_response})) {
+ return 0;
+ } else {
+ return $Apache::lonxml::counters_per_part{$part_response};
+ }
+}
+
+=pod
+
+=item clear_bubble_lines_for_part
+
+Clears the hash of bubble lines per part. If a caller
+needs to analyze several resources this should be called between
+resources to reset the hash for each problem being analyzed.
+
+=cut
+
+sub clear_bubble_lines_for_part {
+ undef(%Apache::lonxml::counters_per_part);
+}
+
+=pod
+
+=item set_bubble_lines(part_response, value)
+
+If there is a problem part, that for whatever reason
+requires bubble lines that are not
+the same as the counter increment, it can call this sub during
+analysis to set its hash value explicitly.
+
+=cut
+
+sub set_bubble_lines {
+ my ($part_response, $value) = @_;
+
+ $Apache::lonxml::counters_per_part{$part_response} = $value;
+}
+
+=pod
+
+=item get_bubble_line_hash
+
+Returns the current bubble line hash. This is assumed to
+be small so we return a copy
+
+
+=cut
+
+sub get_bubble_line_hash {
+ return %Apache::lonxml::counters_per_part;
+}
+
+
+#--------------------------------------------------
+
sub get_all_text {
my($tag,$pars,$style)= @_;
my $gotfullstack=1;
@@ -1248,7 +1385,7 @@ sub writeallows {
&Apache::lonnet::hreflocation($thisdir,&unescape($_))}=$thisurl;
}
@extlinks=();
- &Apache::lonnet::appenv(%httpref);
+ &Apache::lonnet::appenv(\%httpref);
}
sub register_ssi {
@@ -1315,7 +1452,7 @@ sub storefile {
$fh->close();
return 1;
} else {
- &warning("Unable to save file $file");
+ &warning(&mt('Unable to save file [_1]',''.$file.''));
return 0;
}
}
@@ -1348,50 +1485,98 @@ SIMPLECONTENT
return $filecontents;
}
+sub verify_html {
+ my ($filecontents)=@_;
+ if ($filecontents!~/(?:\<|\<\;)(?:html|xml)[^\<]*(?:\>|\>\;)/is) {
+ return &mt('File does not have [_1] or [_2] starting tag','<html>','<xml>');
+ }
+ if ($filecontents!~/(?:\<|\<\;)\/(?:html|xml)(?:\>|\>\;)/is) {
+ return &mt('File does not have [_1] or [_2] ending tag','<html>','<xml>');
+ }
+ if ($filecontents!~/(?:\<|\<\;)(?:body|frameset)[^\<]*(?:\>|\>\;)/is) {
+ return &mt('File does not have [_1] or [_2] starting tag','<body>','<frameset>');
+ }
+ if ($filecontents!~/(?:\<|\<\;)\/(?:body|frameset)[^\<]*(?:\>|\>\;)/is) {
+ return &mt('File does not have [_1] or [_2] ending tag','<body>','<frameset>');
+ }
+ return '';
+}
+
+sub renderingoptions {
+ my %langchoices=('' => '');
+ foreach (&Apache::loncommon::languageids()) {
+ if (&Apache::loncommon::supportedlanguagecode($_)) {
+ $langchoices{&Apache::loncommon::supportedlanguagecode($_)}
+ = &Apache::loncommon::plainlanguagedescription($_);
+ }
+ }
+ return
+ ''.
+ &mt('Language:').' '.
+ &Apache::loncommon::select_form($env{'form.languages'},'languages',
+ %langchoices).'
+
+ '.
+ &mt('Math Rendering:').' '.
+ &Apache::loncommon::select_form($env{'form.texengine'},'texengine',
+ ('' => '',
+ 'tth' => 'tth (TeX to HTML)',
+ 'jsMath' => 'jsMath',
+ 'mimetex' => 'mimetex (Convert to Images)')).'
+ ';
+}
sub inserteditinfo {
- my ($filecontents,$filetype)=@_;
+ my ($filecontents, $filetype, $filename)=@_;
$filecontents = &HTML::Entities::encode($filecontents,'<>&"');
-# my $editheader='Edit below';
my $xml_help = '';
my $initialize='';
- my $add_to_onload;
- if ($filetype eq 'html') {
- my $addbuttons=&Apache::lonhtmlcommon::htmlareaaddbuttons();
- $initialize=&Apache::lonhtmlcommon::spellheader();
- if (!&Apache::lonhtmlcommon::htmlareablocked() &&
- &Apache::lonhtmlcommon::htmlareabrowser()) {
- $initialize.=(<
-$addbuttons
-
- HTMLArea.loadPlugin("FullPage");
-
+lonca
function initDocument() {
- var editor=new HTMLArea("filecont",config);
- editor.registerPlugin(FullPage);
- editor.generate();
- setTimeout(
- function () {
- HTMLArea._addEvents(editor._doc,
- ["keypress","mousedown"], unClean);
- },300);
+ var oFCKeditor = new FCKeditor('filecont');
+ oFCKeditor.Config['CustomConfigurationsPath'] = '/fckeditor/loncapaconfig.js' ;
+ oFCKeditor.Config['FullPage'] = true
+ oFCKeditor.Config['AutoDetectLanguage'] = false;
+ oFCKeditor.Config['DefaultLanguage'] = "$lang";
+ oFCKeditor.ReplaceTextarea();
+ }
+ function check_if_dirty(editor) {
+ if (editor.IsDirty()) {
+ unClean();
+ }
+ }
+ function FCKeditor_OnComplete(editor) {
+ editor.Events.AttachEvent("OnSelectionChange",check_if_dirty);
+ resize_textarea('$textarea_id','LC_aftertextarea');
}
FULLPAGE
- } else {
- $initialize.=(<
-$addbuttons
function initDocument() {
+ resize_textarea('$textarea_id','LC_aftertextarea');
}
FULLPAGE
- }
- $add_to_onload = 'initDocument();';
+ }
+
+ $add_to_onload = 'initDocument();';
+ $add_to_onresize = "resize_textarea('$textarea_id','LC_aftertextarea');";
+
+ if ($filetype eq 'html') {
$xml_help=&Apache::loncommon::helpLatexCheatsheet();
}
- my $cleanbut = '';
my $titledisplay=&display_title();
my %lt=&Apache::lonlocal::texthash('st' => 'Save and Edit',
@@ -1399,32 +1584,43 @@ FULLPAGE
'dv' => 'Discard Edits and View',
'un' => 'undo',
'ed' => 'Edit');
- my $buttons=(<
-
-
-
-BUTTONS
- $buttons.=&Apache::lonhtmlcommon::spelllink('xmledit','filecont');
+ my $spelllink .=&Apache::lonhtmlcommon::spelllink('xmledit','filecont');
my $textarea_events = &Apache::edit::element_change_detection();
my $form_events = &Apache::edit::form_change_detection();
+ my $htmlerror=&verify_html($filecontents);
+ if ($htmlerror) {
+ $htmlerror=''.$htmlerror.'';
+ }
my $editfooter=(<
-$titledisplay