--- loncom/xml/lonxml.pm 2007/10/08 09:20:22 1.462 +++ loncom/xml/lonxml.pm 2008/12/07 23:41:02 1.490 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # XML Parser Module # -# $Id: lonxml.pm,v 1.462 2007/10/08 09:20:22 foxr Exp $ +# $Id: lonxml.pm,v 1.490 2008/12/07 23:41:02 www Exp $ # # Copyright Michigan State University Board of Trustees # @@ -37,6 +37,25 @@ # to any other parties under any circumstances. # +=pod + +=head1 NAME + +Apache::lonxml + +=head1 SYNOPSIS + +XML Parsing Module + +This is part of the LearningOnline Network with CAPA project +described at http://www.lon-capa.org. + + +=head1 SUBROUTINES + +=cut + + package Apache::lonxml; use vars @@ -361,8 +380,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'}) { @@ -377,8 +396,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; @@ -482,10 +505,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); } } @@ -501,11 +524,11 @@ sub inner_xmlparse { if (!$Apache::lonxml::usestyle) { $extras=$Apache::lonxml::style_values; } - if ( $#$parstack > -1 ) { - $result=&Apache::run::evaluate($result,$safeeval,$extras.$$parstack[-1]); - } else { - $result= &Apache::run::evaluate($result,$safeeval,$extras); - } + if ( $#$parstack > -1 ) { + $result=&Apache::run::evaluate($result,$safeeval,$extras.$$parstack[-1]); + } else { + $result= &Apache::run::evaluate($result,$safeeval,$extras); + } } $Apache::lonxml::post_evaluate=1; @@ -631,15 +654,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=(); @@ -694,6 +738,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"); @@ -703,6 +748,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'); @@ -712,6 +758,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'); @@ -1047,28 +1096,29 @@ 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_id - optional part id.. during analysis, this - indicates whic part of a problem is being - counted. + $part_response - A concatenation of the part and response id + identifying exactly what is being 'answered'. + =cut sub increment_counter { - my ($increment, $part_id) = @_; + 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 part_id parameter, + # If the caller supplied the response_id parameter, # Maintain its counter.. creating if necessary. - if(defined($part_id)) { - if (!defined($Apache::lonxml::counters_per_part{$part_id})) { - $Apache::lonxml::counters_per_part{$part_id} = 0; + 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_id} += $increment; - my $new_value = $Apache::lonxml::counters_per_part{$part_id}; + $Apache::lonxml::counters_per_part{$part_response} += $increment; + my $new_value = $Apache::lonxml::counters_per_part{$part_response}; } $Apache::lonxml::counter_changed=1; @@ -1096,7 +1146,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 ''; } @@ -1117,7 +1167,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 { @@ -1129,22 +1179,21 @@ sub store_counter { =pod -=item bubble_lines_for_part(part_id) +=item bubble_lines_for_part(part_response) Returns the number of lines required to get a response for -$part_id (this is just $Apache::lonxml::counters_per_part{$part_id} +$part_response (this is just $Apache::lonxml::counters_per_part{$part_response} =cut sub bubble_lines_for_part { - my ($part_id) = @_; + my ($part_response) = @_; - if (!defined($Apache::lonxml::counters_per_part{$part_id})) { + if (!defined($Apache::lonxml::counters_per_part{$part_response})) { return 0; } else { - return $Apache::lonxml::counters_per_part{$part_id}; + return $Apache::lonxml::counters_per_part{$part_response}; } - } =pod @@ -1163,7 +1212,7 @@ sub clear_bubble_lines_for_part { =pod -=item set_bubble_lines(part_id, value) +=item set_bubble_lines(part_response, value) If there is a problem part, that for whatever reason requires bubble lines that are not @@ -1173,9 +1222,9 @@ analysis to set its hash value explicitl =cut sub set_bubble_lines { - my ($part_id, $value) = @_; + my ($part_response, $value) = @_; - $Apache::lonxml::counters_per_part{$part_id} = $value; + $Apache::lonxml::counters_per_part{$part_response} = $value; } =pod @@ -1355,7 +1404,7 @@ sub writeallows { &Apache::lonnet::hreflocation($thisdir,&unescape($_))}=$thisurl; } @extlinks=(); - &Apache::lonnet::appenv(%httpref); + &Apache::lonnet::appenv(\%httpref); } sub register_ssi { @@ -1422,7 +1471,7 @@ sub storefile { $fh->close(); return 1; } else { - &warning("Unable to save file $file"); + &warning(&mt('Unable to save file [_1]',''.$file.'')); return 0; } } @@ -1455,45 +1504,85 @@ 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 $textarea_id = 'filecont'; my ($add_to_onload, $add_to_onresize); - my $addbuttons=&Apache::lonhtmlcommon::htmlareaaddbuttons(); $initialize=&Apache::lonhtmlcommon::spellheader(); if ($filetype eq 'html' && (!&Apache::lonhtmlcommon::htmlareablocked() && &Apache::lonhtmlcommon::htmlareabrowser())) { - $textarea_id .= '_htmlarea'; + $textarea_id .= '___Frame'; + my $lang = &Apache::lonhtmlcommon::htmlarea_lang(); $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); - editor._iframe.id = '$textarea_id'; - resize_textarea('$textarea_id','LC_aftertextarea'); - },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'); } @@ -1508,38 +1597,45 @@ FULLPAGE $xml_help=&Apache::loncommon::helpLatexCheatsheet(); } - my $cleanbut = ''; - my $titledisplay=&display_title(); my %lt=&Apache::lonlocal::texthash('st' => 'Save and Edit', 'vi' => 'Save and View', '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=(<
-$xml_help - -$buttons
- -
-
$buttons -
-$titledisplay -
+
+ +
+ $filename + + $xml_help +
+
+ + + $spelllink $htmlerror +
+
+ + +
+
+ +
+
+ $titledisplay +
ENDFOOTER @@ -1605,6 +1701,9 @@ sub handler { if ($env{'form.savethisfile'} || $env{'form.viewmode'} || $env{'form.Undo'}) { my $html_file=&Apache::lonnet::getfile($file); my $error = &Apache::lonhomework::handle_save_or_undo($request, \$html_file, \$env{'form.filecont'}); + if ($env{'form.savethisfile'}) { + $env{'form.editmode'}='Edit'; #force edit mode + } } } my %mystyle; @@ -1669,11 +1768,12 @@ ENDNOTFOUND unless ($env{'request.state'} eq 'published') { if ($env{'form.editmode'} && (!($env{'form.viewmode'})) && (!($env{'form.discardview'}))) { - my ($edit_info, $add_to_onload, $add_to_onresize)= - &inserteditinfo($filecontents,$filetype); - my $displayfile=$request->uri; $displayfile=~s/^\/[^\/]*//; + + my ($edit_info, $add_to_onload, $add_to_onresize)= + &inserteditinfo($filecontents,$filetype,$displayfile); + my %options = ('add_entries' => {'onresize' => $add_to_onresize, @@ -1689,9 +1789,7 @@ ENDNOTFOUND my $start_page = &Apache::loncommon::start_page(undef,$js, \%options); $result=$start_page. - &Apache::lonxml::message_location().'

'. - $displayfile. - '

'. + &Apache::lonxml::message_location(). $edit_info. &Apache::loncommon::end_page(); } @@ -1712,7 +1810,8 @@ sub display_title { $title = $env{'request.filename'}; $title = substr($title, rindex($title, '/') + 1); } - $result = ""; + $result = ""; } return $result; } @@ -1749,6 +1848,8 @@ sub error { $errorcount++; + $Apache::lonxml::internal_error=1; + if (defined($Apache::inputtags::part)) { if ( @Apache::inputtags::response ) { push(@errors, @@ -1765,18 +1866,22 @@ sub error { if ( &show_error_warn_msg() ) { # If printing in construction space, put the error inside

 	push(@Apache::lonxml::error_messages,
-	     $Apache::lonxml::warnings_error_header.
-	     "ERROR:".join("
\n",@errors)."
\n"); + $Apache::lonxml::warnings_error_header + .'
' + .''.&mt('ERROR:').' '.join("
\n",@errors) + ."
\n"); $Apache::lonxml::warnings_error_header=''; } else { my $errormsg; my ($symb)=&Apache::lonnet::symbread(); if ( !$symb ) { #public or browsers - $errormsg=&mt("An error occured while processing this resource. The author has been notified."); + $errormsg=&mt("An error occurred while processing this resource. The author has been notified."); } my $host=$Apache::lonnet::perlvar{'lonHostID'}; - push(@errors, "The error occurred on host $host"); + push(@errors, + &mt("The error occurred on host [_1]", + "$host")); my $msg = join('
', @errors); @@ -1810,9 +1915,9 @@ sub error { } } if ($env{'request.role.adv'}) { - $errormsg=&mt("An error occured while processing this resource. The course personnel ([_1]) and the author have been notified.",join(', ',@userlist)); + $errormsg=&mt("An error occurred while processing this resource. The course personnel ([_1]) and the author have been notified.",join(', ',@userlist)); } else { - $errormsg=&mt("An error occured while processing this resource. The instructor has been notified."); + $errormsg=&mt("An error occurred while processing this resource. The instructor has been notified."); } } push(@Apache::lonxml::error_messages,"$errormsg
"); @@ -1825,8 +1930,11 @@ sub warning { if ($env{'form.grade_target'} ne 'tex') { if ( &show_error_warn_msg() ) { push(@Apache::lonxml::warning_messages, - $Apache::lonxml::warnings_error_header. - "WARNING:".join('
',@_)."
\n"); + $Apache::lonxml::warnings_error_header + .'
' + .&mt('[_1]W[_2]ARNING','','').": ".join('
',@_) + ."
\n" + ); $Apache::lonxml::warnings_error_header=''; } } @@ -2051,7 +2159,109 @@ sub get_tag { return $insertlist{"$tagnum.tag"}; } +############################################################ +# PDF-FORM-METHODS + +=pod + +=item &print_pdf_radiobutton(fieldname, value, text) + +Returns a latexline to generate a PDF-Form-Radiobutton with Text. + +$fieldname: PDF internalname of the radiobutton +$value: Value of radiobutton (read when dumping the PDF data) +$text: Text on the rightside of the radiobutton + +=cut +sub print_pdf_radiobutton { + my $result = ''; + my ($fieldName, $value, $text) = @_; + $result .= '\begin{tabularx}{\textwidth}{p{0cm}X}'."\n"; + $result .= '\radioButton[\symbolchoice{circle}]{'. + $fieldName.'}{10bp}{10bp}{'.$value.'}&'.$text."\n"; + $result .= '\end{tabularx}' . "\n"; + $result .= '\hspace{2mm}' . "\n"; + return $result; +} + + +=pod + +=item &print_pdf_start_combobox(fieldname) + +Starts a latexline to generate a PDF-Form-Combobox with text. + +$fieldname: PDF internal name of the Combobox + +=cut +sub print_pdf_start_combobox { + my $result; + my ($fieldName) = @_; + $result .= '\begin{tabularx}{\textwidth}{p{2.5cm}X}'."\n"; + $result .= '\comboBox[]{'.$fieldName.'}{2.3cm}{14bp}{'; # + + return $result; +} + + +=pod + +=item &print_pdf_add_combobox_option(options) + +Generates a latexline to add Options to a PDF-Form-ComboBox. + +$option: PDF internal name of the Combobox-Option + +=cut +sub print_pdf_add_combobox_option { + + my $result; + my ($option) = @_; + + $result .= '('.$option.')'; + + return $result; +} + + +=pod + +=item &print_pdf_end_combobox(text) { + +Returns latexcode to end a PDF-Form-Combobox with text. + +=cut +sub print_pdf_end_combobox { + my $result; + my ($text) = @_; + + $result .= '}&'.$text."\\\\\n"; + $result .= '\end{tabularx}' . "\n"; + $result .= '\hspace{2mm}' . "\n"; + return $result; +} + + +=pod + +=item &print_pdf_hiddenField(fieldname, user, domain) + +Returns a latexline to generate a PDF-Form-hiddenField with userdata. + +$fieldname label for hiddentextfield +$user: name of user +$domain: domain of user + +=cut +sub print_pdf_hiddenfield { + my $result; + my ($fieldname, $user, $domain) = @_; + + $result .= '\textField [\F{\FHidden}\F{-\FPrint}\V{'.$domain.'&'.$user.'}]{'.$fieldname.'}{0in}{0in}'."\n"; + + return $result; +} + 1; __END__ -