Rev 711 | Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle 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 the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>.defined('MOODLE_INTERNAL') || die();// Quiz.require_once($CFG->dirroot . "/mod/quiz/renderer.php");class theme_universe_mod_quiz_renderer extends mod_quiz\output\renderer{/*** Outputs a box.** @param string $contents The contents of the box* @param string $classes A space-separated list of CSS classes* @param string $id An optional ID* @param array $attributes An array of other attributes to give the box.* @return string the HTML to output.*/public function space_box($contents, $classes = 'generalbox', $id = null, $attributes = array()){return $this->space_box_start($classes, $id, $attributes) . $contents . $this->space_box_end();}/*** Outputs the opening section of a box.** @param string $classes A space-separated list of CSS classes* @param string $id An optional ID* @param array $attributes An array of other attributes to give the box.* @return string the HTML to output.*/public function space_box_start($classes = 'generalbox', $id = null, $attributes = array()){$this->opencontainers->push('box', html_writer::end_tag('div'));$attributes['id'] = $id;$attributes['class'] = 'box ' . renderer_base::prepare_classes($classes);return html_writer::start_tag('div', $attributes);}/*** Outputs the closing section of a box.** @return string the HTML to output.*/public function space_box_end(){return $this->opencontainers->pop('box');}/*** Output the page information** @param object $quiz the quiz settings.* @param object $cm the course_module object.* @param context $context the quiz context.* @param array $messages any access messages that should be described.* @param bool $quizhasquestions does quiz has questions added.* @return string HTML to output.*/public function view_information($quiz, $cm, $context, $messages, bool $quizhasquestions = false){$output = '';// Output any access messages.if ($messages) {$output .= $this->box($this->access_messages($messages), 'rui-quizinfo quizinfo mt-3');}// Show number of attempts summary to those who can view reports.if (has_capability('mod/quiz:viewreports', $context)) {if ($strattemptnum = $this->quiz_attempt_summary_link_to_reports($quiz,$cm,$context)) {$output .= html_writer::tag('div',$strattemptnum,array('class' => 'rui-quizattemptcounts quizattemptcounts my-4'));}}if (has_any_capability(['mod/quiz:manageoverrides', 'mod/quiz:viewoverrides'], $context)) {if ($overrideinfo = $this->quiz_override_summary_links($quiz, $cm)) {$output .= html_writer::tag('div', $overrideinfo, ['class' => 'rui-quizattemptcounts quizattemptcounts my-4']);}}return $output;}/*** Generates the table of data** @param array $quiz Array contining quiz data* @param int $context The page context ID* @param mod_quiz_view_object $viewobj*/public function view_table($quiz, $context, $viewobj){if (!$viewobj->attempts) {return '';}// Prepare table header.$table = new html_table();$table->attributes['class'] = 'generaltable rui-quizattemptsummary mt-2 mb-0';$table->head = array();$table->align = array();$table->size = array();if ($viewobj->attemptcolumn) {$table->head[] = get_string('attemptnumber', 'quiz');$table->size[] = '';}$table->head[] = get_string('attemptstate', 'quiz');$table->align[] = 'left';$table->size[] = '';if ($viewobj->markcolumn) {$table->head[] = get_string('marks', 'quiz') . ' / ' .quiz_format_grade($quiz, $quiz->sumgrades);$table->size[] = '';}if ($viewobj->gradecolumn) {$table->head[] = get_string('grade', 'quiz') . ' / ' .quiz_format_grade($quiz, $quiz->grade);$table->size[] = '';}if ($viewobj->canreviewmine) {$table->head[] = get_string('review', 'quiz');$table->size[] = '';}if ($viewobj->feedbackcolumn) {$table->head[] = get_string('feedback', 'quiz');$table->align[] = 'left';$table->size[] = '';}// One row for each attempt.foreach ($viewobj->attemptobjs as $attemptobj) {$attemptoptions = $attemptobj->get_display_options(true);$row = array();// Add the attempt number.if ($viewobj->attemptcolumn) {if ($attemptobj->is_preview()) {$row[] = get_string('preview', 'quiz');} else {$row[] = $attemptobj->get_attempt_number();}}$row[] = $this->attempt_state($attemptobj);if ($viewobj->markcolumn) {if ($attemptoptions->marks >= question_display_options::MARK_AND_MAX &&$attemptobj->is_finished()) {$row[] = quiz_format_grade($quiz, $attemptobj->get_sum_marks());} else {$row[] = '';}}// Ouside the if because we may be showing feedback but not grades.$attemptgrade = quiz_rescale_grade($attemptobj->get_sum_marks(), $quiz, false);if ($viewobj->gradecolumn) {if ($attemptoptions->marks >= question_display_options::MARK_AND_MAX &&$attemptobj->is_finished()) {// Highlight the highest grade if appropriate.if ($viewobj->overallstats && !$attemptobj->is_preview()&& $viewobj->numattempts > 1 && !is_null($viewobj->mygrade)&& $attemptobj->get_state() == quiz_attempt::FINISHED&& $attemptgrade == $viewobj->mygrade&& $quiz->grademethod == QUIZ_GRADEHIGHEST) {$table->rowclasses[$attemptobj->get_attempt_number()] = 'bestrow';}$row[] = quiz_format_grade($quiz, $attemptgrade);} else {$row[] = '';}}if ($viewobj->canreviewmine) {$row[] = $viewobj->accessmanager->make_review_link($attemptobj->get_attempt(),$attemptoptions,$this);}if ($viewobj->feedbackcolumn && $attemptobj->is_finished()) {if ($attemptoptions->overallfeedback) {$row[] = quiz_feedback_for_grade($attemptgrade, $quiz, $context);} else {$row[] = '';}}if ($attemptobj->is_preview()) {$table->data['preview'] = $row;} else {$table->data[$attemptobj->get_attempt_number()] = $row;}} // End. of loop over attempts.$output = '';$output .= $this->view_table_heading();$output .= html_writer::start_tag('div', array('class' => 'table-overflow mb-4'));$output .= html_writer::table($table);$output .= html_writer::end_tag('div');return $output;}/** View Page*//*** Generates the view page** @param stdClass $course the course settings row from the database.* @param stdClass $quiz the quiz settings row from the database.* @param stdClass $cm the course_module settings row from the database.* @param context_module $context the quiz context.* @param mod_quiz_view_object $viewobj* @return string HTML to display*/public function view_page($course, $quiz, $cm, $context, $viewobj){$output = '';$output .= $this->view_page_tertiary_nav($viewobj);$output .= $this->view_information($quiz, $cm, $context, $viewobj->infomessages);$output .= $this->view_table($quiz, $context, $viewobj);$output .= $this->view_result_info($quiz, $context, $cm, $viewobj);$output .= $this->box($this->view_page_buttons($viewobj), 'rui-quizattempt');return $output;}/*** Outputs the table containing data from summary data array** @param array $summarydata contains row data for table* @param int $page contains the current page number*/public function review_summary_table($summarydata, $page){$summarydata = $this->filter_review_summary_table($summarydata, $page);if (empty($summarydata)) {return '';}$output = '';$output .= html_writer::start_tag('div', array('class' => 'rui-summary-table'));$output .= html_writer::start_tag('div', array('class' => 'rui-info-container rui-quizreviewsummary'));foreach ($summarydata as $rowdata => $val) {$csstitle = $rowdata;if ($val['title'] instanceof renderable) {$title = $this->render($val['title']);} else {$title = $val['title'];}if ($val['content'] instanceof renderable) {$content = $this->render($val['content']);} else {$content = $val['content'];}if ($val['title'] instanceof renderable) {$output .= html_writer::tag('div',html_writer::tag('h5', $title, array('class' => 'rui-infobox-title')) .html_writer::tag('div', $content, array('class' => 'rui-infobox-content--small')),array('class' => 'rui-infobox rui-infobox--avatar'));} else {$output .= html_writer::tag('div',html_writer::tag('h5', $title, array('class' => 'rui-infobox-title')) .html_writer::tag('div', $content, array('class' => 'rui-infobox-content--small')),array('class' => 'rui-infobox rui-infobox--' . strtolower(str_replace(' ', '', $csstitle))));}}$output .= html_writer::end_tag('div');$output .= html_writer::end_tag('div');return $output;}/*** Generates the table of summarydata** @param quiz_attempt $attemptobj* @param mod_quiz_display_options $displayoptions*/public function summary_table($attemptobj, $displayoptions){// Prepare the summary table header.$table = new html_table();$table->attributes['class'] = 'generaltable quizsummaryofattempt';$table->head = array(get_string('question', 'quiz'), get_string('status', 'quiz'));$table->align = array('left', 'left');$table->size = array('', '');$markscolumn = $displayoptions->marks >= question_display_options::MARK_AND_MAX;if ($markscolumn) {$table->head[] = get_string('marks', 'quiz');$table->align[] = 'left';$table->size[] = '';}$tablewidth = count($table->align);$table->data = array();// Get the summary info for each question.$slots = $attemptobj->get_slots();foreach ($slots as $slot) {// Add a section headings if we need one here.$heading = $attemptobj->get_heading_before_slot($slot);if ($heading !== null) {// There is a heading here.$rowclasses = 'quizsummaryheading';if ($heading) {$heading = format_string($heading);} else if (count($attemptobj->get_quizobj()->get_sections()) > 1) {// If this is the start of an unnamed section, and the quiz has more// than one section, then add a default heading.$heading = get_string('sectionnoname', 'quiz');$rowclasses .= ' dimmed_text';}$cell = new html_table_cell(format_string($heading));$cell->header = true;$cell->colspan = $tablewidth;$table->data[] = array($cell);$table->rowclasses[] = $rowclasses;}// Don't display information items.if (!$attemptobj->is_real_question($slot)) {continue;}$flag = '';// Real question, show it.if ($attemptobj->is_question_flagged($slot)) {// Quiz has custom JS manipulating these image tags - so we can't use the pix_icon method here.$flag = '<svg class="ml-2"width="20"height="20"viewBox="0 0 24 24"fill="none"xmlns="http://www.w3.org/2000/svg"><path d="M4.75 5.75V19.25"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"></path><path d="M4.75 15.25V5.75C4.75 5.75 6 4.75 9 4.75C12 4.75 13.5 6.25 16 6.25C18.56.25 19.25 5.75 19.25 5.75L15.75 10.5L19.25 15.25C19.25 15.25 18.5 16.25 1616.25C13.5 16.25 11.5 14.25 9 14.25C6.5 14.25 4.75 15.25 4.75 15.25Z"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"></path></svg>';}if ($attemptobj->can_navigate_to($slot)) {$row = array(html_writer::link($attemptobj->attempt_url($slot),$attemptobj->get_question_number($slot) . $flag),$attemptobj->get_question_status($slot, $displayoptions->correctness));} else {$row = array($attemptobj->get_question_number($slot) . $flag,$attemptobj->get_question_status($slot, $displayoptions->correctness));}if ($markscolumn) {$row[] = $attemptobj->get_question_mark($slot);}$table->data[] = $row;$table->rowclasses[] = 'quizsummary' . $slot . ' ' . $attemptobj->get_question_state_class($slot,$displayoptions->correctness);}// Print the summary table.$output = html_writer::table($table);return $output;}}require_once($CFG->dirroot . "/question/engine/renderer.php");class theme_universe_core_question_renderer extends core_question_renderer{/*** Generate the information bit of the question display that contains the* metadata like the question number, current state, and mark.* @param question_attempt $qa the question attempt to display.* @param qbehaviour_renderer $behaviouroutput the renderer to output the behaviour* specific parts.* @param qtype_renderer $qtoutput the renderer to output the question type* specific parts.* @param question_display_options $options controls what should and should not be displayed.* @param string|null $number The question number to display. 'i' is a special* value that gets displayed as Information. Null means no number is displayed.* @return HTML fragment.*/protected function info(question_attempt $qa,qbehaviour_renderer $behaviouroutput,qtype_renderer $qtoutput,question_display_options $options,$number) {$output = '';$output .= '<div class="d-flex align-items-center flex-wrap mb-sm-2 mb-md-0">' .$this->number($number) .'<div class="d-inline-flex align-items-center flex-wrap">' .$this->status($qa, $behaviouroutput, $options) .$this->mark_summary($qa, $behaviouroutput, $options) .'</div></div>';$output .= '<div>' .$this->question_flag($qa, $options->flags) .$this->edit_question_link($qa, $options) .'</div>';return $output;}/*** Generate the display of the question number.* @param string|null $number The question number to display. 'i' is a special* value that gets displayed as Information. Null means no number is displayed.* @return HTML fragment.*/protected function number($number){if (trim($number) === '') {return '';}$numbertext = '';if (trim($number) === 'i') {$numbertext = get_string('information', 'question');} else {$numbertext = get_string('questionx','question',html_writer::tag('span', $number, array('class' => 'rui-qno')));}return html_writer::tag('h4', $numbertext, array('class' => 'h3 w-100 mb-2'));}/*** Generate the display of the status line that gives the current state of* the question.* @param question_attempt $qa the question attempt to display.* @param qbehaviour_renderer $behaviouroutput the renderer to output the behaviour* specific parts.* @param question_display_options $options controls what should and should not be displayed.* @return HTML fragment.*/protected function status(question_attempt $qa,qbehaviour_renderer $behaviouroutput,question_display_options $options) {return html_writer::tag('div',$qa->get_state_string($options->correctness),array('class' => 'state mr-2'));}/*** Render the question flag, assuming $flagsoption allows it.** @param question_attempt $qa the question attempt to display.* @param int $flagsoption the option that says whether flags should be displayed.*/protected function question_flag(question_attempt $qa, $flagsoption){global $CFG;$divattributes = array('class' => 'questionflag mx-1 d-none');switch ($flagsoption) {case question_display_options::VISIBLE:$flagcontent = $this->get_flag_html($qa->is_flagged());break;case question_display_options::EDITABLE:$id = $qa->get_flag_field_name();$checkboxattributes = array('type' => 'checkbox','id' => $id . 'checkbox','name' => $id,'value' => 1,);if ($qa->is_flagged()) {$checkboxattributes['checked'] = 'checked';}$postdata = question_flags::get_postdata($qa);$flagcontent = html_writer::empty_tag('input',array('type' => 'hidden', 'name' => $id, 'value' => 0)) .html_writer::empty_tag('input', $checkboxattributes) .html_writer::empty_tag('input',array('type' => 'hidden', 'value' => $postdata, 'class' => 'questionflagpostdata')) .html_writer::tag('label',$this->get_flag_html($qa->is_flagged(), $id . 'img'),array('id' => $id . 'label', 'for' => $id . 'checkbox')) . "\n";$divattributes = array('class' => 'questionflag mb-sm-2 mb-md-0 mx-md-2 editable d-inline-flex','aria-atomic' => 'true','aria-relevant' => 'text','aria-live' => 'assertive',);break;default:$flagcontent = '';}return html_writer::nonempty_tag('div', $flagcontent, $divattributes);}protected function edit_question_link(question_attempt $qa, question_display_options $options){global $CFG;if (empty($options->editquestionparams)) {return '';}$params = $options->editquestionparams;if ($params['returnurl'] instanceof moodle_url) {$params['returnurl'] = $params['returnurl']->out_as_local_url(false);}$params['id'] = $qa->get_question_id();$editurl = new moodle_url('/question/bank/editquestion/question.php', $params);$icon = '<svg width="19" height="19" fill="none" viewBox="0 0 24 24"><path stroke="currentColor"stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M4.75 19.25L9 18.25L18.2929 8.95711C18.6834 8.56658 18.68347.93342 18.2929 7.54289L16.4571 5.70711C16.0666 5.31658 15.43345.31658 15.0429 5.70711L5.75 15L4.75 19.25Z"></path><path stroke="currentColor"stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M19.25 19.25H13.75"></path></svg>';return html_writer::link($editurl,$icon,array('class' => 'btn btn-icon btn-secondary editquestion line-height-1 ml-sm-2'));}}