From d1629ee5245a5ec0d7ffd7373a72f705499de1e3 Mon Sep 17 00:00:00 2001 From: Michael Gage <1203580+mgage@users.noreply.github.com> Date: Sun, 6 Oct 2019 12:15:18 -0400 Subject: [PATCH 1/3] Remove references to Crypt::SSLeay which is no longer needed for https --- bin/check_modules.pl | 4 +- clients/sendXMLRPC.pl | 1 - .../ContentGenerator/instructorXMLHandler.pm | 1 - .../ContentGenerator/renderViaXMLRPC.pm | 2 +- lib/WebworkClient.pm.orig | 1085 +++++++++++++++++ 5 files changed, 1087 insertions(+), 6 deletions(-) create mode 100755 lib/WebworkClient.pm.orig diff --git a/bin/check_modules.pl b/bin/check_modules.pl index d83f1a40b1..80002c0f6f 100755 --- a/bin/check_modules.pl +++ b/bin/check_modules.pl @@ -37,9 +37,7 @@ Apache2::ServerUtil ); -# Crypt::SSLeay was commented out below, but should the not be -# in the array when commented out - remove it. -# For WW 2.15 replace Email::Address with Email::Address::XS + my @modulesList = qw( Array::Utils diff --git a/clients/sendXMLRPC.pl b/clients/sendXMLRPC.pl index 0dc6595dd6..16559e97ca 100755 --- a/clients/sendXMLRPC.pl +++ b/clients/sendXMLRPC.pl @@ -306,7 +306,6 @@ BEGIN use Carp; -#use Crypt::SSLeay; # needed for https use LWP::Protocol::https; use Time::HiRes qw/time/; use MIME::Base64 qw( encode_base64 decode_base64); diff --git a/lib/WeBWorK/ContentGenerator/instructorXMLHandler.pm b/lib/WeBWorK/ContentGenerator/instructorXMLHandler.pm index 73015a6f84..3c2c483be5 100644 --- a/lib/WeBWorK/ContentGenerator/instructorXMLHandler.pm +++ b/lib/WeBWorK/ContentGenerator/instructorXMLHandler.pm @@ -31,7 +31,6 @@ use PGUtil qw(not_null); our $UNIT_TESTS_ON = 0; # should be called DEBUG?? FIXME -#use Crypt::SSLeay; #use XMLRPC::Lite; use strict; diff --git a/lib/WeBWorK/ContentGenerator/renderViaXMLRPC.pm b/lib/WeBWorK/ContentGenerator/renderViaXMLRPC.pm index de8c911df7..3d7efe82f0 100644 --- a/lib/WeBWorK/ContentGenerator/renderViaXMLRPC.pm +++ b/lib/WeBWorK/ContentGenerator/renderViaXMLRPC.pm @@ -27,7 +27,7 @@ use warnings; package WeBWorK::ContentGenerator::renderViaXMLRPC; use base qw(WeBWorK::ContentGenerator); -#use Crypt::SSLeay; + #use XMLRPC::Lite; #use MIME::Base64 qw( encode_base64 decode_base64); diff --git a/lib/WebworkClient.pm.orig b/lib/WebworkClient.pm.orig new file mode 100755 index 0000000000..2e409feb93 --- /dev/null +++ b/lib/WebworkClient.pm.orig @@ -0,0 +1,1085 @@ +#!/usr/bin/perl -w + +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WebworkClient.pm,v 1.1 2010/06/08 11:46:38 gage Exp $ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +################################################################################ + +=head1 NAME + +WebworkClient.pm + + +=head1 SYNPOSIS + our $xmlrpc_client = new WebworkClient ( + url => $XML_URL, + form_action_url => $FORM_ACTION_URL, + site_password => $XML_PASSWORD//'', + courseID => $credentials{courseID}, + userID => $credentials{userID}, + session_key => $credentials{session_key}//'', + sourceFilePath => $fileName, + ); + +Remember to configure the local output file and display command !!!!!!!! + + + +=head1 DESCRIPTION + +This script will take a file and send it to a WeBWorK daemon webservice +to have it rendered. + +The result returned is split into the basic HTML rendering +and evaluation of answers and then passed to a browser for printing. + +The formatting allows the browser presentation to be interactive with the +daemon running the script webwork2/lib/renderViaXMLRPC.pm +and with instructorXMLRPChandler. + +See WebworkWebservice.pm for related modules which operate on the server side + + WebworkXMLRPC (contained in WebworkWebservice.pm) + renderViaXMLRPC + instructorXMLRPChandler + +=cut + +use strict; +use warnings; + + +# To configure the target webwork server +# two URLs are required +# 1. $XML_URL http://test.webwork.maa.org/mod_xmlrpc +# points to the Webservice.pm and Webservice/RenderProblem modules +# Is used by the client to send the original XML request to the webservice +# +# 2. $FORM_ACTION_URL http:http://test.webwork.maa.org/webwork2/html2xml +# points to the renderViaXMLRPC.pm module. +# +# This url is placed as form action url when the rendered HTML from the original +# request is returned to the client from Webservice/RenderProblem. The client +# reorganizes the XML it receives into an HTML page (with a WeBWorK form) and +# pipes it through a local browser. +# +# The browser uses this url to resubmit the problem (with answers) via the standard +# HTML webform used by WeBWorK to the renderViaXMLRPC.pm handler. +# +# This renderViaXMLRPC.pm handler acts as an intermediary between the browser +# and the webservice. It interprets the HTML form sent by the browser, +# rewrites the form data in XML format, submits it to the WebworkWebservice.pm +# which processes it and sends the the resulting HTML back to renderViaXMLRPC.pm +# which in turn passes it back to the browser. +# 3. The second time a problem is submitted renderViaXMLRPC.pm receives the WeBWorK form +# submitted directly by the browser. +# The renderViaXMLRPC.pm translates the WeBWorK form, has it processes by the webservice +# and returns the result to the browser. +# The The client renderProblem.pl script is no longer involved. +# 4. Summary: renderProblem.pl is only involved in the first round trip +# of the submitted problem. After that the communication is between the browser and +# renderViaXMLRPC using HTML forms and between renderViaXMLRPC and the WebworkWebservice.pm +# module using XML_RPC. + + +our @COMMANDS = qw( listLibraries renderProblem ); #listLib readFile tex2pdf + + + +################################################## +# XMLRPC client -- +# this code is identical between renderProblem.pl and renderViaXMLRPC.pm???? +################################################## + +package WebworkClient; + + + +use LWP::Protocol::https; +use lib "$WeBWorK::Constants::WEBWORK_DIRECTORY/lib"; +use lib "$WeBWorK::Constants::PG_DIRECTORY/lib"; +use XMLRPC::Lite; +use WeBWorK::Utils qw( wwRound encode_utf8_base64 decode_utf8_base64); +use Encode qw(encode_utf8 decode_utf8); +use WeBWorK::Utils::AttemptsTable; +use WeBWorK::CourseEnvironment; +use WeBWorK::Utils::DetermineProblemLangAndDirection; +use WeBWorK::PG::ImageGenerator; +use HTML::Entities; +use WeBWorK::Localize; +use WeBWorK::PG::ImageGenerator; +use IO::Socket::SSL; +use Digest::SHA qw(sha1_base64); +use XML::Simple qw(XMLout); +use JSON; +use FormatRenderedProblem; + +use constant TRANSPORT_METHOD => 'XMLRPC::Lite'; +use constant REQUEST_CLASS => 'WebworkXMLRPC'; # WebworkXMLRPC is used for soap also!! +use constant REQUEST_URI => 'mod_xmlrpc'; + +our $UNIT_TESTS_ON = 0; + +################## +# static variables + +# create seed_ce +# then create imgGen +our $seed_ce; + +eval { + $seed_ce = WeBWorK::CourseEnvironment->new( + {webwork_dir => $WeBWorK::Constants::WEBWORK_DIRECTORY, + courseName => '', + webworkURL => '', + pg_dir => $WeBWorK::Constants::PG_DIRECTORY, + }); +}; + if ($@ or not ref($seed_ce)){ + warn "Unable to find environment for WebworkClient: + webwork_dir => $WeBWorK::Constants::WEBWORK_DIRECTORY + pg_dir => $WeBWorK::Constants::PG_DIRECTORY"; + } + + + +our %imagesModeOptions = %{$seed_ce->{pg}->{displayModeOptions}->{images}}; +our $site_url = $seed_ce->{server_root_url}//''; +our $imgGen = WeBWorK::PG::ImageGenerator->new( + tempDir => $seed_ce->{webworkDirs}->{tmp}, + latex => $seed_ce->{externalPrograms}->{latex}, + dvipng => $seed_ce->{externalPrograms}->{dvipng}, + useCache => 1, + cacheDir => $seed_ce->{webworkDirs}->{equationCache}, + cacheURL => $site_url . $seed_ce->{webworkURLs}->{equationCache}, + cacheDB => $seed_ce->{webworkFiles}->{equationCacheDB}, + dvipng_align => $imagesModeOptions{dvipng_align}, + dvipng_depth_db => $imagesModeOptions{dvipng_depth_db}, +); + + +sub new { #WebworkClient constructor + my $invocant = shift; + my $class = ref $invocant || $invocant; + my $self = { + return_object => {}, + request_object => {}, + error_string => '', + encoded_source => '', + url => '', + course_password => '', + site_password => '', + courseID => '', + userID => '', + inputs_ref => { AnSwEr0001 => '', + AnSwEr0002 => '', + AnSwEr0003 => '', + displayMode => 'no displayMode defined', + forcePortNumber => '', + }, + @_, # options and overloads + }; + + bless $self, $class; +} + + +our $result; + +################################################## +# Utilities -- +# this code is identical between renderProblem.pl and renderViaXMLRPC.pm +################################################## + +=head2 xmlrpcCall + + + + $xmlrpc_client->encodeSource($source); + $xmlrpc_client->{sourceFilePath} = $fileName; + + my $input = { + userID => $credentials{userID}//'', + session_key => $credentials{session_key}//'', + courseID => $credentials{courseID}//'', + courseName => $credentials{courseID}//'', + course_password => $credentials{course_password}//'', + site_password => $XML_PASSWORD//'', + envir => $xmlrpc_client->environment( + fileName => $fileName, + sourceFilePath => $fileName + ), + }; + our($output, $return_string, $result); + + + if ( $result = $xmlrpc_client->xmlrpcCall('renderProblem', $input) ) { + $output = $xmlrpc_client->formatRenderedProblem; + } else { + $output = $xmlrpc_client->return_object; # error report + } + + Keys in $result or in $xmlrpc_client->return_object for the command "renderProblem" + session_key + flags + errors + internal_debug_messages + WARNINGS + problem_state + debug_messages + userID + compute_time + warning_messages + courseID + text + problem_result + header_text + answers + + +=cut + + + + + +sub xmlrpcCall { + my $self = shift; + my $command = shift; + my $input = shift||{}; + my $requestObject; + $command = 'listLibraries' unless defined $command; + my $default_inputs = $self->default_inputs(); + $requestObject = {%$default_inputs, %$input}; #input values can override default inputs + + $self->request_object($requestObject); # store the request object for later + + my $requestResult; + my $transporter = TRANSPORT_METHOD->new; + + eval { + $requestResult= $transporter + #->uri('http://'.HOSTURL.':'.HOSTPORT.'/'.REQUEST_CLASS) + #-> proxy(PROTOCOL.'://'.HOSTURL.':'.HOSTPORT.'/'.REQUEST_URI); + -> proxy(($self->url).'/'.REQUEST_URI); + }; + print STDERR "WebworkClient: Initiating xmlrpc request to url ",($self->url).'/'.REQUEST_URI, " \n Error: $@\n" if $@; + # turn off verification of the ssl cert + $transporter->transport->ssl_opts(verify_hostname=>0, + SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE); + + if ($UNIT_TESTS_ON) { + print STDERR "WebworkClient.pm ".__LINE__." xmlrpcCall sent to ", $self->url,"\n"; + print STDERR "WebworkClient.pm ".__LINE__." xmlrpcCall issued with command $command\n"; + print STDERR "WebworkClient.pm ".__LINE__." input is: ",join(" ", %{$self->request_object}),"\n"; + print STDERR "WebworkClient.pm ".__LINE__." xmlrpcCall $command initiated webwork webservice object $requestResult\n"; + } + + local( $result); + # use eval to catch errors + #print STDERR "WebworkClient: issue command ", REQUEST_CLASS.'.'.$command, " ",join(" ", %$input),"\n"; + eval { $result = $requestResult->call(REQUEST_CLASS.'.'.$command, $self->request_object ) }; + # result is of type XMLRPC::SOM + print STDERR "There were a lot of errors\n" if $@; + print STDERR "Errors: \n $@\n End Errors\n" if $@; + + print CGI::h2("WebworkClient Errors") if $@; + print CGI::p("Errors:",CGI::br(),CGI::blockquote({style=>"color:red"},CGI::code($@)),CGI::br(),"End Errors") if $@; + + if (not ref($result) ) { + my $error_string = "xmlrpcCall to $command returned no result for ". + ($self->{sourceFilePath}//'')."\n"; + print STDERR $error_string; + $self->error_string($error_string); + $self->fault(1); + return $self; + } elsif ( $result->fault ) { # report errors + my $error_string = 'Error message for '. + join( ' ', + "command:", + $command, + "\n
faultcode:", + $result->faultcode, + "\n
faultstring:", + $result->faultstring, "\n
End error message
\n" + ); + + print STDERR $error_string; + $self->return_object($result->result()); + $self->error_string($error_string); + $self->fault(1); # set fault flag to true + return $self; + } else { + if (ref($result->result())=~/HASH/ and defined($result->result()->{text}) ) { + $result->result()->{text} = decode_utf8_base64($result->result()->{text}); + } + if (ref($result->result())=~/HASH/ and defined($result->result()->{header_text}) ) { + $result->result()->{header_text} = decode_utf8_base64($result->result()->{header_text}); + } + + $self->return_object($result->result()); + # print "\n retrieve result ", keys %{$self->return_object}; + return $self->return_object; # $result->result(); + # would it be better to return the entire $result? + # probably not, there is no hash directly available from the $result object. + } + +} + + +=head2 jsXmlrpcCall + +=cut + +sub jsXmlrpcCall { + my $self = shift; + my $command = shift; + my $input = shift; + $command = 'listLibraries' unless $command; + if ($UNIT_TESTS_ON) { + print STDERR "WebworkClient.pm ".__LINE__." jsXmlrpcCall issued with command $command\n"; + } + + print "the command was $command"; + + my $transporter = TRANSPORT_METHOD->new; + + my $requestResult = $transporter + -> proxy(($self->url).'/'.REQUEST_URI); + $transporter->transport->ssl_opts(verify_hostname=>0, + SSL_verify_mode => 'SSL_VERIFY_NONE'); + + local( $result); + # use eval to catch errors + eval { $result = $requestResult->call(REQUEST_CLASS.'.'.$command,$input) }; + if ($@) { + print STDERR "There were a lot of errors for $command\n" ; + print STDERR "Errors: \n $@\n End Errors\n" ; + return 0 #failure + } + print "hmm $result"; + unless (ref($result) and $result->fault) { + my $rh_result = $result->result(); + print "\n success \n"; + print pretty_print($rh_result->{'ra_out'}); + $self->return_object( $rh_result ); + return 1; # success + + } else { + $self->return_object( 'Error from server: '. join( ",\n ", + $result->faultcode, + $result->faultstring) + ); + return 0; #failure + } +} + +=head2 encodeSource + + +=cut +sub encodeSource { + my $self = shift; + my $source = shift||''; + $self->{encoded_source} =encode_utf8_base64($source); +} + +=head2 Accessor methods + + encoded_source + request_object + return_object + error_string + fault + url + form_data + +=cut + +sub encoded_source { + my $self = shift; + my $source = shift; + $self->{encoded_source} =$source if defined $source and $source =~/\S/; # source is non-empty + $self->{encoded_source}; +} +sub request_object { # in or input + my $self = shift; + my $object = shift; + $self->{request_object} =$object if defined $object and ref($object); # source is non-empty + $self->{request_object}; +} +sub return_object { # out + my $self = shift; + my $object = shift; + $self->{return_object} =$object if defined $object and ref($object); # source is non-empty + $self->{return_object}; +} +sub error_string { + my $self = shift; + my $string = shift; + $self->{error_string} =$string if defined $string and $string =~/\S/; # source is non-empty + $self->{error_string}; +} +sub fault { + my $self = shift; + my $fault_flag = shift; + $self->{fault_flag} =$fault_flag if defined $fault_flag and $fault_flag =~/\S/; # source is non-empty + $self->{fault_flag}; +} +sub url { + my $self = shift; + my $new_url = shift; + $self->{url} = $new_url if defined($new_url) and $new_url =~ /\S/; + $self->{url}; +} + +sub form_data { + my $self = shift; + my $form_data = shift; + $self->{inputs_ref} = $form_data if defined($form_data) and $form_data =~ /\S/; + $self->{inputs_ref}; +} + +=head2 initiate default values + +=cut +sub setInputTable_for_listLib { + my $self = shift; + my $out = { + set => 'set0', + library_name => 'Library', + command => 'all', + }; + + $out; +} + +sub default_inputs { + my $self = shift; + my $webwork_dir = $WeBWorK::Constants::WEBWORK_DIRECTORY; #'/opt/webwork/webwork2'; + my $seed_ce = new WeBWorK::CourseEnvironment({ webwork_dir => $webwork_dir}); + die "Can't create seed course environment for webwork in $webwork_dir" unless ref($seed_ce); + + $self->{seed_ce} = $seed_ce; + + my @modules_to_evaluate; + my @extra_packages_to_load; + my @modules = @{ $seed_ce->{pg}->{modules} }; + + foreach my $module_packages_ref (@modules) { + my ($module, @extra_packages) = @$module_packages_ref; + # the first item is the main package + push @modules_to_evaluate, $module; + # the remaining items are "extra" packages + push @extra_packages_to_load, @extra_packages; + } + + my $out = { + library_name => 'Library', + command => 'renderProblem', + answer_form_submitted => 1, + course => $self->{course}, + extra_packages_to_load => [@extra_packages_to_load], + mode => $self->{displayMode}, + displayMode => $self->{displayMode}, + modules_to_evaluate => [@modules_to_evaluate], + envir => $self->environment(), + problem_state => { + + num_of_correct_ans => 0, + num_of_incorrect_ans => 4, + recorded_score => 1.0, + }, + source => $self->encoded_source, #base64 encoded + }; + + $out; +} + +=item environment + +=cut + +sub environment { + my $self = shift; + my $envir = { + answerDate => '4014438528', + CAPA_Graphics_URL=>'http://webwork-db.math.rochester.edu/capa_graphics/', + CAPA_GraphicsDirectory =>'/ww/webwork/CAPA/CAPA_Graphics/', + CAPA_MCTools=>'/ww/webwork/CAPA/CAPA_MCTools/', + CAPA_Tools=>'/ww/webwork/CAPA/CAPA_Tools/', + cgiDirectory=>'Not defined', + cgiURL => 'foobarNot defined', + classDirectory=> 'Not defined', + courseName=>'Not defined', + courseScriptsDirectory=>'not defined', + displayMode=>$self->{inputs_ref}->{displayMode}//"no display mode defined in WebworkClient-> environment", + dueDate=> '4014438528', + effectivePermissionLevel => 10, + externalGif2EpsPath=>'not defined', + externalPng2EpsPath=>'not defined', + externalTTHPath=>'/usr/local/bin/tth', + fileName=>'WebworkClient.pm:: define fileName in environment', + formattedAnswerDate=>'6/19/00', + formattedDueDate=>'6/19/00', + formattedOpenDate=>'6/19/00', + functAbsTolDefault=> 0.0000001, + functLLimitDefault=>0, + functMaxConstantOfIntegration=> 1000000000000.0, + functNumOfPoints=> 5, + functRelPercentTolDefault=> 0.000001, + functULimitDefault=>1, + functVarDefault=> 'x', + functZeroLevelDefault=> 0.000001, + functZeroLevelTolDefault=>0.000001, + htmlDirectory =>'not defined', + htmlURL =>'not defined', + inputs_ref => $self->{inputs_ref}, + macroDirectory=>'not defined', + numAbsTolDefault=>0.0000001, + numFormatDefault=>'%0.13g', + numOfAttempts=> 0, + numRelPercentTolDefault => 0.0001, + numZeroLevelDefault =>0.000001, + numZeroLevelTolDefault =>0.000001, + openDate=> '3014438528', + permissionLevel =>10, + PRINT_FILE_NAMES_FOR => [ 'gage'], + probFileName => 'WebworkClient.pm:: define probFileName in environment', + problemSeed => $self->{inputs_ref}->{problemSeed}//3333, + problemUUID => $self->{inputs_ref}->{problemUUID}//0, + problemValue =>1, + probNum => 13, + psvn => $self->{inputs_ref}->{psvn}//54321, + questionNumber => 1, + scriptDirectory => 'Not defined', + sectionName => 'Gage', + sectionNumber => 1, + server_root_url =>"foobarfoobar", + sessionKey=> 'Not defined', + setNumber =>'not defined', + studentLogin =>'gage', + studentName => 'Mike Gage', + tempDirectory => 'not defined', + templateDirectory=>'not defined', + tempURL=>'not defined', + webworkDocsURL => 'not defined', + showHints => 1, # extra options -- usually passed from the input form + showSolutions => 1, + @_, + }; + $envir; +}; + +=item formatRenderedLibraries + +=cut + +sub formatRenderedLibraries { + my $self = shift; + #my @rh_result = @{$self->return_object}; # wrap problem in formats + my %rh_result = %{$self->return_object}; + my $result = ""; + foreach my $key (sort keys %rh_result) { + $result .= "$key"; + $result .= $rh_result{$key}; + } + return $result; +} + +=item formatRenderedProblem + +=cut + +sub formatRenderedProblem { +<<<<<<< HEAD + FormatRenderedProblem::formatRenderedProblem(@_); +======= + my $self = shift; + my $problemText =''; + my $rh_result = $self->return_object() || {}; # wrap problem in formats + $problemText = "No output from rendered Problem" unless $rh_result ; + #print "formatRenderedProblem text $rh_result = ",%$rh_result,"\n"; + if (ref($rh_result) and $rh_result->{text} ) { + $problemText = $rh_result->{text}; + } else { + $problemText .= "Unable to decode problem text
\n". + $self->{error_string}."\n". + format_hash_ref($rh_result); + } + my $problemHeadText = $rh_result->{header_text}//''; + my $rh_answers = $rh_result->{answers}//{}; + my $answerOrder = $rh_result->{flags}->{ANSWER_ENTRY_ORDER}; #[sort keys %{ $rh_result->{answers} }]; + my $encoded_source = $self->encoded_source//''; + my $sourceFilePath = $self->{sourceFilePath}//''; + my $warnings = ''; + my $answerhashXML = XMLout($rh_answers, RootName => 'answerhashes'); + + ################################################# + # Code to get and set problem language and direction based on flags set by the PG problem. + # This uses the same utility function as used by lib/WeBWorK/ContentGenerator/Problem.pm + # and various modules in lib/WeBWorK/ContentGenerator/Instructor/ . + # However, for technical reasons it requires using additional optional arguments + # which were added for the use here as they are not available via an internal + # CourseEnvironment where it was available in the other uses. + ################################################# + # Need to set things like $PROBLEM_LANG_AND_DIR = "lang=\"he\" dir=\"rtl\""; + + my $formLanguage = ($self->{inputs_ref}->{language})//'en'; + + my @PROBLEM_LANG_AND_DIR = (); + + my $mode_for_get_problem_lang_and_dir = "auto:en:ltr"; # Will be used to set the default + # Setting to force English and LTR always: + # $mode_for_get_problem_lang_and_dir = "force:en:ltr"; + # Setting to avoid any setting be used: + # $mode_for_get_problem_lang_and_dir = "none"; + + my @to_set_lang_dir = get_problem_lang_and_dir( $self, $rh_result, $mode_for_get_problem_lang_and_dir, $formLanguage ); + # We are calling get_problem_lang_and_dir() when $self does not + # have a request hash called "r" inside it, so need to set the requested + # and the course-wide language. We request mode $mode_for_get_problem_lang_and_dir + # which by default is set above to "auto:en:ltr" so PG files can request their + # language and text direction be set, but falls back to English and LTR. + # We also do not have access to a default course language in the same sense + # so use the $formLanguage instead. + + while ( scalar(@to_set_lang_dir) > 0 ) { + push( @PROBLEM_LANG_AND_DIR, shift( @to_set_lang_dir ) ); # HTML tag being set + push( @PROBLEM_LANG_AND_DIR, "=\"" ); + push( @PROBLEM_LANG_AND_DIR, shift( @to_set_lang_dir ) ); # HTML value being set + push( @PROBLEM_LANG_AND_DIR, "\" " ); + } + my $PROBLEM_LANG_AND_DIR = join("",@PROBLEM_LANG_AND_DIR); + + ################################################# + # Code to get and set main language and direction for generated HTML pages. + # Very similar to the code in output_course_lang_and_dir() of + # lib/WeBWorK/ContentGenerator.pm with changes for the XMLRPC on the setting. + # It depends on the $formLanguage and not a course setting. + ################################################# + + my $master_lang_setting = "lang=\"en-US\""; # default setting + my $master_dir_setting = ""; # default is NOT set + + if ( $formLanguage ne "en" ) { + # Attempt to override the defaults + if ( $formLanguage =~ /^he/i ) { # supports also the current "heb" option + # Hebrew - requires RTL direction + $master_lang_setting = "lang=\"he\""; # Hebrew + $master_dir_setting = " dir=\"rtl\""; # RTL + } elsif ( $formLanguage =~ /^ar/i ) { + # Arabic - requires RTL direction + $master_lang_setting = "lang=\"ar\""; # Arabic + $master_dir_setting = " dir=\"rtl\""; # RTL + } else { + # Use the $formLanguage without changing the text direction. + # Additional RTL languages should be added above, as needed. + $master_lang_setting = "lang=\"${formLanguage}\""; + } + } + + my $COURSE_LANG_AND_DIR = "${master_lang_setting}${master_dir_setting}"; + + ################################################# + # regular Perl warning messages generated with warn + ################################################# + + if ( defined ($rh_result->{WARNINGS}) and $rh_result->{WARNINGS} ){ + $warnings = "
+

WARNINGS

".decode_utf8_base64($rh_result->{WARNINGS})."

"; + } + #warn "keys: ", join(" | ", sort keys %{$rh_result }); + + ################################################# + # PG debug messages generated with DEBUG_message(); + ################################################# + + my $debug_messages = $rh_result->{debug_messages} || []; + $debug_messages = join("
\n", @{ $debug_messages } ); + + ################################################# + # PG warning messages generated with WARN_message(); + ################################################# + + my $PG_warning_messages = $rh_result->{warning_messages} || []; + $PG_warning_messages = join("
\n", @{ $PG_warning_messages } ); + + ################################################# + # internal debug messages generated within PG_core + # these are sometimes needed if the PG_core warning message system + # isn't properly set up before the bug occurs. + # In general don't use these unless necessary. + ################################################# + + my $internal_debug_messages = $rh_result->{internal_debug_messages} || []; + $internal_debug_messages = join("
\n", @{ $internal_debug_messages } ); + + my $fileName = $self->{input}->{envir}->{fileName} || ""; + + + ################################################# + + + $self->{outputformats}={}; + my $XML_URL = $self->url; + my $FORM_ACTION_URL = $self->{form_action_url}; + + ################################################# + # Local docker usage with a port number sometimes misbehaves if the port number + # is not forced into $XML_URL and $FORM_ACTION_URL + ################################################# + my $forcePortNumber = ($self->{inputs_ref}->{forcePortNumber})//''; + if ( $forcePortNumber =~ /^[0-9]+$/ ) { + $forcePortNumber = 0 + $forcePortNumber; + if ( ! ( $XML_URL =~ /:${forcePortNumber}/ ) ) { + $XML_URL .= ":${forcePortNumber}"; + } + if ( ! ( $FORM_ACTION_URL =~ m+:${forcePortNumber}/webwork2/html2xml+ ) ) { + $FORM_ACTION_URL =~ s+/webwork2/html2xml+:${forcePortNumber}/webwork2/html2xml+ ; # Ex: "http://localhost:8080/webwork2/html2xml" + } + } + + ################################################# + + my $courseID = $self->{courseID}; + my $userID = $self->{userID}; + my $course_password = $self->{course_password}; + my $problemSeed = $self->{inputs_ref}->{problemSeed}//4444; + my $psvn = $self->{inputs_ref}->{psvn}//54321; + my $session_key = $rh_result->{session_key}//''; + my $displayMode = $self->{inputs_ref}->{displayMode}; + + my $previewMode = defined($self->{inputs_ref}->{preview}); + my $checkMode = defined($self->{inputs_ref}->{WWcheck}); + my $submitMode = defined($self->{inputs_ref}->{WWsubmit}); + my $showCorrectMode = defined($self->{inputs_ref}->{WWcorrectAns}); + # problemUUID can be added to the request as a parameter. + # It adds a prefix to the + # identifier used by the format so that several different problems + # can appear on the same page. + my $problemUUID = $self->{inputs_ref}->{problemUUID}//0; + my $problemResult = $rh_result->{problem_result}//''; + my $problemState = $rh_result->{problem_state}//''; + my $showSummary = ($self->{inputs_ref}->{showSummary})//1; #default to show summary for the moment + + # $formLanguage moved above + #my $formLanguage = ($self->{inputs_ref}->{language})//'en'; + + my $scoreSummary = ''; + + + my $tbl = WeBWorK::Utils::AttemptsTable->new( + $rh_answers, + answersSubmitted => $self->{inputs_ref}->{answersSubmitted}//0, + answerOrder => $answerOrder//[], + displayMode => $self->{inputs_ref}->{displayMode}, + imgGen => $imgGen, + ce => '', #used only to build the imgGen + showAttemptPreviews => ($previewMode or $submitMode or $showCorrectMode), + showAttemptResults => ($submitMode or $showCorrectMode), + showCorrectAnswers => ($showCorrectMode), + showMessages => ($previewMode or $submitMode or $showCorrectMode), + showSummary => ( ($showSummary and ($submitMode or $showCorrectMode) )//0 )?1:0, + maketext => WeBWorK::Localize::getLoc($formLanguage//'en'), + summary => ($self->{problem_result}->{summary} )//'', # can be set by problem grader + ); + + + my $answerTemplate = $tbl->answerTemplate; + my $color_input_blanks_script = $tbl->color_answer_blanks; + $tbl->imgGen->render(refresh => 1) if $tbl->displayMode eq 'images'; + + # warn "imgGen is ", $tbl->imgGen; + #warn "answerOrder ", $tbl->answerOrder; + #warn "answersSubmitted ", $tbl->answersSubmitted; + # render equation images + + my $mt = WeBWorK::Localize::getLangHandle($formLanguage//'en'); + + if ($submitMode && $problemResult) { + my $ScoreMsg = $mt->maketext("You received a score of [_1] for this attempt.",wwRound(0, $problemResult->{score} * 100).'%'); + $scoreSummary = CGI::p($ScoreMsg); + if ($problemResult->{msg}) { + $scoreSummary .= CGI::p($problemResult->{msg}); + } + + my $notRecorded = $mt->maketext("Your score was not recorded."); + $scoreSummary .= CGI::p($notRecorded); + $scoreSummary .= CGI::hidden({id=>'problem-result-score', name=>'problem-result-score',value=>$problemResult->{score}}); + } + + ########################################################## + # Try to save the grade to an LTI if one provided us data + ########################################################## + + my $LTIGradeMessage = ''; + if (defined($self->{inputs_ref}->{lis_outcome_service_url}) && + defined($self->{inputs_ref}->{'oauth_consumer_key'}) && + defined($self->{inputs_ref}->{'oauth_signature_method'}) && + defined($self->{inputs_ref}->{'lis_result_sourcedid'}) && + defined($self->{seed_ce}->{'LISConsumerKeyHash'}->{$self->{inputs_ref}->{'oauth_consumer_key'}}) ) { + + my $request_url = $self->{inputs_ref}->{lis_outcome_service_url}; + my $consumer_key = $self->{inputs_ref}->{'oauth_consumer_key'}; + my $signature_method = $self->{inputs_ref}->{'oauth_signature_method'}; + my $sourcedid = $self->{inputs_ref}->{'lis_result_sourcedid'}; + my $consumer_secret = $self->{seed_ce}->{'LISConsumerKeyHash'}->{$consumer_key}; + my $score = $problemResult ? $problemResult->{score} : 0; + + # This is boilerplate XML used to submit the $score for $sourcedid + my $replaceResultXML = < + + + + V1.0 + 999999123 + + + + + + + $sourcedid + + + + en + $score + + + + + + +EOS + + my $bodyhash = sha1_base64($replaceResultXML); + + # since sha1_base64 doesn't pad we have to do so manually + while (length($bodyhash) % 4) { + $bodyhash .= '='; + } + + my $requestGen = Net::OAuth->request("consumer"); + + $requestGen->add_required_message_params('body_hash'); + + my $gradeRequest = $requestGen->new( + request_url => $request_url, + request_method => "POST", + consumer_secret => $consumer_secret, + consumer_key => $consumer_key, + signature_method => $signature_method, + nonce => int(rand( 2**32)), + timestamp => time(), + body_hash => $bodyhash + ); + $gradeRequest->sign(); + + my $HTTPRequest = HTTP::Request->new( + $gradeRequest->request_method, + $gradeRequest->request_url, + [ + 'Authorization' => $gradeRequest->to_authorization_header, + 'Content-Type' => 'application/xml', + ], + $replaceResultXML, + ); + + my $response = LWP::UserAgent->new->request($HTTPRequest); + + if ($response->is_success) { + $response->content =~ /\s*(\w+)\s*<\/imsx_codeMajor>/; + my $message = $1; + if ($message ne 'success') { + $LTIGradeMessage = CGI::p("Unable to update LMS grade. Error: ".$message); + $debug_messages .= CGI::escapeHTML($response->content); + } else { + $LTIGradeMessage = CGI::p("Grade sucessfully saved."); + } + } else { + $LTIGradeMessage = CGI::p("Unable to update LMS grade. Error: ".$response->message); + $debug_messages .= CGI::escapeHTML($response->content); + } + + # save parameters for next time + $LTIGradeMessage .= CGI::input({type=>'hidden', name=>'lis_outcome_service_url', value=>$request_url}); + $LTIGradeMessage .= CGI::input({type=>'hidden', name=>'oauth_consumer_key', value=>$consumer_key}); + $LTIGradeMessage .= CGI::input({type=>'hidden', name=>'oauth_signature_method', value=>$signature_method}); + $LTIGradeMessage .= CGI::input({type=>'hidden', name=>'lis_result_sourcedid', value=>$sourcedid}); + + } + + my $localStorageMessages = CGI::start_div({id=>'local-storage-messages'}); + $localStorageMessages.= CGI::p('Your overall score for this problem is'.' '.CGI::span({id=>'problem-overall-score'},'')); + $localStorageMessages .= CGI::end_div(); + + # my $pretty_print_self = pretty_print($self); + + # Enable localized strings for the buttons: + my $STRING_Preview = $mt->maketext("Preview My Answers"); + my $STRING_ShowCorrect = $mt->maketext("Show correct answers"); + my $STRING_Submit = $mt->maketext("Check Answers"); + +# With these values - things work, but the button text is English +# with the localized values, or any answers in UTF-8 - thing break +$STRING_Preview = "Preview My Answers"; +$STRING_ShowCorrect = "Show correct answers"; +$STRING_Submit = "Check Answers"; + +###################################################### +# Return interpolated problem template +###################################################### + + my $format_name = $self->{inputs_ref}->{outputformat}//'standard'; + + # The json output format is special and cannot be handled by the + # the standard code + if ( $format_name eq "json" ) { + my %output_data_hash; + my $key_value_pairs = do("WebworkClient/${format_name}_format.pl"); + my $key; + my $val; + while ( @$key_value_pairs ) { + $key = shift( @$key_value_pairs ); + $val = shift( @$key_value_pairs ); + if ( ( $key =~ /^hidden_input_field/ ) || + ( $key =~ /^real_webwork/ ) || + ( $key =~ /^internal/ ) || + ( $key =~ /_VI$/ ) + ) { + # interpolate values into $val + $val =~ s/(\$\w+)/$1/gee; + if ( $key =~ /_VI$/ ) { $key =~ s/_VI$//; } + } + $output_data_hash{$key} = $val; + } + # Add the current score to the %output_data_hash + my $json_score = 0; + if ( $submitMode && $problemResult ) { + $json_score = wwRound(0, $problemResult->{score} * 100); + } + $output_data_hash{score} = $json_score; + + my $json_output_data = to_json( \%output_data_hash ,{pretty=>1, canonical=>1}); + # FIXME: Should set header of response to content_type("text/json; charset=utf-8"); + return $json_output_data; + } + + + # find the appropriate template in WebworkClient folder + my $template = do("WebworkClient/${format_name}_format.pl"); + die "Unknown format name $format_name" unless $template; + # interpolate values into template + $template =~ s/(\$\w+)/$1/gee; + return $template; +>>>>>>> WeBWorK-2.15 +} + +=back + +=cut +###################################################### +# Utilities +###################################################### + + +=head2 Utility functions: + +=over 4 + +=item writeRenderLogEntry() + +# $ce - a WeBWork::CourseEnvironment object +# $function - fully qualified function name +# $details - any information, do not use the characters '[' or ']' +# $beginEnd - the string "begin", "intermediate", or "end" +# use the intermediate step begun or completed for INTERMEDIATE +# use an empty string for $details when calling for END +# Information printed in format: +# [formatted date & time ] processID unixTime BeginEnd $function $details + +=cut + +sub writeRenderLogEntry($$$) { + my ($function, $details, $beginEnd) = @_; + $beginEnd = ($beginEnd eq "begin") ? ">" : ($beginEnd eq "end") ? "<" : "-"; + WeBWorK::Utils::writeLog($seed_ce, "render_timing", "$$ ".time." $beginEnd $function [$details]"); +} + +=item pretty_print_self + +=cut + + +sub pretty_print { # provides html output -- NOT a method + my $r_input = shift; + my $level = shift; + $level = 4 unless defined($level); + $level--; + return '' unless $level > 0; # only print three levels of hashes (safety feature) + my $out = ''; + if ( not ref($r_input) ) { + $out = $r_input if defined $r_input; # not a reference + $out =~ s/"; + + + foreach my $key ( sort ( keys %$r_input )) { + # Safety feature - we do not want to display the contents of "%seed_ce" which + # contains the database password and lots of other things, and explicitly hide + # certain internals of the CourseEnvironment in case one slips in. + next if ( ( $key =~ /database/ ) || + ( $key =~ /dbLayout/ ) || + ( $key eq "ConfigValues" ) || + ( $key eq "ENV" ) || + ( $key eq "externalPrograms" ) || + ( $key eq "permissionLevels" ) || + ( $key eq "seed_ce" ) + ); + $out .= " $key=> ".pretty_print($r_input->{$key}) . ""; + } + $out .=""; + } elsif (ref($r_input) eq 'ARRAY' ) { + my @array = @$r_input; + $out .= "( " ; + while (@array) { + $out .= pretty_print(shift @array, $level) . " , "; + } + $out .= " )"; + } elsif (ref($r_input) eq 'CODE') { + $out = "$r_input"; + } else { + $out = $r_input; + $out =~ s/ Date: Sun, 6 Oct 2019 12:18:00 -0400 Subject: [PATCH 2/3] Remove webwork2/PG_VERSION file and references to it in the PG code. $ce->{PG_VERSION} is now obtained directly from the file pg/VERSION via a special exception which allows CourseEnvironment to read that file. (Normally CourseEnvironment only has access to files in webwork2. ) --- PG_VERSION | 5 ----- conf/defaults.config | 9 ++++----- lib/WeBWorK/CourseEnvironment.pm | 4 ++-- 3 files changed, 6 insertions(+), 12 deletions(-) delete mode 100644 PG_VERSION diff --git a/PG_VERSION b/PG_VERSION deleted file mode 100644 index ed4f5fa88f..0000000000 --- a/PG_VERSION +++ /dev/null @@ -1,5 +0,0 @@ -$PG_VERSION ='2.15'; #beta!!! -$PG_COPYRIGHT_YEARS = '1996-2019'; -# this file is not being used (I believe) -# it should be deleted. -1; diff --git a/conf/defaults.config b/conf/defaults.config index 291df1fcae..d648c76dff 100644 --- a/conf/defaults.config +++ b/conf/defaults.config @@ -29,11 +29,10 @@ # file, you can put a directive in localOverrides.conf. include("conf/site.conf"); -include("VERSION"); # get WW version -#include("PG_VERSION"); -# The version of PG is now obtained from pg/VERSION -# using code inside CourseEnvironment.pm -# include can only read files under the webwork2 directory +include("VERSION"); # get WW version +# The version of PG is now obtained from the file pg/VERSION +# using code added to CourseEnvironment.pm +# with this one exception include can only read files under the webwork2 directory ################################################################################ # site.conf should contain basic information about directories and URLs on diff --git a/lib/WeBWorK/CourseEnvironment.pm b/lib/WeBWorK/CourseEnvironment.pm index 858dc6df53..7022c0885a 100644 --- a/lib/WeBWorK/CourseEnvironment.pm +++ b/lib/WeBWorK/CourseEnvironment.pm @@ -226,10 +226,10 @@ sub new { } # # We'll get the pg version here and read it into the safe symbol table if (-r $PG_version_file){ - #print STDERR ( "\n\nread PG_version file $PG_version_file\n\n"); + #print STDERR ( "\n\nread PG_version file $PG_version_file\n\n"); my $PG_version_file_contents = readFile($PG_version_file)//''; $safe->reval($PG_version_file_contents); - #print STDERR ("\n contents: $PG_version_file_contents"); + #print STDERR ("\n contents: $PG_version_file_contents"); no strict 'refs'; my %symbolHash2 = %{$safe->root."::"}; From 45cc1532ae0f563ae82315e6842ce84cf4615100 Mon Sep 17 00:00:00 2001 From: Michael Gage <1203580+mgage@users.noreply.github.com> Date: Sun, 6 Oct 2019 12:20:53 -0400 Subject: [PATCH 3/3] Update VERSION file Remove !!beta --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ab044fde76..e312985291 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ -$WW_VERSION = '2.15'; # beta!!! +$WW_VERSION = '2.15'; $WW_COPYRIGHT_YEARS = '1996-2019'; 1;