first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,346 @@
<?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/>.
namespace mod_quiz\output;
use action_link;
use core\output\named_templatable;
use html_writer;
use mod_quiz\grade_calculator;
use mod_quiz\output\grades\grade_out_of;
use mod_quiz\quiz_attempt;
use moodle_url;
use mod_quiz\question\display_options;
use question_display_options;
use renderable;
use renderer_base;
use stdClass;
use user_picture;
/**
* A summary of a single quiz attempt for rendering.
*
* This is used in places like
* - at the top of the review attempt page (review.php)
* - at the top of the review single question page (reviewquestion.php)
* - on the quiz entry page (view.php).
*
* @package mod_quiz
* @copyright 2024 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class attempt_summary_information implements renderable, named_templatable {
/** @var array[] The rows of summary data. {@see add_item()} should make the structure clear. */
protected array $summarydata = [];
/**
* Add an item to the summary.
*
* @param string $shortname unique identifier of this item (not displayed).
* @param string|renderable $title the title of this item.
* @param string|renderable $content the content of this item.
*/
public function add_item(string $shortname, string|renderable $title, string|renderable $content): void {
$this->summarydata[$shortname] = [
'title' => $title,
'content' => $content,
];
}
/**
* Add an item to the summary just before the given item.
*
* If that item is not present, then add as the first item.
*
* @param string $shortname unique identifier of this item (not displayed).
* @param string|renderable $title the title of this item.
* @param string|renderable $content the content of this item.
* @param string $addbefore identifier of the other item to add this before.
*/
public function add_item_before(
string $shortname,
string|renderable $title,
string|renderable $content,
string $addbefore,
): void {
$position = array_search($addbefore, array_keys($this->summarydata));
if ($position !== false) {
$this->insert_new_item_at_position($shortname, $title, $content, $position);
} else {
$this->insert_new_item_at_position($shortname, $title, $content, 0);
}
}
/**
* Add an item to the summary just after the given item.
*
* If that item is not present, then just add at the end.
*
* @param string $shortname unique identifier of this item (not displayed).
* @param string|renderable $title the title of this item.
* @param string|renderable $content the content of this item.
* @param string $addafter identifier of the other item to add this before.
*/
public function add_item_after(
string $shortname,
string|renderable $title,
string|renderable $content,
string $addafter,
): void {
$position = array_search($addafter, array_keys($this->summarydata));
if ($position !== false) {
$this->insert_new_item_at_position($shortname, $title, $content, $position + 1);
} else {
$this->add_item($shortname, $title, $content);
}
}
/**
* Add an item to the summary just before the given position.
*
* @param string $shortname unique identifier of this item (not displayed).
* @param string|renderable $title the title of this item.
* @param string|renderable $content the content of this item.
* @param int $position Numerical position to insert the item at. 0 means first.
*/
protected function insert_new_item_at_position(
string $shortname,
string|renderable $title,
string|renderable $content,
int $position,
) {
$this->summarydata = array_merge(
array_slice($this->summarydata, 0, $position),
[$shortname => ['title' => $title, 'content' => $content]],
array_slice($this->summarydata, $position, count($this->summarydata)),
);
}
/**
* Remove an item, if present.
*
* @param string $shortname
*/
public function remove_item(string $shortname): void {
unset($this->summarydata[$shortname]);
}
/**
* Filter the data held, to keep only the information with the given shortnames.
*
* @param array $shortnames items to keep.
*/
public function filter_keeping_only(array $shortnames): void {
foreach ($this->summarydata as $shortname => $rowdata) {
if (!in_array($shortname, $shortnames)) {
unset($this->summarydata[$shortname]);
}
}
}
/**
* To aid conversion of old code. This converts the old array format into an instance of this class.
*
* @param array $items array of $shortname => [$title, $content].
* @return static
*/
public static function create_from_legacy_array(array $items): static {
$summary = new static();
foreach ($items as $shortname => $item) {
$summary->add_item($shortname, $item['title'], $item['content']);
}
return $summary;
}
/**
* Initialise an instance of this class for a particular quiz attempt.
*
* @param quiz_attempt $attemptobj the attempt to summarise.
* @param display_options $options options for what can be seen.
* @param int|null $pageforlinkingtootherattempts if null, no links to other attempsts will be created.
* If specified, the URL of this particular page of the attempt, otherwise
* the URL will go to the first page. If -1, deduce $page from $slot.
* @param bool|null $showall if true, the URL will be to review the entire attempt on one page,
* and $page will be ignored. If null, a sensible default will be chosen.
* @return self summary information.
*/
public static function create_for_attempt(
quiz_attempt $attemptobj,
display_options $options,
?int $pageforlinkingtootherattempts = null,
?bool $showall = null,
): static {
global $DB, $USER;
$summary = new static();
// Prepare summary information about the whole attempt.
if (!$attemptobj->get_quiz()->showuserpicture && $attemptobj->get_userid() != $USER->id) {
// If showuserpicture is true, the picture is shown elsewhere, so don't repeat it.
$student = $DB->get_record('user', ['id' => $attemptobj->get_userid()]);
$userpicture = new user_picture($student);
$userpicture->courseid = $attemptobj->get_courseid();
$summary->add_item('user', $userpicture,
new action_link(
new moodle_url('/user/view.php', ['id' => $student->id, 'course' => $attemptobj->get_courseid()]),
fullname($student, true),
)
);
}
if ($pageforlinkingtootherattempts !== null && $attemptobj->has_capability('mod/quiz:viewreports')) {
$attemptlist = $attemptobj->links_to_other_attempts(
$attemptobj->review_url(null, $pageforlinkingtootherattempts, $showall));
if ($attemptlist) {
$summary->add_item('attemptlist', get_string('attempts', 'quiz'), $attemptlist);
}
}
// Attempt state.
$summary->add_item('state', get_string('attemptstate', 'quiz'),
quiz_attempt::state_name($attemptobj->get_attempt()->state));
// Timing information.
$attempt = $attemptobj->get_attempt();
$quiz = $attemptobj->get_quiz();
$overtime = 0;
if ($attempt->state == quiz_attempt::FINISHED) {
if ($timetaken = ($attempt->timefinish - $attempt->timestart)) {
if ($quiz->timelimit && $timetaken > ($quiz->timelimit + 60)) {
$overtime = $timetaken - $quiz->timelimit;
$overtime = format_time($overtime);
}
$timetaken = format_time($timetaken);
} else {
$timetaken = "-";
}
} else {
$timetaken = get_string('unfinished', 'quiz');
}
$summary->add_item('startedon', get_string('startedon', 'quiz'), userdate($attempt->timestart));
if ($attempt->state == quiz_attempt::FINISHED) {
$summary->add_item('completedon', get_string('completedon', 'quiz'),
userdate($attempt->timefinish));
$summary->add_item('timetaken', get_string('attemptduration', 'quiz'), $timetaken);
}
if (!empty($overtime)) {
$summary->add_item('overdue', get_string('overdue', 'quiz'), $overtime);
}
// Show marks (if the user is allowed to see marks at the moment).
$grade = $summary->add_attempt_grades_if_appropriate($attemptobj, $options);
// Any additional summary data from the behaviour.
foreach ($attemptobj->get_additional_summary_data($options) as $shortname => $data) {
$summary->add_item($shortname, $data['title'], $data['content']);
}
// Feedback if there is any, and the user is allowed to see it now.
$feedback = $attemptobj->get_overall_feedback($grade);
if ($options->overallfeedback && $feedback) {
$summary->add_item('feedback', get_string('feedback', 'quiz'), $feedback);
}
return $summary;
}
/**
* Add the grade information to this summary information.
*
* This is a helper used by {@see create_for_attempt()}.
*
* @param quiz_attempt $attemptobj the attempt to summarise.
* @param display_options $options options for what can be seen.
* @return float|null the overall attempt grade, if it exists, else null. Raw value, not formatted.
*/
public function add_attempt_grades_if_appropriate(
quiz_attempt $attemptobj,
display_options $options,
): ?float {
$quiz = $attemptobj->get_quiz();
$grade = quiz_rescale_grade($attemptobj->get_sum_marks(), $quiz, false);
if ($options->marks < question_display_options::MARK_AND_MAX) {
// User can't see grades.
return $grade;
}
if (!quiz_has_grades($quiz) || $attemptobj->get_state() != quiz_attempt::FINISHED) {
// No grades to show.
return $grade;
}
if (is_null($grade)) {
// Attempt needs ot be graded.
$this->add_item('grade', get_string('gradenoun'), quiz_format_grade($quiz, $grade));
return $grade;
}
// Grades for extra grade items, if any.
foreach ($attemptobj->get_grade_item_totals() as $gradeitemid => $gradeoutof) {
$this->add_item('marks' . $gradeitemid, format_string($gradeoutof->name), $gradeoutof);
}
// Show raw marks only if they are different from the grade.
if ($quiz->grade != $quiz->sumgrades) {
$this->add_item('marks', get_string('marks', 'quiz'),
new grade_out_of($quiz, $attemptobj->get_sum_marks(), $quiz->sumgrades, style: grade_out_of::SHORT));
}
// Now the scaled grade.
$this->add_item('grade', get_string('gradenoun'),
new grade_out_of($quiz, $grade, $quiz->grade,
style: abs($quiz->grade - 100) < grade_calculator::ALMOST_ZERO ?
grade_out_of::NORMAL : grade_out_of::WITH_PERCENT));
return $grade;
}
public function export_for_template(renderer_base $output): array {
$templatecontext = [
'hasitems' => !empty($this->summarydata),
'items' => [],
];
foreach ($this->summarydata as $item) {
if ($item['title'] instanceof renderable) {
$title = $output->render($item['title']);
} else {
$title = $item['title'];
}
if ($item['content'] instanceof renderable) {
$content = $output->render($item['content']);
} else {
$content = $item['content'];
}
$templatecontext['items'][] = (object) ['title' => $title, 'content' => $content];
}
return $templatecontext;
}
public function get_template_name(renderer_base $renderer): string {
// Only reason we are forced to implement this is that we want the quiz renderer
// passed to export_for_template, not a core_renderer.
return 'mod_quiz/attempt_summary_information';
}
}
@@ -0,0 +1,123 @@
<?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/>.
namespace mod_quiz\output;
use mod_quiz\structure;
use renderable;
use renderer_base;
use templatable;
/**
* Represents the page where teachers can set up additional grade items.
*
* @package mod_quiz
* @category output
* @copyright 2023 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class edit_grading_page implements renderable, templatable {
/**
* Constructor.
*
* @param structure $structure
*/
public function __construct(
/** @var structure structure of the quiz for which to display the grade edit page. */
protected readonly structure $structure,
) {
}
public function export_for_template(renderer_base $output) {
global $PAGE;
/** @var edit_renderer $editrenderer */
$editrenderer = $PAGE->get_renderer('mod_quiz', 'edit');
// Get the list of grade items, but to be the choices for a slot, and the list to be edited.
$gradeitemchoices = [
0 => [
'id' => 0,
'choice' => get_string('gradeitemnoneselected', 'quiz'),
'isselected' => false,
],
];
$selectdgradeitemchoices = [];
$gradeitems = [];
foreach ($this->structure->get_grade_items() as $gradeitem) {
$gradeitem = clone($gradeitem);
unset($gradeitem->quizid);
$gradeitem->displayname = format_string($gradeitem->name);
$gradeitem->isused = $this->structure->is_grade_item_used($gradeitem->id);
$gradeitem->summarks = $gradeitem->isused ?
$this->structure->formatted_grade_item_sum_marks($gradeitem->id) :
'-';
$gradeitems[] = $gradeitem;
$gradeitemchoices[$gradeitem->id] = (object) [
'id' => $gradeitem->id,
'choice' => $gradeitem->displayname,
'isselected' => false,
];
$selectdgradeitemchoices[$gradeitem->id] = clone($gradeitemchoices[$gradeitem->id]);
$selectdgradeitemchoices[$gradeitem->id]->isselected = true;
}
// Get the list of quiz sections.
$sections = [];
foreach ($this->structure->get_sections() as $section) {
$sections[$section->id] = (object) [
'displayname' => $section->heading ? format_string($section->heading) : get_string('sectionnoname', 'quiz'),
'slots' => [],
];
}
// Add the relevant slots ot each section.
foreach ($this->structure->get_slots() as $slot) {
if (!$this->structure->is_real_question($slot->slot)) {
continue;
}
// Mark the right choice as selected.
$choices = $gradeitemchoices;
if ($slot->quizgradeitemid) {
$choices[$slot->quizgradeitemid] = $selectdgradeitemchoices[$slot->quizgradeitemid];
}
$sections[$slot->section->id]->slots[] = (object) [
'id' => $slot->id,
'displaynumber' => $this->structure->get_displayed_number_for_slot($slot->slot),
'displayname' => $editrenderer->get_question_name_for_slot(
$this->structure, $slot->slot, $PAGE->url),
'maxmark' => $this->structure->formatted_question_grade($slot->slot),
'choices' => array_values($choices),
];
}
return [
'quizid' => $this->structure->get_quizid(),
'hasgradeitems' => !empty($gradeitems),
'gradeitems' => $gradeitems,
'hasslots' => $this->structure->has_questions(),
'sections' => array_values($sections),
'hasmultiplesections' => count($sections) > 1,
'nogradeitems' => ['message' => get_string('gradeitemsnoneyet', 'quiz')],
'noslots' => ['message' => get_string('gradeitemnoslots', 'quiz')],
];
}
}
@@ -0,0 +1,78 @@
<?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/>.
namespace mod_quiz\output;
use moodle_url;
use renderable;
use renderer_base;
use templatable;
use url_select;
/**
* Represents the tertiary navigation around the quiz edit pages.
*
* @package mod_quiz
* @copyright 2023 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class edit_nav_actions implements renderable, templatable {
/** @var string option for $whichpage argument to the constructor. */
const SUMMARY = 'summary';
/** @var string option for $whichpage argument to the constructor. */
const GRADING = 'grading';
/**
* overrides_action constructor.
*
* @param int $cmid The course module id.
* @param string $whichpage self::SUMMARY (edit.php) or self::GRADING (editgrading.php).
*/
public function __construct(
/** @var int The course module ID. */
protected readonly int $cmid,
/** @var string which page this is. Either self::SUMMARY (edit.php) or self::GRADING (editgrading.php). */
protected readonly string $whichpage,
) {
}
public function export_for_template(renderer_base $output): array {
// Build the navigation drop-down.
$questionsurl = new moodle_url('/mod/quiz/edit.php', ['cmid' => $this->cmid]);
$gradeitemsetupurl = new moodle_url('/mod/quiz/editgrading.php', ['cmid' => $this->cmid]);
$menu = [
$questionsurl->out(false) => get_string('questions', 'quiz'),
$gradeitemsetupurl->out(false) => get_string('gradeitemsetup', 'quiz'),
];
$overridesnav = new url_select(
$menu,
$this->whichpage === self::SUMMARY ? $questionsurl->out(false) : $gradeitemsetupurl->out(false),
null
);
$overridesnav->set_label(get_string('quizsetupnavigation', 'quiz'), ['class' => 'sr-only']);
return [
'navmenu' => $overridesnav->export_for_template($output),
];
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,112 @@
<?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/>.
namespace mod_quiz\output\grades;
use html_writer;
use renderable;
use stdClass;
/**
* Represents a grade out of a give total, that wants to be output in a particular way.
*
* @package mod_quiz
* @category output
* @copyright 2024 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grade_out_of implements renderable {
/** @var string Indicates we want a short rendering. Also the lang string used. */
const SHORT = 'outofshort';
/** @var string Indicates we want the default rendering. Also the lang string used. */
const NORMAL = 'outof';
/** @var string like normal, but with the percent equivalent in brackets. Also the lang string used */
const WITH_PERCENT = 'outofpercent';
/**
* Constructor.
*
* @param stdClass $quiz Quiz settings.
* @param float $grade the mark to show.
* @param float $maxgrade the total to show it out of.
* @param string|null $name optional, a name for what this grade is.
* @param string $style which format to use, grade_out_of::NORMAL (default), ::SHORT or ::WITH_PERCENT.
*/
public function __construct(
/** @var stdClass Quiz settings (so we can access the settings like decimal places). */
public readonly stdClass $quiz,
/** @var float the grade to show. */
public float $grade,
/** @var float the total the grade is out of. */
public float $maxgrade,
/** @var string|null optional, a name for what this grade is. Must be output via format_string. */
public readonly ?string $name = null,
/** @var string The display style, one of the consts above. */
public readonly string $style = self::NORMAL,
) {
}
/**
* Get the lang string to use to display the grade in the requested style.
*
* @return string lang string key from the mod_quiz lang pack.
*/
public function get_string_key(): string {
return $this->style;
}
/**
* Get the formatted values to be inserted into the {@see get_string_key()} string placeholders.
*
* Values are not styled. To apply the recommended styling, call {@see style_formatted_values()}
*
* @return stdClass to be passed as the third argument to get_string().
*/
public function get_formatted_values(): stdClass {
$a = new stdClass();
$a->grade = quiz_format_grade($this->quiz, $this->grade);
$a->maxgrade = quiz_format_grade($this->quiz, $this->maxgrade);
if ($this->style === self::WITH_PERCENT) {
$a->percent = format_float($this->grade * 100 / $this->maxgrade,
$this->quiz->decimalpoints, true, true);
}
return $a;
}
/**
* Apply the normal styling to the values returned by {@see get_formatted_values()}.
*
* @param stdClass $a formatted values, as returned by get_formatted_values.
* @return stdClass same structure, with some values wrapped in &lt;b> tags.
*/
public function style_formatted_values(stdClass $a): stdClass {
if ($this->style !== self::SHORT) {
$a->grade = html_writer::tag('b', $a->grade);
}
if ($this->style === self::WITH_PERCENT) {
$a->percent = html_writer::tag('b', $a->percent);
}
return $a;
}
}
@@ -0,0 +1,38 @@
<?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/>.
namespace mod_quiz\output;
use renderable;
/**
* Represents the list of links to other attempts
*
* @package mod_quiz
* @category output
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class links_to_other_attempts implements renderable {
/**
* @var array The list of links. string attempt number => one of three things:
* - null if this is the current attempt, and so should not be linked. (Just the number is output.)
* - moodle_url if this is a different attempt. (Output as a link to the URL with the number as link text.)
* - a renderable, in which case the results of rendering the renderable is output.
* (The third option is used by {@see quiz_attempt::links_to_other_redos()}.)
*/
public $links = [];
}
@@ -0,0 +1,85 @@
<?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/>.
namespace mod_quiz\output;
use core\output\named_templatable;
use mod_quiz\quiz_attempt;
use renderable;
use renderer_base;
/**
* Display summary information about a list of attempts.
*
* This is used on the front page of the quiz (view.php).
*
* @package mod_quiz
* @copyright 2024 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class list_of_attempts implements renderable, named_templatable {
/** @var int time to consider as now. */
protected int $timenow;
/** @var quiz_attempt[] The list of attempts to summarise. */
protected array $attempts = [];
/**
* Constructor.
*
* @param int $timenow time that is now.
*/
public function __construct(int $timenow) {
$this->timenow = $timenow;
}
/**
* Add an event to the list.
*
* @param quiz_attempt $attemptobj
*/
public function add_attempt(quiz_attempt $attemptobj): void {
$this->attempts[] = $attemptobj;
}
public function export_for_template(renderer_base $output): array {
$templatecontext = [
'hasattempts' => !empty($this->attempts),
'attempts' => [],
];
foreach ($this->attempts as $attemptobj) {
$displayoptions = $attemptobj->get_display_options(true);
$templatecontext['attempts'][] = (object) [
'name' => get_string('attempt', 'mod_quiz', $attemptobj->get_attempt_number()),
'summarydata' => attempt_summary_information::create_for_attempt(
$attemptobj, $displayoptions)->export_for_template($output),
'reviewlink' => $attemptobj->get_access_manager($this->timenow)->make_review_link(
$attemptobj->get_attempt(), $displayoptions, $output),
];
}
return $templatecontext;
}
public function get_template_name(\renderer_base $renderer): string {
// Only reason we are forced to implement this is that we want the quiz renderer
// passed to export_for_template, not a core_renderer.
return 'mod_quiz/list_of_attempts';
}
}
@@ -0,0 +1,62 @@
<?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/>.
namespace mod_quiz\output;
use html_writer;
/**
* Specialisation of {@see navigation_panel_base} for the attempt quiz page.
*
* This class is not currently renderable or templatable, but it probably should be in the future,
* which is why it is already in the output namespace.
*
* @package mod_quiz
* @category output
* @copyright 2008 Tim Hunt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class navigation_panel_attempt extends navigation_panel_base {
public function get_question_url($slot) {
if ($this->attemptobj->can_navigate_to($slot)) {
return $this->attemptobj->attempt_url($slot, -1, $this->page);
} else {
return null;
}
}
public function render_before_button_bits(renderer $output) {
return html_writer::tag('div', get_string('navnojswarning', 'quiz'),
['id' => 'quiznojswarning']);
}
public function render_end_bits(renderer $output) {
if ($this->page == -1) {
// Don't link from the summary page to itself.
return '';
}
// We create a hidden div with an information message in order for the student
// to known when their answers have been auto-saved.
$html = html_writer::div(get_string('lastautosave', 'quiz', '-'), 'autosave_info', ['hidden' => 'hidden']);
$html .= html_writer::link($this->attemptobj->summary_url(),
get_string('endtest', 'quiz'), ['class' => 'endtestlink aalink']) .
$this->render_restart_preview_link($output);
return $html;
}
}
@@ -0,0 +1,201 @@
<?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/>.
namespace mod_quiz\output;
use mod_quiz\quiz_attempt;
use moodle_url;
use question_attempt;
use question_display_options;
use question_state;
use renderable;
use user_picture;
/**
* Represents the navigation panel, and builds a {@see block_contents} to allow it to be output.
*
* This class is not currently renderable or templatable, but it probably should be in the future,
* which is why it is already in the output namespace.
*
* @package mod_quiz
* @category output
* @copyright 2008 Tim Hunt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class navigation_panel_base {
/** @var quiz_attempt */
protected $attemptobj;
/** @var question_display_options */
protected $options;
/** @var integer */
protected $page;
/** @var boolean */
protected $showall;
/**
* Constructor.
*
* @param quiz_attempt $attemptobj construct the panel for this attempt.
* @param question_display_options $options display options in force.
* @param int $page which page of the quiz attempt is being shown, -1 if all.
* @param bool $showall whether all pages are being shown at once.
*/
public function __construct(quiz_attempt $attemptobj,
question_display_options $options, $page, $showall) {
$this->attemptobj = $attemptobj;
$this->options = $options;
$this->page = $page;
$this->showall = $showall;
}
/**
* Get the buttons and section headings to go in the quiz navigation block.
*
* @return renderable[] the buttons, possibly interleaved with section headings.
*/
public function get_question_buttons() {
$buttons = [];
foreach ($this->attemptobj->get_slots() as $slot) {
$heading = $this->attemptobj->get_heading_before_slot($slot);
if (!is_null($heading)) {
$sections = $this->attemptobj->get_quizobj()->get_sections();
if (!(empty($heading) && count($sections) == 1)) {
$buttons[] = new navigation_section_heading(format_string($heading));
}
}
$qa = $this->attemptobj->get_question_attempt($slot);
$showcorrectness = $this->options->correctness && $qa->has_marks();
$button = new navigation_question_button();
$button->id = 'quiznavbutton' . $slot;
$button->isrealquestion = $this->attemptobj->is_real_question($slot);
$button->number = $this->attemptobj->get_question_number($slot);
$button->stateclass = $qa->get_state_class($showcorrectness);
$button->navmethod = $this->attemptobj->get_navigation_method();
if (!$showcorrectness && $button->stateclass === 'notanswered') {
$button->stateclass = 'complete';
}
$button->statestring = $this->get_state_string($qa, $showcorrectness);
$button->page = $this->attemptobj->get_question_page($slot);
$button->currentpage = $this->showall || $button->page == $this->page;
$button->flagged = $qa->is_flagged();
$button->url = $this->get_question_url($slot);
if ($this->attemptobj->is_blocked_by_previous_question($slot)) {
$button->url = null;
$button->stateclass = 'blocked';
$button->statestring = get_string('questiondependsonprevious', 'quiz');
}
$buttons[] = $button;
}
return $buttons;
}
/**
* Get the human-readable description of the current state of a particular question.
*
* @param question_attempt $qa the attempt at the question of interest.
* @param bool $showcorrectness whether the current use is allowed to see if they have got the question right.
* @return string Human-readable description of the state.
*/
protected function get_state_string(question_attempt $qa, $showcorrectness) {
if ($qa->get_question(false)->length > 0) {
return $qa->get_state_string($showcorrectness);
}
// Special case handling for 'information' items.
if ($qa->get_state() == question_state::$todo) {
return get_string('notyetviewed', 'quiz');
} else {
return get_string('viewed', 'quiz');
}
}
/**
* Hook for subclasses to override to do output above the question buttons.
*
* @param renderer $output the quiz renderer to use.
* @return string HTML to output.
*/
public function render_before_button_bits(renderer $output) {
return '';
}
/**
* Hook that subclasses must override to do output after the question buttons.
*
* @param renderer $output the quiz renderer to use.
* @return string HTML to output.
*/
abstract public function render_end_bits(renderer $output);
/**
* Render the restart preview button.
*
* @param renderer $output the quiz renderer to use.
* @return string HTML to output.
*/
protected function render_restart_preview_link($output) {
if (!$this->attemptobj->is_own_preview()) {
return '';
}
return $output->restart_preview_button(new moodle_url(
$this->attemptobj->start_attempt_url(), ['forcenew' => true]));
}
/**
* Get the URL to navigate to a particular question.
*
* @param int $slot slot number, to identify the question.
* @return moodle_url|null URL if the user can navigate there, or null if they cannot.
*/
abstract protected function get_question_url($slot);
/**
* Get the user picture which should be displayed, if required.
*
* @return user_picture|null
*/
public function user_picture() {
global $DB;
if ($this->attemptobj->get_quiz()->showuserpicture == QUIZ_SHOWIMAGE_NONE) {
return null;
}
$user = $DB->get_record('user', ['id' => $this->attemptobj->get_userid()]);
$userpicture = new user_picture($user);
$userpicture->courseid = $this->attemptobj->get_courseid();
if ($this->attemptobj->get_quiz()->showuserpicture == QUIZ_SHOWIMAGE_LARGE) {
$userpicture->size = true;
}
return $userpicture;
}
/**
* Return 'allquestionsononepage' as CSS class name when $showall is set,
* otherwise, return 'multipages' as CSS class name.
*
* @return string, CSS class name
*/
public function get_button_container_class() {
// Quiz navigation is set on 'Show all questions on one page'.
if ($this->showall) {
return 'allquestionsononepage';
}
// Quiz navigation is set on 'Show one page at a time'.
return 'multipages';
}
}
@@ -0,0 +1,52 @@
<?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/>.
namespace mod_quiz\output;
use html_writer;
/**
* Specialisation of {@see navigation_panel_base} for the review quiz page.
*
* This class is not currently renderable or templatable, but it probably should be in the future,
* which is why it is already in the output namespace.
*
* @package mod_quiz
* @category output
* @copyright 2008 Tim Hunt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class navigation_panel_review extends navigation_panel_base {
public function get_question_url($slot) {
return $this->attemptobj->review_url($slot, -1, $this->showall, $this->page);
}
public function render_end_bits(renderer $output) {
$html = '';
if ($this->attemptobj->get_num_pages() > 1) {
if ($this->showall) {
$html .= html_writer::link($this->attemptobj->review_url(null, 0, false),
get_string('showeachpage', 'quiz'));
} else {
$html .= html_writer::link($this->attemptobj->review_url(null, 0, true),
get_string('showall', 'quiz'));
}
}
$html .= $output->finish_review_link($this->attemptobj);
$html .= $this->render_restart_preview_link($output);
return $html;
}
}
@@ -0,0 +1,51 @@
<?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/>.
namespace mod_quiz\output;
use moodle_url;
use renderable;
/**
* Represents a single link in the navigation panel.
*
* @package mod_quiz
* @category output
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class navigation_question_button implements renderable {
/** @var string id="..." to add to the HTML for this button. */
public $id;
/** @var bool, if this is a real question, not an info item. */
public $isrealquestion;
/** @var string number to display in this button. Either the question number of 'i'. */
public $number;
/** @var string class to add to the class="" attribute to represnt the question state. */
public $stateclass;
/** @var string Textual description of the question state, e.g. to use as a tool tip. */
public $statestring;
/** @var int the page number this question is on. */
public $page;
/** @var bool true if this question is on the current page. */
public $currentpage;
/** @var bool true if this question has been flagged. */
public $flagged;
/** @var moodle_url the link this button goes to, or null if there should not be a link. */
public $url;
/** @var int QUIZ_NAVMETHOD_FREE or QUIZ_NAVMETHOD_SEQ. */
public $navmethod;
}
@@ -0,0 +1,40 @@
<?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/>.
namespace mod_quiz\output;
use renderable;
/**
* Represents a heading in the navigation panel.
*
* @package mod_quiz
* @category output
* @copyright 2015 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class navigation_section_heading implements renderable {
/** @var string the heading text. */
public $heading;
/**
* Constructor.
* @param string $heading the heading text
*/
public function __construct($heading) {
$this->heading = $heading;
}
}
@@ -0,0 +1,111 @@
<?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/>.
namespace mod_quiz\output;
use moodle_url;
use renderable;
use renderer_base;
use templatable;
use url_select;
/**
* Render overrides action in the quiz secondary navigation
*
* The user/group overrides are now handled in the secondary navigation.
* This class provides the data for the templates to handle the data for
* overrides tab.
*
* @package mod_quiz
* @copyright 2021 Sujith Haridasan <sujith@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class overrides_actions implements renderable, templatable {
/** @var int The course module ID. */
private $cmid;
/** @var string The mode passed for the overrides url. */
private $mode;
/** @var bool Check if the user have capabilities to list overrides. */
private $canedit;
/** @var bool Should the add override button be enabled or disabled. */
private $addenabled;
/**
* overrides_action constructor.
*
* @param int $cmid The course module id.
* @param string $mode The mode passed for the overrides url.
* @param bool $canedit Does the user have capabilities to list overrides.
* @param bool $addenabled Whether the add button should be enabled or disabled.
*/
public function __construct(int $cmid, string $mode, bool $canedit, bool $addenabled) {
$this->cmid = $cmid;
$this->mode = $mode;
$this->canedit = $canedit;
$this->addenabled = $addenabled;
}
/**
* Create the add override button.
*
* @param \renderer_base $output an instance of the quiz renderer.
* @return \single_button the button, ready to reander.
*/
public function create_add_button(\renderer_base $output): \single_button {
$addoverrideurl = new moodle_url('/mod/quiz/overrideedit.php',
['cmid' => $this->cmid, 'action' => 'add' . $this->mode]);
if ($this->mode === 'group') {
$label = get_string('addnewgroupoverride', 'quiz');
} else {
$label = get_string('addnewuseroverride', 'quiz');
}
$addoverridebutton = new \single_button($addoverrideurl, $label, 'get', \single_button::BUTTON_PRIMARY);
if (!$this->addenabled) {
$addoverridebutton->disabled = true;
}
return $addoverridebutton;
}
public function export_for_template(renderer_base $output): array {
global $PAGE;
$templatecontext = [];
// Build the navigation drop-down.
$useroverridesurl = new moodle_url('/mod/quiz/overrides.php', ['cmid' => $this->cmid, 'mode' => 'user']);
$groupoverridesurl = new moodle_url('/mod/quiz/overrides.php', ['cmid' => $this->cmid, 'mode' => 'group']);
$menu = [
$useroverridesurl->out(false) => get_string('useroverrides', 'quiz'),
$groupoverridesurl->out(false) => get_string('groupoverrides', 'quiz')
];
$overridesnav = new url_select($menu, $PAGE->url->out(false), null, 'quizoverrides');
$templatecontext['overridesnav'] = $overridesnav->export_for_template($output);
// Build the add button - but only if the user can edit.
if ($this->canedit) {
$templatecontext['addoverridebutton'] = $this->create_add_button($output)->export_for_template($output);
}
return $templatecontext;
}
}
@@ -0,0 +1,35 @@
<?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/>.
/**
* The question_chooser renderable.
*
* @package mod_quiz
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_quiz\output;
/**
* The question_chooser renderable class.
*
* @package mod_quiz
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class question_chooser extends \qbank_editquestion\qbank_chooser {
}
File diff suppressed because it is too large Load Diff
+100
View File
@@ -0,0 +1,100 @@
<?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/>.
namespace mod_quiz\output;
use mod_quiz\access_manager;
use mod_quiz\form\preflight_check_form;
use mod_quiz\quiz_attempt;
use moodle_url;
/**
* This class captures all the various information to render the front page of the quiz activity.
*
* This class is not currently renderable or templatable, but it very nearly could be,
* which is why it is in the output namespace. It is used to send data to the renderer.
*
* @package mod_quiz
* @category output
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class view_page {
/** @var array $infomessages of messages with information to display about the quiz. */
public $infomessages;
/** @var array $attempts contains all the user's attempts at this quiz. */
public $attempts;
/** @var quiz_attempt[] $attemptobjs objects corresponding to $attempts. */
public $attemptobjs;
/** @var list_of_attempts list of past attempts for rendering. */
public $attemptslist;
/** @var access_manager $accessmanager contains various access rules. */
public $accessmanager;
/** @var bool $canreviewmine whether the current user has the capability to
* review their own attempts. */
public $canreviewmine;
/** @var bool $canedit whether the current user has the capability to edit the quiz. */
public $canedit;
/** @var moodle_url $editurl the URL for editing this quiz. */
public $editurl;
/** @var int $attemptcolumn contains the number of attempts done. */
public $attemptcolumn;
/** @var int $gradecolumn contains the grades of any attempts. */
public $gradecolumn;
/** @var int $markcolumn contains the marks of any attempt. */
public $markcolumn;
/** @var int $overallstats contains all marks for any attempt. */
public $overallstats;
/** @var string $feedbackcolumn contains any feedback for and attempt. */
public $feedbackcolumn;
/** @var string $timenow contains a timestamp in string format. */
public $timenow;
/** @var int $numattempts contains the total number of attempts. */
public $numattempts;
/** @var float $mygrade contains the user's final grade for a quiz. */
public $mygrade;
/** @var bool $moreattempts whether this user is allowed more attempts. */
public $moreattempts;
/** @var int $mygradeoverridden contains an overriden grade. */
public $mygradeoverridden;
/** @var string $gradebookfeedback contains any feedback for a gradebook. */
public $gradebookfeedback;
/** @var bool $unfinished contains 1 if an attempt is unfinished. */
public $unfinished;
/** @var stdClass $lastfinishedattempt the last attempt from the attempts array. */
public $lastfinishedattempt;
/** @var array $preventmessages of messages telling the user why they can't
* attempt the quiz now. */
public $preventmessages;
/** @var string $buttontext caption for the start attempt button. If this is null, show no
* button, or if it is '' show a back to the course button. */
public $buttontext;
/** @var moodle_url $startattempturl URL to start an attempt. */
public $startattempturl;
/** @var preflight_check_form|null $preflightcheckform confirmation form that must be
* submitted before an attempt is started, if required. */
public $preflightcheckform;
/** @var moodle_url $startattempturl URL for any Back to the course button. */
public $backtocourseurl;
/** @var bool $showbacktocourse should we show a back to the course button? */
public $showbacktocourse;
/** @var bool whether the attempt must take place in a popup window. */
public $popuprequired;
/** @var array options to use for the popup window, if required. */
public $popupoptions;
/** @var bool $quizhasquestions whether the quiz has any questions. */
public $quizhasquestions;
}