From 754bd02d5400f5a8be00bb4bb1d397700ea98d1b Mon Sep 17 00:00:00 2001 From: "tai.letan" Date: Thu, 24 Oct 2024 12:01:22 +0700 Subject: [PATCH] Questionnaire: create PDF version for downloadable printed packs --- classes/output/renderer.php | 6 +- classes/question/date.php | 4 +- classes/question/drop.php | 4 +- classes/question/essay.php | 6 +- classes/question/question.php | 8 ++- classes/question/slider.php | 4 +- classes/question/text.php | 4 +- lang/en/questionnaire.php | 1 + questionnaire.class.php | 6 +- styles.css | 80 ++++++++++++++++++++++++++ templates/question_date.mustache | 16 ++++-- templates/question_drop.mustache | 21 +++++-- templates/question_slider.mustache | 2 +- templates/question_text.mustache | 2 +- tests/behat/view_questionnaire.feature | 75 ++++++++++++++++++++++++ 15 files changed, 215 insertions(+), 24 deletions(-) diff --git a/classes/output/renderer.php b/classes/output/renderer.php index 456370e3..7756a047 100755 --- a/classes/output/renderer.php +++ b/classes/output/renderer.php @@ -217,16 +217,18 @@ public function render_progress_bar($section, $questionsbysec) { /** * Render a question for a survey. + * * @param \mod_questionnaire\question\question $question The question object. * @param \mod_questionnaire\responsetype\response\response $response Any current response data. * @param int $qnum The question number. * @param boolean $blankquestionnaire Used for printing a blank one. * @param array $dependants Array of all questions/choices depending on $question. + * @param boolean $isprint if true, this content is in print blank page, vice versa. * @return string The output for the page. */ - public function question_output($question, $response, $qnum, $blankquestionnaire, $dependants=[]) { + public function question_output($question, $response, $qnum, $blankquestionnaire, $dependants=[], $isprint=false) { - $pagetags = $question->question_output($response, $blankquestionnaire, $dependants, $qnum); + $pagetags = $question->question_output($response, $blankquestionnaire, $dependants, $qnum, $isprint); // If the question has a template, then render it from the 'qformelement' context. If no template, then 'qformelement' // already contains HTML. diff --git a/classes/question/date.php b/classes/question/date.php index c564c34b..fb6d6ed4 100644 --- a/classes/question/date.php +++ b/classes/question/date.php @@ -63,9 +63,10 @@ public function response_template() { * @param \mod_questionnaire\responsetype\response\response $response * @param array $descendantsdata * @param boolean $blankquestionnaire + * @param boolean $isprint if true, this content is in print blank page, vice versa. * @return object The check question context tags. */ - protected function question_survey_display($response, $descendantsdata, $blankquestionnaire=false) { + protected function question_survey_display($response, $descendantsdata, $blankquestionnaire=false, $isprint=false) { // Date. $questiontags = new \stdClass(); if (!empty($response->answers[$this->id])) { @@ -83,6 +84,7 @@ protected function question_survey_display($response, $descendantsdata, $blankqu $choice->onkeypress = 'return event.keyCode != 13;'; $choice->name = 'q'.$this->id; $choice->value = (isset($response->answers[$this->id][0]->value) ? $response->answers[$this->id][0]->value : ''); + $choice->isprint = $isprint; $questiontags->qelements = new \stdClass(); $questiontags->qelements->choice = $choice; return $questiontags; diff --git a/classes/question/drop.php b/classes/question/drop.php index 17c49667..4c6e329c 100644 --- a/classes/question/drop.php +++ b/classes/question/drop.php @@ -89,10 +89,11 @@ public function supports_feedback() { * @param \mod_questionnaire\responsetype\response\response $response * @param array $dependants Array of all questions/choices depending on this question. * @param boolean $blankquestionnaire + * @param boolean $isprint if true, this content is in print blank page, vice versa. * @return object The check question context tags. * */ - protected function question_survey_display($response, $dependants, $blankquestionnaire=false) { + protected function question_survey_display($response, $dependants, $blankquestionnaire=false, $isprint=false) { // Drop. $options = []; @@ -116,6 +117,7 @@ protected function question_survey_display($response, $dependants, $blankquestio $chobj->id = self::qtypename($this->type_id) . $this->name; $chobj->class = 'select custom-select menu q'.$this->id; $chobj->options = $options; + $chobj->isprint = $isprint; $choicetags->qelements->choice = $chobj; return $choicetags; diff --git a/classes/question/essay.php b/classes/question/essay.php index 3e8591bc..8db193db 100644 --- a/classes/question/essay.php +++ b/classes/question/essay.php @@ -65,9 +65,10 @@ public function response_template() { * @param response $response * @param array $descendantsdata * @param bool $blankquestionnaire + * @param boolean $isprint if true, this content is in print blank page, vice versa. * */ - protected function question_survey_display($response, $descendantsdata, $blankquestionnaire=false) { + protected function question_survey_display($response, $descendantsdata, $blankquestionnaire=false, $isprint=false) { $output = ''; // Essay. @@ -89,13 +90,12 @@ protected function question_survey_display($response, $descendantsdata, $blankqu } else { $value = ''; } - if ($canusehtmleditor) { + if ($canusehtmleditor && !$isprint) { $editor = editors_get_preferred_editor(); $editor->use_editor($name, questionnaire_get_editor_options($this->context)); $texteditor = html_writer::tag('textarea', $value, ['id' => $name, 'name' => $name, 'rows' => $rows, 'cols' => $cols, 'class' => 'form-control']); } else { - $editor = FORMAT_PLAIN; $texteditor = html_writer::tag('textarea', $value, ['id' => $name, 'name' => $name, 'rows' => $rows, 'cols' => $cols]); } diff --git a/classes/question/question.php b/classes/question/question.php index 7b4594b5..f6403332 100644 --- a/classes/question/question.php +++ b/classes/question/question.php @@ -869,11 +869,15 @@ public function results_template($pdf = false) { * @param boolean $blankquestionnaire * @param array $dependants Array of all questions/choices depending on this question. * @param int $qnum + * @param boolean $isprint if true, this content is in print blank page, vice versa. * @return \stdClass */ - public function question_output($response, $blankquestionnaire, $dependants=[], $qnum='') { + public function question_output($response, $blankquestionnaire, $dependants=[], $qnum='', $isprint=false) { $pagetags = $this->questionstart_survey_display($qnum, $response); - $pagetags->qformelement = $this->question_survey_display($response, $dependants, $blankquestionnaire); + if ($isprint) { + $pagetags->isprint = true; + } + $pagetags->qformelement = $this->question_survey_display($response, $dependants, $blankquestionnaire, $isprint); return $pagetags; } diff --git a/classes/question/slider.php b/classes/question/slider.php index 005c6bab..93e96c27 100644 --- a/classes/question/slider.php +++ b/classes/question/slider.php @@ -106,10 +106,11 @@ public function get_feedback_maxscore() { * @param \mod_questionnaire\responsetype\response\response $response * @param array $dependants Array of all questions/choices depending on this question. * @param boolean $blankquestionnaire + * @param boolean $isprint if true, this content is in print blank page, vice versa. * @return object The check question context tags. * */ - protected function question_survey_display($response, $dependants = [], $blankquestionnaire = false) { + protected function question_survey_display($response, $dependants = [], $blankquestionnaire = false, $isprint=false) { global $PAGE; $PAGE->requires->js_init_call('M.mod_questionnaire.init_slider', null, false, questionnaire_get_js_module()); $extradata = json_decode($this->extradata); @@ -121,6 +122,7 @@ protected function question_survey_display($response, $dependants = [], $blankqu $extradata->id = self::qtypename($this->type_id) . $this->id; $questiontags->qelements = new \stdClass(); $questiontags->qelements->extradata = $extradata; + $questiontags->isprint = $isprint; return $questiontags; } diff --git a/classes/question/text.php b/classes/question/text.php index 3bd12661..a305d2ce 100644 --- a/classes/question/text.php +++ b/classes/question/text.php @@ -76,9 +76,10 @@ public function response_template() { * @param \stdClass $response * @param array $descendantsdata * @param bool $blankquestionnaire + * @param boolean $isprint if true, this content is in print blank page, vice versa. * */ - protected function question_survey_display($response, $descendantsdata, $blankquestionnaire=false) { + protected function question_survey_display($response, $descendantsdata, $blankquestionnaire=false, $isprint=false) { // Text Box. $questiontags = new \stdClass(); $questiontags->qelements = new \stdClass(); @@ -92,6 +93,7 @@ protected function question_survey_display($response, $descendantsdata, $blankqu $choice->value = (isset($response->answers[$this->id][0]) ? format_string(stripslashes($response->answers[$this->id][0]->value)) : ''); $choice->id = self::qtypename($this->type_id) . $this->id; + $choice->isprint = $isprint; $questiontags->qelements->choice = $choice; return $questiontags; } diff --git a/lang/en/questionnaire.php b/lang/en/questionnaire.php index 7de8d9f7..77a5ed65 100644 --- a/lang/en/questionnaire.php +++ b/lang/en/questionnaire.php @@ -421,6 +421,7 @@ $string['print'] = 'Print this Response'; $string['printblank'] = 'Print Blank'; $string['printblanktooltip'] = 'Opens printer-friendly window with blank Questionnaire'; +$string['printstrictdateformatting'] = 'Enter the date'; $string['printtooltip'] = 'Opens printer-friendly window with current Response'; $string['privacy:metadata:questionnaire_response'] = 'A response in progress or submitted'; diff --git a/questionnaire.class.php b/questionnaire.class.php index 004d4609..96dafa3b 100644 --- a/questionnaire.class.php +++ b/questionnaire.class.php @@ -1605,6 +1605,10 @@ public function survey_print_render($courseid, $message = '', $referer='', $rid= } $page = 1; + $isprint = false; + if ($referer === 'print') { + $isprint = true; + } foreach ($this->questionsbysec as $section) { $output = ''; if ($numsections > 1) { @@ -1621,7 +1625,7 @@ public function survey_print_render($courseid, $message = '', $referer='', $rid= $dependants = []; } $output .= $this->renderer->question_output($this->questions[$questionid], $this->responses[0] ?? [], - $i++, null, $dependants); + $i++, null, $dependants, $isprint); $this->page->add_to_page('questions', $output); $output = ''; } diff --git a/styles.css b/styles.css index 41dcd324..7cd9758a 100644 --- a/styles.css +++ b/styles.css @@ -170,6 +170,18 @@ td.selected { margin-top: 10px; } +#page-mod-questionnaire-print .printdropdown input:first-child, +#page-mod-questionnaire-print .printdropdown label:first-of-type, +#page-mod-questionnaire-print .printdropdown br:first-of-type { + display: none; +} + +#page-mod-questionnaire-print .print-text { + min-width: 100%; + box-sizing: border-box; + height: 30px; +} + #page-mod-questionnaire-complete .notice .buttons div, #page-mod-questionnaire-complete .notice .buttons form { display: inline; @@ -543,3 +555,71 @@ td.selected { height: 2px; left: 50%; } + +/* Styles for the print blank page. */ +#page-mod-questionnaire-print .slider { + position: relative; + width: 100%; +} + +#page-mod-questionnaire-print.path-mod-questionnaire .question-slider.print-slider { + margin-top: 20px; + margin-bottom: 20px; +} + +#page-mod-questionnaire-print.path-mod-questionnaire .print-slider .left-side-label, +#page-mod-questionnaire-print.path-mod-questionnaire .print-slider .right-side-label, +#page-mod-questionnaire-print.path-mod-questionnaire .print-slider .slider { + margin: 0; +} + +#page-mod-questionnaire-print input[type="range"] { + appearance: none; + height: 10px; + border-radius: 5px; + background-color: #ddd; +} + +#page-mod-questionnaire-print input[type="range"]::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 0; + height: 0; +} + +#page-mod-questionnaire-print input[type="range"]::-moz-range-thumb { + -webkit-appearance: none; + appearance: none; + width: 0; + height: 0; +} + +#page-mod-questionnaire-print input[type="range"]::-ms-thumb { + -webkit-appearance: none; + appearance: none; + width: 0; + height: 0; +} + +#page-mod-questionnaire-print .path-mod-questionnaire .right-side-label, +#page-mod-questionnaire-print .path-mod-questionnaire .left-side-label { + margin: 0; +} + +#page-mod-questionnaire-print .bubble { + display: none; +} + +@media print { + #page-mod-questionnaire-print .slider { + position: relative; + width: 100%; + } + + #page-mod-questionnaire-print input[type="range"] { + appearance: none; + width: 100%; + border: 1px solid #000000; + border-radius: 5px; + } +} diff --git a/templates/question_date.mustache b/templates/question_date.mustache index 412d9a0f..55e59b82 100644 --- a/templates/question_date.mustache +++ b/templates/question_date.mustache @@ -42,11 +42,17 @@ {{#qelements}} {{#choice}} -
{{# str }}strictdateformatting, mod_questionnaire{{/ str}}
-
- - -
+{{#isprint}} +
{{# str }}printstrictdateformatting, mod_questionnaire{{/ str}}
+ +{{/isprint}} +{{^isprint}} +
{{# str }}strictdateformatting, mod_questionnaire{{/ str}}
+
+ + +
+{{/isprint}} {{/choice}} {{/qelements}} \ No newline at end of file diff --git a/templates/question_drop.mustache b/templates/question_drop.mustache index 7d6cc34f..722c7050 100644 --- a/templates/question_drop.mustache +++ b/templates/question_drop.mustache @@ -54,11 +54,22 @@ {{#qelements}} {{#choice}} - + {{#isprint}} +
+ {{#choice.options}} + + +
+ {{/choice.options}} +
+ {{/isprint}} + {{^isprint}} + + {{/isprint}} {{/choice}} {{/qelements}} \ No newline at end of file diff --git a/templates/question_slider.mustache b/templates/question_slider.mustache index 3a5e123c..b441e67d 100644 --- a/templates/question_slider.mustache +++ b/templates/question_slider.mustache @@ -48,7 +48,7 @@ {{#qelements}} {{#extradata}} -
+
{{extradata.leftlabel}}
diff --git a/templates/question_text.mustache b/templates/question_text.mustache index 1efd82cc..d593dbea 100644 --- a/templates/question_text.mustache +++ b/templates/question_text.mustache @@ -44,7 +44,7 @@ {{#qelements}} {{#choice}} - + {{/choice}} {{/qelements}} \ No newline at end of file diff --git a/tests/behat/view_questionnaire.feature b/tests/behat/view_questionnaire.feature index 1ec5187d..bc2e8399 100644 --- a/tests/behat/view_questionnaire.feature +++ b/tests/behat/view_questionnaire.feature @@ -100,3 +100,78 @@ Feature: Questionnaires can be public, private or template And I log out And I am on the "Questionnaire from public" "mod_questionnaire > view" page logged in as "student1" Then I should see "This questionnaire is no longer available. Ask your teacher to delete it." + + @javascript @_switch_window + Scenario: Test view questions in print blank page. + Given the following "users" exist: + | username | firstname | lastname | email | + | manager1 | Manager | 1 | manager1@example.com | + | teacher1 | Teacher | 1 | teacher1@example.com | + | student1 | Student | 1 | student1@example.com | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + | Course 2 | C2 | 0 | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + | student1 | C1 | student | + | manager1 | C1 | manager | + | manager1 | C2 | manager | + | student1 | C2 | student | + And the following "activities" exist: + | activity | name | description | course | idnumber | + | questionnaire | Test questionnaire | Test questionnaire description | C1 | questionnaire0 | + And the following config values are set as admin: + | coursebinenable | 0 | tool_recyclebin | + And I log in as "manager1" + And I am on site homepage + And I am on "Course 1" course homepage + And I follow "Test questionnaire" + And I follow "Test questionnaire" + And I navigate to "Questions" in current page administration + And I add a "Date" question and I fill the form with: + | Question Name | Q1 | + | Yes | y | + | Question Text | Enter today's date | + And I add a "Dropdown Box" question and I fill the form with: + | Question Name | Q2 | + | Yes | y | + | Question Text | Select one choice | + | Possible answers | 1=One,2=Two,3=Three,4=Four | + And I add a "Essay Box" question and I fill the form with: + | Question Name | Q3 | + | No | n | + | Response format | 0 | + | Input box size | 10 lines | + | Question Text | Enter your essay | + And I add a "Slider" question and I fill the form with: + | Question Name | Q4 | + | Question Text | Slider question test | + | Left label | Left | + | Right label | Right | + | Centre label | Center | + | Minimum slider range (left) | 5 | + | Maximum slider range (right) | 100 | + | Slider starting value | 5 | + | Slider increment value | 5 | + And I add a "Text Box" question and I fill the form with: + | Question Name | Q5 | + | Yes | y | + | Question Text | Enter zero | + And I navigate to "Preview" in current page administration + And I click on "Print Blank" "link" + When I switch to a second window + # Check date picker question type. + Then "div.qn-datemsg + input[type='text']" "css_element" should exist + And "div.qn-datemsg + div > input[type='date']" "css_element" should not exist + # Check dropdown question type. + And "div.printdropdown input[type='checkbox']" "css_element" should exist + And "select.custom-select" "css_element" should not exist + # Check essay question type. + And ".qn-content textarea" "css_element" should exist + And ".qn-content .tox-tinymce" "css_element" should not exist + # Check slider question type. + And ".question-slider .slider .bubble" "css_element" should not be visible + # Check text box question type. + And ".print-text" "css_element" should exist