diff --git a/bin/check_modules.pl b/bin/check_modules.pl index 7bb9a38bb7..6aef84329d 100755 --- a/bin/check_modules.pl +++ b/bin/check_modules.pl @@ -127,6 +127,7 @@ =head1 DESCRIPTION Opcode PadWalker Path::Class + Perl::Tidy PHP::Serialization Pod::Simple::Search Pod::Simple::XHTML @@ -160,6 +161,7 @@ =head1 DESCRIPTION 'LWP::Protocol::https' => 6.06, 'Mojolicious' => 9.22, 'Net::SSLeay' => 1.46, + 'Perl::Tidy' => 20220613 ); my ($test_programs, $test_modules, $show_help); diff --git a/conf/snippets/newProblem.pg b/conf/snippets/newProblem.pg index 3ab92489b4..75289789ee 100644 --- a/conf/snippets/newProblem.pg +++ b/conf/snippets/newProblem.pg @@ -14,9 +14,9 @@ DOCUMENT(); loadMacros( - "PGstandard.pl", # Standard macros for PG language - "PGML.pl", # PGML markup and Math Objects - "PGcourse.pl", # Customization file for the course + "PGstandard.pl", # Standard macros for PG language + "PGML.pl", # PGML markup and Math Objects + "PGcourse.pl", # Customization file for the course ); # Uncomment the following if you don't want to show which @@ -34,6 +34,6 @@ END_PGML BEGIN_PGML_SOLUTION You could type [|pi|]* or [|3.14|]*, or [|22/7|]*, among other options. -END_PGML_SOLUTION +END_PGML_SOLUTION ENDDOCUMENT(); diff --git a/htdocs/js/PGProblemEditor/pgproblemeditor.js b/htdocs/js/PGProblemEditor/pgproblemeditor.js index b0dedbf798..64a92a0ca3 100644 --- a/htdocs/js/PGProblemEditor/pgproblemeditor.js +++ b/htdocs/js/PGProblemEditor/pgproblemeditor.js @@ -98,7 +98,53 @@ ?.addEventListener('change', () => deleteBackupCheck.checked = true); } + // Send a request to the server to perltidy the current PG code in the CodeMirror editor. + const tidyPGCode = () => { + const request_object = { + user: document.getElementById('hidden_user')?.value, + courseID: document.getElementsByName('courseID')[0]?.value, + key: document.getElementById('hidden_key')?.value + }; + + request_object.rpc_command = 'tidyPGCode'; + request_object.pgCode = webworkConfig?.pgCodeMirror?.getValue() + ?? document.getElementById('problemContents')?.value ?? ''; + + fetch(webserviceURL, { method: 'post', mode: 'same-origin', body: new URLSearchParams(request_object) }) + .then((response) => response.json()) + .then((data) => { + if (data.result_data.status) { + if (data.result_data.errors) { + renderArea.innerHTML = '
' + + '

PG perltidy errors:

' + + '
' +
+							data.result_data.errors
+							.replace(/^[\s\S]*Begin Error Output Stream\n\n/, '')
+							.replace(/\n\d*: To save a full \.LOG file rerun with -g/, '') +
+							'
'; + } + showMessage('Errors occurred perltidying code.', false); + return; + } + if (request_object.pgCode === data.result_data.tidiedPGCode) { + showMessage('There were no changes to the code.', true); + } else { + if (webworkConfig?.pgCodeMirror) webworkConfig.pgCodeMirror.setValue(data.result_data.tidiedPGCode); + else document.getElementById('problemContents').value = data.result_data.tidiedPGCode; + saveTempFile(); + showMessage('Successfuly perltidied code.', true); + } + }) + .catch((err) => showMessage(`Error: ${err?.message ?? err}`)); + }; + document.getElementById('take_action')?.addEventListener('click', async (e) => { + if (document.getElementById('current_action')?.value === 'pgtidy') { + e.preventDefault(); + tidyPGCode(); + return; + } + const actionView = document.getElementById('view'); const editorForm = document.getElementById('editor'); diff --git a/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm b/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm index 8a8f2dc3a1..2600999e38 100644 --- a/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm +++ b/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm @@ -96,6 +96,7 @@ the submit button pressed (the action). Make this set header for: action = add_problem Revert: action = revert Generate Hardcopy: action = hardcopy + Tidy Code: action = pgtidy An undefined or invalid action is interpreted as an initial edit of the file. @@ -115,13 +116,14 @@ use WeBWorK::Utils::Instructor qw(assignProblemToAllSetUsers addProblemToSet); use constant DEFAULT_SEED => 123456; # Editor tabs -use constant ACTION_FORMS => [qw(view hardcopy save save_as add_problem revert)]; +use constant ACTION_FORMS => [qw(view hardcopy pgtidy save save_as add_problem revert)]; use constant ACTION_FORM_TITLES => { view => x('View/Reload'), hardcopy => x('Generate Hardcopy'), - add_problem => x('Append'), + pgtidy => x('Tidy Code'), save => x('Save'), save_as => x('Save As'), + add_problem => x('Append'), revert => x('Revert'), }; @@ -778,9 +780,10 @@ sub view_handler ($c) { return; } -# The hardcopy action is handled by javascript. This is provided just in case -# something goes wrong and the action gets called. +# The hardcopy and pgtidy actions are handled by javascript. These are provided just in case +# something goes wrong and the actions are called. sub hardcopy_action { } +sub pgtidy_action { } sub add_problem_handler ($c) { my $db = $c->db; diff --git a/lib/WeBWorK/CourseEnvironment.pm b/lib/WeBWorK/CourseEnvironment.pm index afe0c9d502..ac1efeaeee 100644 --- a/lib/WeBWorK/CourseEnvironment.pm +++ b/lib/WeBWorK/CourseEnvironment.pm @@ -101,6 +101,11 @@ sub new { my %ORIG_SIG; $ORIG_SIG{$_} = $SIG{$_} for keys %SIG; + # The following line is a work around for a bug that occurs on some systems. See + # https://rt.cpan.org/Public/Bug/Display.html?id=77916 and + # https://github.com/openwebwork/webwork2/pull/2098#issuecomment-1619812699. + %+; + my $safe = Safe->new; $safe->permit('rand'); # seed course environment with initial values diff --git a/lib/WebworkWebservice.pm b/lib/WebworkWebservice.pm index f5f162d968..d6e6f68a23 100644 --- a/lib/WebworkWebservice.pm +++ b/lib/WebworkWebservice.pm @@ -268,6 +268,7 @@ sub command_permission { putUserProblem => 'modify_student_data', putProblemVersion => 'modify_student_data', putPastAnswer => 'modify_student_data', + tidyPGCode => 'access_instructor_tools', # WebworkWebservice::RenderProblem renderProblem => 'proctor_quiz_login', diff --git a/lib/WebworkWebservice/ProblemActions.pm b/lib/WebworkWebservice/ProblemActions.pm index ae51dc1866..8f80e35782 100644 --- a/lib/WebworkWebservice/ProblemActions.pm +++ b/lib/WebworkWebservice/ProblemActions.pm @@ -21,13 +21,11 @@ use warnings; use Data::Structure::Util qw(unbless); -use WeBWorK::Debug; +use WeBWorK::PG::Tidy qw(pgtidy); sub getUserProblem { my ($invocant, $self, $params) = @_; - debug('in getUserProblem'); - my $db = $self->db; my $userProblem = $db->getUserProblem($params->{user_id}, $params->{set_id}, $params->{problem_id}); @@ -43,8 +41,6 @@ sub getUserProblem { sub putUserProblem { my ($invocant, $self, $params) = @_; - debug('in putUserProblem'); - my $db = $self->db; my $userProblem = $db->getUserProblem($params->{user_id}, $params->{set_id}, $params->{problem_id}); @@ -75,8 +71,6 @@ sub putUserProblem { sub putProblemVersion { my ($invocant, $self, $params) = @_; - debug('in putProblemVersion'); - my $db = $self->db; my $problemVersion = @@ -108,8 +102,6 @@ sub putProblemVersion { sub putPastAnswer { my ($invocant, $self, $params) = @_; - debug('in putPastAnswer'); - my $db = $self->db; my $pastAnswer = $db->getPastAnswer($params->{answer_id}); @@ -133,4 +125,21 @@ sub putPastAnswer { }; } +sub tidyPGCode { + my ($invocant, $self, $params) = @_; + + local @ARGV = (); + + my $code = $params->{pgCode}; + my $tidiedPGCode; + my $errors; + + my $result = pgtidy(source => \$code, destination => \$tidiedPGCode, errorfile => \$errors); + + return { + ra_out => { tidiedPGCode => $tidiedPGCode, status => $result, errors => $errors }, + text => 'Tidied code' + }; +} + 1; diff --git a/templates/ContentGenerator/Instructor/PGProblemEditor/pgtidy_form.html.ep b/templates/ContentGenerator/Instructor/PGProblemEditor/pgtidy_form.html.ep new file mode 100644 index 0000000000..c099b968d5 --- /dev/null +++ b/templates/ContentGenerator/Instructor/PGProblemEditor/pgtidy_form.html.ep @@ -0,0 +1,6 @@ +% # Hardcopy headers are previewed from the hardcopy generation tab. +% last if $c->{file_type} eq 'course_info'; +% +
+

<%= maketext('Reformat the code using perltidy.') =%>

+
diff --git a/templates/HelpFiles/InstructorPGProblemEditor.html.ep b/templates/HelpFiles/InstructorPGProblemEditor.html.ep index ebdf6d65dc..d7b5395cc6 100644 --- a/templates/HelpFiles/InstructorPGProblemEditor.html.ep +++ b/templates/HelpFiles/InstructorPGProblemEditor.html.ep @@ -120,6 +120,14 @@ . 'generating the PDF file using pdflatex.') =%> +
<%= maketext('Tidy Code') %>
+
+ <%= maketext('Reformat the code using perltidy. This will change the code in the editor window, and save ' + . 'changes to the temporary file. In some cases (if the code contains backslashes or double tildes) this ' + . 'can result in odd spacing in the code. So make sure to inspect the formatted code, and edit further or ' + . 'revert if needed.') =%> +
+
<%= maketext('Save') %>
<%= maketext('Save the contents of the editor window to the file on disk and re-render the problem. If '