2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * Renderer outputting the quiz editing UI.
21 * @copyright 2013 The Open University.
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 namespace mod_quiz\output
;
26 defined('MOODLE_INTERNAL') ||
die();
28 use \mod_quiz\structure
;
32 * Renderer outputting the quiz editing UI.
34 * @copyright 2013 The Open University.
35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38 class edit_renderer
extends \plugin_renderer_base
{
41 * Render the edit page
43 * @param \quiz $quizobj object containing all the quiz settings information.
44 * @param structure $structure object containing the structure of the quiz.
45 * @param \question_edit_contexts $contexts the relevant question bank contexts.
46 * @param \moodle_url $pageurl the canonical URL of this page.
47 * @param array $pagevars the variables from {@link question_edit_setup()}.
48 * @return string HTML to output.
50 public function edit_page(\quiz
$quizobj, structure
$structure,
51 \question_edit_contexts
$contexts, \moodle_url
$pageurl, array $pagevars) {
55 $output .= $this->heading_with_help(get_string('editingquizx', 'quiz',
56 format_string($quizobj->get_quiz_name())), 'editingquiz', 'quiz', '',
57 get_string('basicideasofquiz', 'quiz'), 2);
59 // Information at the top.
60 $output .= $this->quiz_state_warnings($structure);
61 $output .= $this->quiz_information($structure);
62 $output .= $this->maximum_grade_input($structure, $pageurl);
63 $output .= $this->repaginate_button($structure, $pageurl);
64 $output .= $this->total_marks($quizobj->get_quiz());
66 // Show the questions organised into sections and pages.
67 $output .= $this->start_section_list();
69 foreach ($structure->get_sections() as $section) {
70 $output .= $this->start_section($structure, $section);
71 $output .= $this->questions_in_section($structure, $section, $contexts, $pagevars, $pageurl);
73 if ($structure->is_last_section($section)) {
74 $output .= \html_writer
::start_div('last-add-menu');
75 $output .= html_writer
::tag('span', $this->add_menu_actions($structure, 0,
76 $pageurl, $contexts, $pagevars), array('class' => 'add-menu-outer'));
77 $output .= \html_writer
::end_div();
80 $output .= $this->end_section();
83 $output .= $this->end_section_list();
85 // Initialise the JavaScript.
86 $this->initialise_editing_javascript($structure, $contexts, $pagevars, $pageurl);
88 // Include the contents of any other popups required.
89 if ($structure->can_be_edited()) {
92 $popups .= $this->question_bank_loading();
93 $this->page
->requires
->yui_module('moodle-mod_quiz-quizquestionbank',
94 'M.mod_quiz.quizquestionbank.init',
95 array('class' => 'questionbank', 'cmid' => $structure->get_cmid()));
97 $popups .= $this->random_question_form($pageurl, $contexts, $pagevars);
98 $this->page
->requires
->yui_module('moodle-mod_quiz-randomquestion',
99 'M.mod_quiz.randomquestion.init');
101 $output .= html_writer
::div($popups, 'mod_quiz_edit_forms');
103 // Include the question chooser.
104 $output .= $this->question_chooser();
105 $this->page
->requires
->yui_module('moodle-mod_quiz-questionchooser', 'M.mod_quiz.init_questionchooser');
112 * Render any warnings that might be required about the state of the quiz,
113 * e.g. if it has been attempted, or if the shuffle questions option is
116 * @param structure $structure the quiz structure.
117 * @return string HTML to output.
119 public function quiz_state_warnings(structure
$structure) {
120 $warnings = $structure->get_edit_page_warnings();
122 if (empty($warnings)) {
127 foreach ($warnings as $warning) {
128 $output[] = \html_writer
::tag('p', $warning);
130 return $this->box(implode("\n", $output), 'statusdisplay');
134 * Render the status bar.
136 * @param structure $structure the quiz structure.
137 * @return string HTML to output.
139 public function quiz_information(structure
$structure) {
140 list($currentstatus, $explanation) = $structure->get_dates_summary();
142 $output = html_writer
::span(
143 get_string('numquestionsx', 'quiz', $structure->get_question_count()),
144 'numberofquestions') . ' | ' .
145 html_writer
::span($currentstatus, 'quizopeningstatus',
146 array('title' => $explanation));
148 return html_writer
::div($output, 'statusbar');
152 * Render the form for setting a quiz' overall grade
154 * @param structure $structure the quiz structure.
155 * @param \moodle_url $pageurl the canonical URL of this page.
156 * @return string HTML to output.
158 public function maximum_grade_input($structure, \moodle_url
$pageurl) {
160 $output .= html_writer
::start_div('maxgrade');
161 $output .= html_writer
::start_tag('form', array('method' => 'post', 'action' => 'edit.php',
162 'class' => 'quizsavegradesform'));
163 $output .= html_writer
::start_tag('fieldset', array('class' => 'invisiblefieldset'));
164 $output .= html_writer
::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
165 $output .= html_writer
::input_hidden_params($pageurl);
166 $a = html_writer
::empty_tag('input', array('type' => 'text', 'id' => 'inputmaxgrade',
167 'name' => 'maxgrade', 'size' => ($structure->get_decimal_places_for_grades() +
2),
168 'value' => $structure->formatted_quiz_grade()));
169 $output .= html_writer
::tag('label', get_string('maximumgradex', '', $a),
170 array('for' => 'inputmaxgrade'));
171 $output .= html_writer
::empty_tag('input', array('type' => 'submit',
172 'name' => 'savechanges', 'value' => get_string('save', 'quiz')));
173 $output .= html_writer
::end_tag('fieldset');
174 $output .= html_writer
::end_tag('form');
175 $output .= html_writer
::end_tag('div');
180 * Return the repaginate button
181 * @param structure $structure the structure of the quiz being edited.
182 * @param \moodle_url $pageurl the canonical URL of this page.
183 * @return string HTML to output.
185 protected function repaginate_button(structure
$structure, \moodle_url
$pageurl) {
187 $header = html_writer
::tag('span', get_string('repaginatecommand', 'quiz'), array('class' => 'repaginatecommand'));
188 $form = $this->repaginate_form($structure, $pageurl);
189 $containeroptions = array(
190 'class' => 'rpcontainerclass',
191 'cmid' => $structure->get_cmid(),
196 $buttonoptions = array(
198 'name' => 'repaginate',
199 'id' => 'repaginatecommand',
200 'value' => get_string('repaginatecommand', 'quiz'),
202 if (!$structure->can_be_repaginated()) {
203 $buttonoptions['disabled'] = 'disabled';
205 $this->page
->requires
->yui_module('moodle-mod_quiz-repaginate', 'M.mod_quiz.repaginate.init');
208 return html_writer
::tag('div',
209 html_writer
::empty_tag('input', $buttonoptions), $containeroptions);
213 * Return the repaginate form
214 * @param structure $structure the structure of the quiz being edited.
215 * @param \moodle_url $pageurl the canonical URL of this page.
216 * @return string HTML to output.
218 protected function repaginate_form(structure
$structure, \moodle_url
$pageurl) {
220 $perpage[0] = get_string('allinone', 'quiz');
221 for ($i = 1; $i <= 50; ++
$i) {
225 $hiddenurl = clone($pageurl);
226 $hiddenurl->param('sesskey', sesskey());
228 $select = html_writer
::select($perpage, 'questionsperpage',
229 $structure->get_questions_per_page(), false);
231 $buttonattributes = array('type' => 'submit', 'name' => 'repaginate', 'value' => get_string('go'));
233 $formcontent = html_writer
::tag('form', html_writer
::div(
234 html_writer
::input_hidden_params($hiddenurl) .
235 get_string('repaginate', 'quiz', $select) .
236 html_writer
::empty_tag('input', $buttonattributes)
237 ), array('action' => 'edit.php', 'method' => 'post'));
239 return html_writer
::div($formcontent, '', array('id' => 'repaginatedialog'));
243 * Render the total marks available for the quiz.
245 * @param \stdClass $quiz the quiz settings from the database.
246 * @return string HTML to output.
248 public function total_marks($quiz) {
249 $totalmark = html_writer
::span(quiz_format_grade($quiz, $quiz->sumgrades
), 'mod_quiz_summarks');
250 return html_writer
::tag('span',
251 get_string('totalmarksx', 'quiz', $totalmark),
252 array('class' => 'totalpoints'));
256 * Generate the starting container html for the start of a list of sections
257 * @return string HTML to output.
259 protected function start_section_list() {
260 return html_writer
::start_tag('ul', array('class' => 'slots'));
264 * Generate the closing container html for the end of a list of sections
265 * @return string HTML to output.
267 protected function end_section_list() {
268 return html_writer
::end_tag('ul');
272 * Display the start of a section, before the questions.
274 * @param structure $structure the structure of the quiz being edited.
275 * @param \stdClass $section The quiz_section entry from DB
276 * @return string HTML to output.
278 protected function start_section($structure, $section) {
283 if ($structure->is_only_one_slot_in_section($section)) {
284 $sectionstyle = ' only-has-one-slot';
287 $output .= html_writer
::start_tag('li', array('id' => 'section-'.$section->id
,
288 'class' => 'section main clearfix'.$sectionstyle, 'role' => 'region',
289 'aria-label' => $section->heading
));
291 $output .= html_writer
::start_div('content');
293 $output .= html_writer
::start_div('section-heading');
295 $headingtext = $this->heading(html_writer
::span(
296 html_writer
::span($section->heading
, 'instancesection'), 'sectioninstance'), 3);
298 if (!$structure->can_be_edited()) {
299 $editsectionheadingicon = '';
301 $editsectionheadingicon = html_writer
::link(new \
moodle_url('#'),
302 $this->pix_icon('t/editstring', get_string('sectionheadingedit', 'quiz', $section->heading
),
303 'moodle', array('class' => 'editicon visibleifjs')),
304 array('class' => 'editing_section', 'data-action' => 'edit_section_title'));
306 $output .= html_writer
::div($headingtext . $editsectionheadingicon, 'instancesectioncontainer');
308 if (!$structure->is_first_section($section) && $structure->can_be_edited()) {
309 $output .= $this->section_remove_icon($section);
311 $output .= $this->section_shuffle_questions($structure, $section);
313 $output .= html_writer
::end_div($output, 'section-heading');
319 * Display a checkbox for shuffling question within a section.
321 * @param structure $structure object containing the structure of the quiz.
322 * @param \stdClass $section data from the quiz_section table.
323 * @return string HTML to output.
325 public function section_shuffle_questions(structure
$structure, $section) {
326 $checkboxattributes = array(
327 'type' => 'checkbox',
328 'id' => 'shuffle-' . $section->id
,
330 'data-action' => 'shuffle_questions',
331 'class' => 'cm-edit-action',
334 if (!$structure->can_be_edited()) {
335 $checkboxattributes['disabled'] = 'disabled';
337 if ($section->shufflequestions
) {
338 $checkboxattributes['checked'] = 'checked';
341 if ($structure->is_first_section($section)) {
342 $help = $this->help_icon('shufflequestions', 'quiz');
347 $progressspan = html_writer
::span('', 'shuffle-progress');
348 $checkbox = html_writer
::empty_tag('input', $checkboxattributes);
349 $label = html_writer
::label(get_string('shufflequestions', 'quiz') . ' ' . $help,
350 $checkboxattributes['id'], false);
351 return html_writer
::span($progressspan . $checkbox . $label,
352 'instanceshufflequestions', array('data-action' => 'shuffle_questions'));
356 * Display the end of a section, after the questions.
358 * @return string HTML to output.
360 protected function end_section() {
361 $output = html_writer
::end_tag('div');
362 $output .= html_writer
::end_tag('li');
368 * Render an icon to remove a section from the quiz.
370 * @param object $section the section to be removed.
371 * @return string HTML to output.
373 public function section_remove_icon($section) {
374 $title = get_string('sectionheadingremove', 'quiz', $section->heading
);
375 $url = new \
moodle_url('/mod/quiz/edit.php',
376 array('sesskey' => sesskey(), 'removesection' => '1', 'sectionid' => $section->id
));
377 $image = $this->pix_icon('t/delete', $title);
378 return $this->action_link($url, $image, null, array(
379 'class' => 'cm-edit-action editing_delete', 'data-action' => 'deletesection'));
383 * Renders HTML to display the questions in a section of the quiz.
385 * This function calls {@link core_course_renderer::quiz_section_question()}
387 * @param structure $structure object containing the structure of the quiz.
388 * @param \stdClass $section information about the section.
389 * @param \question_edit_contexts $contexts the relevant question bank contexts.
390 * @param array $pagevars the variables from {@link \question_edit_setup()}.
391 * @param \moodle_url $pageurl the canonical URL of this page.
392 * @return string HTML to output.
394 public function questions_in_section(structure
$structure, $section,
395 $contexts, $pagevars, $pageurl) {
398 foreach ($structure->get_slots_in_section($section->id
) as $slot) {
399 $output .= $this->question_row($structure, $slot, $contexts, $pagevars, $pageurl);
401 return html_writer
::tag('ul', $output, array('class' => 'section img-text'));
405 * Displays one question with the surrounding controls.
407 * @param structure $structure object containing the structure of the quiz.
408 * @param int $slot which slot we are outputting.
409 * @param \question_edit_contexts $contexts the relevant question bank contexts.
410 * @param array $pagevars the variables from {@link \question_edit_setup()}.
411 * @param \moodle_url $pageurl the canonical URL of this page.
412 * @return string HTML to output.
414 public function question_row(structure
$structure, $slot, $contexts, $pagevars, $pageurl) {
417 $output .= $this->page_row($structure, $slot, $contexts, $pagevars, $pageurl);
419 // Page split/join icon.
421 if ($structure->can_be_edited() && !$structure->is_last_slot_in_quiz($slot) &&
422 !$structure->is_last_slot_in_section($slot)) {
423 $joinhtml = $this->page_split_join_button($structure, $slot);
426 $questionhtml = $this->question($structure, $slot, $pageurl);
427 $qtype = $structure->get_question_type_for_slot($slot);
428 $questionclasses = 'activity ' . $qtype . ' qtype_' . $qtype . ' slot';
430 $output .= html_writer
::tag('li', $questionhtml . $joinhtml,
431 array('class' => $questionclasses, 'id' => 'slot-' . $structure->get_slot_id_for_slot($slot),
432 'data-canfinish' => $structure->can_finish_during_the_attempt($slot)));
438 * Displays one question with the surrounding controls.
440 * @param structure $structure object containing the structure of the quiz.
441 * @param int $slot the first slot on the page we are outputting.
442 * @param \question_edit_contexts $contexts the relevant question bank contexts.
443 * @param array $pagevars the variables from {@link \question_edit_setup()}.
444 * @param \moodle_url $pageurl the canonical URL of this page.
445 * @return string HTML to output.
447 public function page_row(structure
$structure, $slot, $contexts, $pagevars, $pageurl) {
450 $pagenumber = $structure->get_page_number_for_slot($slot);
452 // Put page in a heading for accessibility and styling.
453 $page = $this->heading(get_string('page') . ' ' . $pagenumber, 4);
455 if ($structure->is_first_slot_on_page($slot)) {
456 // Add the add-menu at the page level.
457 $addmenu = html_writer
::tag('span', $this->add_menu_actions($structure,
458 $pagenumber, $pageurl, $contexts, $pagevars),
459 array('class' => 'add-menu-outer'));
461 $addquestionform = $this->add_question_form($structure,
462 $pagenumber, $pageurl, $pagevars);
464 $output .= html_writer
::tag('li', $page . $addmenu . $addquestionform,
465 array('class' => 'pagenumber activity yui3-dd-drop page', 'id' => 'page-' . $pagenumber));
472 * Returns the add menu that is output once per page.
473 * @param structure $structure object containing the structure of the quiz.
474 * @param int $page the page number that this menu will add to.
475 * @param \moodle_url $pageurl the canonical URL of this page.
476 * @param \question_edit_contexts $contexts the relevant question bank contexts.
477 * @param array $pagevars the variables from {@link \question_edit_setup()}.
478 * @return string HTML to output.
480 public function add_menu_actions(structure
$structure, $page, \moodle_url
$pageurl,
481 \question_edit_contexts
$contexts, array $pagevars) {
483 $actions = $this->edit_menu_actions($structure, $page, $pageurl, $pagevars);
484 if (empty($actions)) {
487 $menu = new \action_menu
();
488 $menu->set_alignment(\action_menu
::TR
, \action_menu
::TR
);
489 $menu->set_constraint('.mod-quiz-edit-content');
490 $trigger = html_writer
::tag('span', get_string('add', 'quiz'), array('class' => 'add-menu'));
491 $menu->set_menu_trigger($trigger);
492 // The menu appears within an absolutely positioned element causing width problems.
493 // Make sure no-wrap is set so that we don't get a squashed menu.
494 $menu->set_nowrap_on_items(true);
496 // Disable the link if quiz has attempts.
497 if (!$structure->can_be_edited()) {
498 return $this->render($menu);
501 foreach ($actions as $action) {
502 if ($action instanceof \action_menu_link
) {
503 $action->add_class('add-menu');
507 $menu->attributes
['class'] .= ' page-add-actions commands';
509 // Prioritise the menu ahead of all other actions.
510 $menu->prioritise
= true;
512 return $this->render($menu);
516 * Returns the list of actions to go in the add menu.
517 * @param structure $structure object containing the structure of the quiz.
518 * @param int $page the page number that this menu will add to.
519 * @param \moodle_url $pageurl the canonical URL of this page.
520 * @param array $pagevars the variables from {@link \question_edit_setup()}.
521 * @return array the actions.
523 public function edit_menu_actions(structure
$structure, $page,
524 \moodle_url
$pageurl, array $pagevars) {
525 $questioncategoryid = question_get_category_id_from_pagevars($pagevars);
528 $str = get_strings(array('addasection', 'addaquestion', 'addarandomquestion',
529 'addarandomselectedquestion', 'questionbank'), 'quiz');
532 // Get section, page, slotnumber and maxmark.
535 // Add a new section to the add_menu if possible. This is always added to the HTML
536 // then hidden with CSS when no needed, so that as things are re-ordered, etc. with
537 // Ajax it can be relevaled again when necessary.
538 $params = array('cmid' => $structure->get_cmid(), 'addsectionatpage' => $page);
540 $actions['addasection'] = new \action_menu_link_secondary
(
541 new \
moodle_url($pageurl, $params),
542 new \
pix_icon('t/add', $str->addasection
, 'moodle', array('class' => 'iconsmall', 'title' => '')),
543 $str->addasection
, array('class' => 'cm-edit-action addasection', 'data-action' => 'addasection')
546 // Add a new question to the quiz.
547 $returnurl = new \
moodle_url($pageurl, array('addonpage' => $page));
548 $params = array('returnurl' => $returnurl->out_as_local_url(false),
549 'cmid' => $structure->get_cmid(), 'category' => $questioncategoryid,
550 'addonpage' => $page, 'appendqnumstring' => 'addquestion');
552 $actions['addaquestion'] = new \action_menu_link_secondary
(
553 new \
moodle_url('/question/addquestion.php', $params),
554 new \
pix_icon('t/add', $str->addaquestion
, 'moodle', array('class' => 'iconsmall', 'title' => '')),
555 $str->addaquestion
, array('class' => 'cm-edit-action addquestion', 'data-action' => 'addquestion')
558 // Call question bank.
559 $icon = new \
pix_icon('t/add', $str->questionbank
, 'moodle', array('class' => 'iconsmall', 'title' => ''));
560 $title = get_string('addquestionfrombanktopage', 'quiz', $page);
561 $attributes = array('class' => 'cm-edit-action questionbank',
562 'data-header' => $title, 'data-action' => 'questionbank', 'data-addonpage' => $page);
563 $actions['questionbank'] = new \action_menu_link_secondary
($pageurl, $icon, $str->questionbank
, $attributes);
565 // Add a random question.
566 $returnurl = new \
moodle_url('/mod/quiz/edit.php', array('cmid' => $structure->get_cmid(), 'data-addonpage' => $page));
567 $params = array('returnurl' => $returnurl, 'cmid' => $structure->get_cmid(), 'appendqnumstring' => 'addarandomquestion');
568 $url = new \
moodle_url('/mod/quiz/addrandom.php', $params);
569 $icon = new \
pix_icon('t/add', $str->addarandomquestion
, 'moodle', array('class' => 'iconsmall', 'title' => ''));
570 $attributes = array('class' => 'cm-edit-action addarandomquestion', 'data-action' => 'addarandomquestion');
571 $title = get_string('addrandomquestiontopage', 'quiz', $page);
572 $attributes = array_merge(array('data-header' => $title, 'data-addonpage' => $page), $attributes);
573 $actions['addarandomquestion'] = new \action_menu_link_secondary
($url, $icon, $str->addarandomquestion
, $attributes);
579 * Render the form that contains the data for adding a new question to the quiz.
581 * @param structure $structure object containing the structure of the quiz.
582 * @param int $page the page number that this menu will add to.
583 * @param \moodle_url $pageurl the canonical URL of this page.
584 * @param array $pagevars the variables from {@link \question_edit_setup()}.
585 * @return string HTML to output.
587 protected function add_question_form(structure
$structure, $page, \moodle_url
$pageurl, array $pagevars) {
589 $questioncategoryid = question_get_category_id_from_pagevars($pagevars);
591 $output = html_writer
::tag('input', null,
592 array('type' => 'hidden', 'name' => 'returnurl',
593 'value' => $pageurl->out_as_local_url(false, array('addonpage' => $page))));
594 $output .= html_writer
::tag('input', null,
595 array('type' => 'hidden', 'name' => 'cmid', 'value' => $structure->get_cmid()));
596 $output .= html_writer
::tag('input', null,
597 array('type' => 'hidden', 'name' => 'appendqnumstring', 'value' => 'addquestion'));
598 $output .= html_writer
::tag('input', null,
599 array('type' => 'hidden', 'name' => 'category', 'value' => $questioncategoryid));
601 return html_writer
::tag('form', html_writer
::div($output),
602 array('class' => 'addnewquestion', 'method' => 'post',
603 'action' => new \
moodle_url('/question/addquestion.php')));
607 * Display a question.
609 * @param structure $structure object containing the structure of the quiz.
610 * @param int $slot the first slot on the page we are outputting.
611 * @param \moodle_url $pageurl the canonical URL of this page.
612 * @return string HTML to output.
614 public function question(structure
$structure, $slot, \moodle_url
$pageurl) {
616 $output .= html_writer
::start_tag('div');
618 if ($structure->can_be_edited()) {
619 $output .= $this->question_move_icon($structure, $slot);
622 $output .= html_writer
::start_div('mod-indent-outer');
623 $output .= $this->question_number($structure->get_displayed_number_for_slot($slot));
625 // This div is used to indent the content.
626 $output .= html_writer
::div('', 'mod-indent');
628 // Display the link to the question (or do nothing if question has no url).
629 if ($structure->get_question_type_for_slot($slot) == 'random') {
630 $questionname = $this->random_question($structure, $slot, $pageurl);
632 $questionname = $this->question_name($structure, $slot, $pageurl);
635 // Start the div for the activity title, excluding the edit icons.
636 $output .= html_writer
::start_div('activityinstance');
637 $output .= $questionname;
639 // Closing the tag which contains everything but edit icons. Content part of the module should not be part of this.
640 $output .= html_writer
::end_tag('div'); // .activityinstance.
644 $questionicons .= $this->question_preview_icon($structure->get_quiz(), $structure->get_question_in_slot($slot));
645 if ($structure->can_be_edited()) {
646 $questionicons .= $this->question_remove_icon($structure, $slot, $pageurl);
648 $questionicons .= $this->marked_out_of_field($structure, $slot);
649 $output .= html_writer
::span($questionicons, 'actions'); // Required to add js spinner icon.
650 if ($structure->can_be_edited()) {
651 $output .= $this->question_dependency_icon($structure, $slot);
654 // End of indentation div.
655 $output .= html_writer
::end_tag('div');
656 $output .= html_writer
::end_tag('div');
662 * Render the move icon.
664 * @param structure $structure object containing the structure of the quiz.
665 * @param int $slot the first slot on the page we are outputting.
666 * @return string The markup for the move action.
668 public function question_move_icon(structure
$structure, $slot) {
669 return html_writer
::link(new \
moodle_url('#'),
670 $this->pix_icon('i/dragdrop', get_string('move'), 'moodle', array('class' => 'iconsmall', 'title' => '')),
671 array('class' => 'editing_move', 'data-action' => 'move')
676 * Output the question number.
677 * @param string $number The number, or 'i'.
678 * @return string HTML to output.
680 public function question_number($number) {
681 if (is_numeric($number)) {
682 $number = html_writer
::span(get_string('question'), 'accesshide') . ' ' . $number;
684 return html_writer
::tag('span', $number, array('class' => 'slotnumber'));
688 * Render the preview icon.
690 * @param \stdClass $quiz the quiz settings from the database.
691 * @param \stdClass $question data from the question and quiz_slots tables.
692 * @param bool $label if true, show the preview question label after the icon
693 * @return string HTML to output.
695 public function question_preview_icon($quiz, $question, $label = null) {
696 $url = quiz_question_preview_url($quiz, $question);
698 // Do we want a label?
699 $strpreviewlabel = '';
701 $strpreviewlabel = ' ' . get_string('preview', 'quiz');
705 $strpreviewquestion = get_string('previewquestion', 'quiz');
706 $image = $this->pix_icon('t/preview', $strpreviewquestion);
708 $action = new \
popup_action('click', $url, 'questionpreview',
709 question_preview_popup_params());
711 return $this->action_link($url, $image . $strpreviewlabel, $action,
712 array('title' => $strpreviewquestion, 'class' => 'preview'));
716 * Render an icon to remove a question from the quiz.
718 * @param structure $structure object containing the structure of the quiz.
719 * @param int $slot the first slot on the page we are outputting.
720 * @param \moodle_url $pageurl the canonical URL of the edit page.
721 * @return string HTML to output.
723 public function question_remove_icon(structure
$structure, $slot, $pageurl) {
724 $url = new \
moodle_url($pageurl, array('sesskey' => sesskey(), 'remove' => $slot));
725 $strdelete = get_string('delete');
727 $image = $this->pix_icon('t/delete', $strdelete);
729 return $this->action_link($url, $image, null, array('title' => $strdelete,
730 'class' => 'cm-edit-action editing_delete', 'data-action' => 'delete'));
734 * Display an icon to split or join two pages of the quiz.
736 * @param structure $structure object containing the structure of the quiz.
737 * @param int $slot the first slot on the page we are outputting.
738 * @return string HTML to output.
740 public function page_split_join_button($structure, $slot) {
741 $insertpagebreak = !$structure->is_last_slot_on_page($slot);
742 $url = new \
moodle_url('repaginate.php', array('quizid' => $structure->get_quizid(),
743 'slot' => $slot, 'repag' => $insertpagebreak ?
2 : 1, 'sesskey' => sesskey()));
745 if ($insertpagebreak) {
746 $title = get_string('addpagebreak', 'quiz');
747 $image = $this->pix_icon('e/insert_page_break', $title);
748 $action = 'addpagebreak';
750 $title = get_string('removepagebreak', 'quiz');
751 $image = $this->pix_icon('e/remove_page_break', $title);
752 $action = 'removepagebreak';
755 // Disable the link if quiz has attempts.
757 if (!$structure->can_be_edited()) {
758 $disabled = 'disabled';
760 return html_writer
::span($this->action_link($url, $image, null, array('title' => $title,
761 'class' => 'page_split_join cm-edit-action', 'disabled' => $disabled, 'data-action' => $action)),
762 'page_split_join_wrapper');
766 * Display the icon for whether this question can only be seen if the previous
767 * one has been answered.
769 * @param structure $structure object containing the structure of the quiz.
770 * @param int $slot the first slot on the page we are outputting.
771 * @return string HTML to output.
773 public function question_dependency_icon($structure, $slot) {
775 'thisq' => $structure->get_displayed_number_for_slot($slot),
776 'previousq' => $structure->get_displayed_number_for_slot(max($slot - 1, 1)),
778 if ($structure->is_question_dependent_on_previous_slot($slot)) {
779 $title = get_string('questiondependencyremove', 'quiz', $a);
780 $image = $this->pix_icon('t/locked', get_string('questiondependsonprevious', 'quiz'),
781 'moodle', array('title' => ''));
782 $action = 'removedependency';
784 $title = get_string('questiondependencyadd', 'quiz', $a);
785 $image = $this->pix_icon('t/unlocked', get_string('questiondependencyfree', 'quiz'),
786 'moodle', array('title' => ''));
787 $action = 'adddependency';
790 // Disable the link if quiz has attempts.
792 if (!$structure->can_be_edited()) {
793 $disabled = 'disabled';
796 if (!$structure->can_question_depend_on_previous_slot($slot)) {
797 $extraclass = ' question_dependency_cannot_depend';
799 return html_writer
::span($this->action_link('#', $image, null, array('title' => $title,
800 'class' => 'cm-edit-action', 'disabled' => $disabled, 'data-action' => $action)),
801 'question_dependency_wrapper' . $extraclass);
805 * Renders html to display a name with the link to the question on a quiz edit page
807 * If the user does not have permission to edi the question, it is rendered
810 * @param structure $structure object containing the structure of the quiz.
811 * @param int $slot which slot we are outputting.
812 * @param \moodle_url $pageurl the canonical URL of this page.
813 * @return string HTML to output.
815 public function question_name(structure
$structure, $slot, $pageurl) {
818 $question = $structure->get_question_in_slot($slot);
819 $editurl = new \
moodle_url('/question/question.php', array(
820 'returnurl' => $pageurl->out_as_local_url(),
821 'cmid' => $structure->get_cmid(), 'id' => $question->id
));
823 $instancename = quiz_question_tostring($question);
825 $qtype = \question_bank
::get_qtype($question->qtype
, false);
826 $namestr = $qtype->local_name();
828 $icon = $this->pix_icon('icon', $namestr, $qtype->plugin_name(), array('title' => $namestr,
829 'class' => 'icon activityicon', 'alt' => ' ', 'role' => 'presentation'));
831 $editicon = $this->pix_icon('t/edit', '', 'moodle', array('title' => ''));
833 // Need plain question name without html tags for link title.
834 $title = shorten_text(format_string($question->name
), 100);
836 // Display the link itself.
837 $activitylink = $icon . html_writer
::tag('span', $editicon . $instancename, array('class' => 'instancename'));
838 $output .= html_writer
::link($editurl, $activitylink,
839 array('title' => get_string('editquestion', 'quiz').' '.$title));
845 * Renders html to display a random question the link to edit the configuration
846 * and also to see that category in the question bank.
848 * @param structure $structure object containing the structure of the quiz.
849 * @param int $slot which slot we are outputting.
850 * @param \moodle_url $pageurl the canonical URL of this page.
851 * @return string HTML to output.
853 public function random_question(structure
$structure, $slot, $pageurl) {
855 $question = $structure->get_question_in_slot($slot);
856 $editurl = new \
moodle_url('/question/question.php', array(
857 'returnurl' => $pageurl->out_as_local_url(),
858 'cmid' => $structure->get_cmid(), 'id' => $question->id
));
860 $temp = clone($question);
861 $temp->questiontext
= '';
862 $instancename = quiz_question_tostring($temp);
864 $configuretitle = get_string('configurerandomquestion', 'quiz');
865 $qtype = \question_bank
::get_qtype($question->qtype
, false);
866 $namestr = $qtype->local_name();
867 $icon = $this->pix_icon('icon', $namestr, $qtype->plugin_name(), array('title' => $namestr,
868 'class' => 'icon activityicon', 'alt' => ' ', 'role' => 'presentation'));
870 $editicon = $this->pix_icon('t/edit', $configuretitle, 'moodle', array('title' => ''));
872 // If this is a random question, display a link to show the questions
873 // selected from in the question bank.
874 $qbankurl = new \
moodle_url('/question/edit.php', array(
875 'cmid' => $structure->get_cmid(),
876 'cat' => $question->category
. ',' . $question->contextid
,
877 'recurse' => !empty($question->questiontext
)));
878 $qbanklink = ' ' . \html_writer
::link($qbankurl,
879 get_string('seequestions', 'quiz'), array('class' => 'mod_quiz_random_qbank_link'));
881 return html_writer
::link($editurl, $icon . $editicon, array('title' => $configuretitle)) .
882 ' ' . $instancename . ' ' . $qbanklink;
886 * Display the 'marked out of' information for a question.
887 * Along with the regrade action.
888 * @param structure $structure object containing the structure of the quiz.
889 * @param int $slot which slot we are outputting.
890 * @return string HTML to output.
892 public function marked_out_of_field(structure
$structure, $slot) {
893 if (!$structure->is_real_question($slot)) {
894 $output = html_writer
::span('',
895 'instancemaxmark decimalplaces_' . $structure->get_decimal_places_for_question_marks());
897 $output .= html_writer
::span(
898 $this->pix_icon('spacer', '', 'moodle', array('class' => 'editicon visibleifjs', 'title' => '')),
900 return html_writer
::span($output, 'instancemaxmarkcontainer infoitem');
903 $output = html_writer
::span($structure->formatted_question_grade($slot),
904 'instancemaxmark decimalplaces_' . $structure->get_decimal_places_for_question_marks(),
905 array('title' => get_string('maxmark', 'quiz')));
907 $output .= html_writer
::span(
909 new \
moodle_url('#'),
910 $this->pix_icon('t/editstring', '', 'moodle', array('class' => 'editicon visibleifjs', 'title' => '')),
912 'class' => 'editing_maxmark',
913 'data-action' => 'editmaxmark',
914 'title' => get_string('editmaxmark', 'quiz'),
918 return html_writer
::span($output, 'instancemaxmarkcontainer');
922 * Render the question type chooser dialogue.
923 * @return string HTML to output.
925 public function question_chooser() {
926 $container = html_writer
::div(print_choose_qtype_to_add_form(array(), null, false), '',
927 array('id' => 'qtypechoicecontainer'));
928 return html_writer
::div($container, 'createnewquestion');
932 * Render the contents of the question bank pop-up in its initial state,
933 * when it just contains a loading progress indicator.
934 * @return string HTML to output.
936 public function question_bank_loading() {
937 return html_writer
::div(html_writer
::empty_tag('img',
938 array('alt' => 'loading', 'class' => 'loading-icon', 'src' => $this->pix_url('i/loading'))),
939 'questionbankloading');
943 * Return random question form.
944 * @param \moodle_url $thispageurl the canonical URL of this page.
945 * @param \question_edit_contexts $contexts the relevant question bank contexts.
946 * @param array $pagevars the variables from {@link \question_edit_setup()}.
947 * @return string HTML to output.
949 protected function random_question_form(\moodle_url
$thispageurl, \question_edit_contexts
$contexts, array $pagevars) {
951 if (!$contexts->have_cap('moodle/question:useall')) {
954 $randomform = new \
quiz_add_random_form(new \
moodle_url('/mod/quiz/addrandom.php'),
955 array('contexts' => $contexts, 'cat' => $pagevars['cat']));
956 $randomform->set_data(array(
957 'category' => $pagevars['cat'],
958 'returnurl' => $thispageurl->out_as_local_url(true),
960 'cmid' => $thispageurl->param('cmid'),
962 return html_writer
::div($randomform->render(), 'randomquestionformforpopup');
966 * Initialise the JavaScript for the general editing. (JavaScript for popups
967 * is handled with the specific code for those.)
969 * @param structure $structure object containing the structure of the quiz.
970 * @param \question_edit_contexts $contexts the relevant question bank contexts.
971 * @param array $pagevars the variables from {@link \question_edit_setup()}.
972 * @param \moodle_url $pageurl the canonical URL of this page.
973 * @return bool Always returns true
975 protected function initialise_editing_javascript(structure
$structure,
976 \question_edit_contexts
$contexts, array $pagevars, \moodle_url
$pageurl) {
978 $config = new \
stdClass();
979 $config->resourceurl
= '/mod/quiz/edit_rest.php';
980 $config->sectionurl
= '/mod/quiz/edit_rest.php';
981 $config->pageparams
= array();
982 $config->questiondecimalpoints
= $structure->get_decimal_places_for_question_marks();
983 $config->pagehtml
= $this->new_page_template($structure, $contexts, $pagevars, $pageurl);
984 $config->addpageiconhtml
= $this->add_page_icon_template($structure);
986 $this->page
->requires
->yui_module('moodle-mod_quiz-toolboxes',
987 'M.mod_quiz.init_resource_toolbox',
989 'courseid' => $structure->get_courseid(),
990 'quizid' => $structure->get_quizid(),
991 'ajaxurl' => $config->resourceurl
,
995 unset($config->pagehtml
);
996 unset($config->addpageiconhtml
);
998 $this->page
->requires
->yui_module('moodle-mod_quiz-toolboxes',
999 'M.mod_quiz.init_section_toolbox',
1001 'courseid' => $structure,
1002 'quizid' => $structure->get_quizid(),
1003 'ajaxurl' => $config->sectionurl
,
1004 'config' => $config,
1008 $this->page
->requires
->yui_module('moodle-mod_quiz-dragdrop', 'M.mod_quiz.init_section_dragdrop',
1010 'courseid' => $structure,
1011 'quizid' => $structure->get_quizid(),
1012 'ajaxurl' => $config->sectionurl
,
1013 'config' => $config,
1016 $this->page
->requires
->yui_module('moodle-mod_quiz-dragdrop', 'M.mod_quiz.init_resource_dragdrop',
1018 'courseid' => $structure,
1019 'quizid' => $structure->get_quizid(),
1020 'ajaxurl' => $config->resourceurl
,
1021 'config' => $config,
1024 // Require various strings for the command toolbox.
1025 $this->page
->requires
->strings_for_js(array(
1028 'deletechecktypename',
1030 'edittitleinstructions',
1031 'emptydragdropregion',
1046 $this->page
->requires
->strings_for_js(array(
1048 'confirmremovesectionheading',
1049 'confirmremovequestion',
1053 'sectionheadingedit',
1054 'sectionheadingremove',
1056 'questiondependencyadd',
1057 'questiondependencyfree',
1058 'questiondependencyremove',
1059 'questiondependsonprevious',
1062 foreach (\question_bank
::get_all_qtypes() as $qtype => $notused) {
1063 $this->page
->requires
->string_for_js('pluginname', 'qtype_' . $qtype);
1070 * HTML for a page, with ids stripped, so it can be used as a javascript template.
1072 * @param structure $structure object containing the structure of the quiz.
1073 * @param \question_edit_contexts $contexts the relevant question bank contexts.
1074 * @param array $pagevars the variables from {@link \question_edit_setup()}.
1075 * @param \moodle_url $pageurl the canonical URL of this page.
1076 * @return string HTML for a new page.
1078 protected function new_page_template(structure
$structure,
1079 \question_edit_contexts
$contexts, array $pagevars, \moodle_url
$pageurl) {
1080 if (!$structure->has_questions()) {
1084 $pagehtml = $this->page_row($structure, 1, $contexts, $pagevars, $pageurl);
1086 // Normalise the page number.
1087 $pagenumber = $structure->get_page_number_for_slot(1);
1088 $strcontexts = array();
1089 $strcontexts[] = 'page-';
1090 $strcontexts[] = get_string('page') . ' ';
1091 $strcontexts[] = 'addonpage%3D';
1092 $strcontexts[] = 'addonpage=';
1093 $strcontexts[] = 'addonpage="';
1094 $strcontexts[] = get_string('addquestionfrombanktopage', 'quiz', '');
1095 $strcontexts[] = 'data-addonpage%3D';
1096 $strcontexts[] = 'action-menu-';
1098 foreach ($strcontexts as $strcontext) {
1099 $pagehtml = str_replace($strcontext . $pagenumber, $strcontext . '%%PAGENUMBER%%', $pagehtml);
1106 * HTML for a page, with ids stripped, so it can be used as a javascript template.
1108 * @param structure $structure object containing the structure of the quiz.
1109 * @return string HTML for a new icon
1111 protected function add_page_icon_template(structure
$structure) {
1113 if (!$structure->has_questions()) {
1117 $html = $this->page_split_join_button($structure, 1);
1118 return str_replace('&slot=1&', '&slot=%%SLOT%%&', $html);
1122 * Return the contents of the question bank, to be displayed in the question-bank pop-up.
1124 * @param \mod_quiz\question\bank\custom_view $questionbank the question bank view object.
1125 * @param array $pagevars the variables from {@link \question_edit_setup()}.
1126 * @return string HTML to output / send back in response to an AJAX request.
1128 public function question_bank_contents(\mod_quiz\question\bank\custom_view
$questionbank, array $pagevars) {
1130 $qbank = $questionbank->render('editq', $pagevars['qpage'], $pagevars['qperpage'],
1131 $pagevars['cat'], $pagevars['recurse'], $pagevars['showhidden'], $pagevars['qbshowtext']);
1132 return html_writer
::div(html_writer
::div($qbank, 'bd'), 'questionbankformforpopup');