Merge branch 'MDL-68772-master' of https://github.com/Clyxz/moodle
[moodle.git] / mod / quiz / classes / output / edit_renderer.php
CommitLineData
e1a2d0d9
CC
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
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.
8//
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.
13//
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/>.
16
17/**
18 * Renderer outputting the quiz editing UI.
19 *
20 * @package mod_quiz
21 * @copyright 2013 The Open University.
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25namespace mod_quiz\output;
26defined('MOODLE_INTERNAL') || die();
27
28use \mod_quiz\structure;
29use \html_writer;
91898889 30use renderable;
e1a2d0d9
CC
31
32/**
33 * Renderer outputting the quiz editing UI.
34 *
35 * @copyright 2013 The Open University.
36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37 * @since Moodle 2.7
38 */
39class edit_renderer extends \plugin_renderer_base {
40
62e72577
JP
41 /** @var string The toggle group name of the checkboxes for the toggle-all functionality. */
42 protected $togglegroup = 'quiz-questions';
43
e1a2d0d9
CC
44 /**
45 * Render the edit page
46 *
47 * @param \quiz $quizobj object containing all the quiz settings information.
48 * @param structure $structure object containing the structure of the quiz.
49 * @param \question_edit_contexts $contexts the relevant question bank contexts.
50 * @param \moodle_url $pageurl the canonical URL of this page.
51 * @param array $pagevars the variables from {@link question_edit_setup()}.
52 * @return string HTML to output.
53 */
54 public function edit_page(\quiz $quizobj, structure $structure,
55 \question_edit_contexts $contexts, \moodle_url $pageurl, array $pagevars) {
56 $output = '';
57
58 // Page title.
59 $output .= $this->heading_with_help(get_string('editingquizx', 'quiz',
60 format_string($quizobj->get_quiz_name())), 'editingquiz', 'quiz', '',
61 get_string('basicideasofquiz', 'quiz'), 2);
62
63 // Information at the top.
64 $output .= $this->quiz_state_warnings($structure);
f37cffb6
CC
65
66 $output .= html_writer::start_div('mod_quiz-edit-top-controls');
398f0ae1
MH
67
68 $output .= html_writer::start_div('d-flex justify-content-between flex-wrap mb-1');
69 $output .= html_writer::start_div('d-flex flex-column justify-content-around');
e1a2d0d9 70 $output .= $this->quiz_information($structure);
398f0ae1 71 $output .= html_writer::end_tag('div');
441d284a 72 $output .= $this->maximum_grade_input($structure, $pageurl);
398f0ae1 73 $output .= html_writer::end_tag('div');
f37cffb6 74
398f0ae1 75 $output .= html_writer::start_div('d-flex justify-content-between flex-wrap mb-1');
f37cffb6 76 $output .= html_writer::start_div('mod_quiz-edit-action-buttons btn-group edit-toolbar', ['role' => 'group']);
e1a2d0d9 77 $output .= $this->repaginate_button($structure, $pageurl);
f37cffb6
CC
78 $output .= $this->selectmultiple_button($structure);
79 $output .= html_writer::end_tag('div');
80
398f0ae1 81 $output .= html_writer::start_div('d-flex flex-column justify-content-around');
e1a2d0d9 82 $output .= $this->total_marks($quizobj->get_quiz());
398f0ae1
MH
83 $output .= html_writer::end_tag('div');
84 $output .= html_writer::end_tag('div');
e1a2d0d9 85
f37cffb6
CC
86 $output .= $this->selectmultiple_controls($structure);
87 $output .= html_writer::end_tag('div');
88
e1a2d0d9 89 // Show the questions organised into sections and pages.
6d19eb4b 90 $output .= $this->start_section_list($structure);
e1a2d0d9 91
5d949702
K
92 foreach ($structure->get_sections() as $section) {
93 $output .= $this->start_section($structure, $section);
e1a2d0d9 94 $output .= $this->questions_in_section($structure, $section, $contexts, $pagevars, $pageurl);
5d949702
K
95
96 if ($structure->is_last_section($section)) {
e1a2d0d9
CC
97 $output .= \html_writer::start_div('last-add-menu');
98 $output .= html_writer::tag('span', $this->add_menu_actions($structure, 0,
99 $pageurl, $contexts, $pagevars), array('class' => 'add-menu-outer'));
100 $output .= \html_writer::end_div();
101 }
5d949702 102
e1a2d0d9
CC
103 $output .= $this->end_section();
104 }
105
106 $output .= $this->end_section_list();
107
32498c28 108 // Initialise the JavaScript.
441d284a 109 $this->initialise_editing_javascript($structure, $contexts, $pagevars, $pageurl);
e1a2d0d9
CC
110
111 // Include the contents of any other popups required.
112 if ($structure->can_be_edited()) {
1e46fb42 113 $thiscontext = $contexts->lowest();
171d7919 114 $this->page->requires->js_call_amd('mod_quiz/quizquestionbank', 'init', [
1e46fb42 115 $thiscontext->id
171d7919 116 ]);
e1a2d0d9 117
1e46fb42
RW
118 $this->page->requires->js_call_amd('mod_quiz/add_random_question', 'init', [
119 $thiscontext->id,
120 $pagevars['cat'],
121 $pageurl->out_as_local_url(true),
122 $pageurl->param('cmid')
123 ]);
e1a2d0d9
CC
124
125 // Include the question chooser.
126 $output .= $this->question_chooser();
e1a2d0d9
CC
127 }
128
129 return $output;
130 }
131
132 /**
133 * Render any warnings that might be required about the state of the quiz,
134 * e.g. if it has been attempted, or if the shuffle questions option is
135 * turned on.
136 *
137 * @param structure $structure the quiz structure.
138 * @return string HTML to output.
139 */
140 public function quiz_state_warnings(structure $structure) {
141 $warnings = $structure->get_edit_page_warnings();
142
143 if (empty($warnings)) {
144 return '';
145 }
146
147 $output = array();
148 foreach ($warnings as $warning) {
149 $output[] = \html_writer::tag('p', $warning);
150 }
151 return $this->box(implode("\n", $output), 'statusdisplay');
152 }
153
154 /**
155 * Render the status bar.
156 *
157 * @param structure $structure the quiz structure.
158 * @return string HTML to output.
159 */
160 public function quiz_information(structure $structure) {
161 list($currentstatus, $explanation) = $structure->get_dates_summary();
162
163 $output = html_writer::span(
164 get_string('numquestionsx', 'quiz', $structure->get_question_count()),
165 'numberofquestions') . ' | ' .
166 html_writer::span($currentstatus, 'quizopeningstatus',
167 array('title' => $explanation));
168
169 return html_writer::div($output, 'statusbar');
170 }
171
172 /**
173 * Render the form for setting a quiz' overall grade
174 *
441d284a 175 * @param structure $structure the quiz structure.
e1a2d0d9
CC
176 * @param \moodle_url $pageurl the canonical URL of this page.
177 * @return string HTML to output.
178 */
441d284a 179 public function maximum_grade_input($structure, \moodle_url $pageurl) {
e1a2d0d9
CC
180 $output = '';
181 $output .= html_writer::start_div('maxgrade');
182 $output .= html_writer::start_tag('form', array('method' => 'post', 'action' => 'edit.php',
12d6dfc1 183 'class' => 'quizsavegradesform form-inline'));
e1a2d0d9
CC
184 $output .= html_writer::start_tag('fieldset', array('class' => 'invisiblefieldset'));
185 $output .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
186 $output .= html_writer::input_hidden_params($pageurl);
030d8f22
TH
187 $output .= html_writer::tag('label', get_string('maximumgrade') . ' ',
188 array('for' => 'inputmaxgrade'));
189 $output .= html_writer::empty_tag('input', array('type' => 'text', 'id' => 'inputmaxgrade',
441d284a 190 'name' => 'maxgrade', 'size' => ($structure->get_decimal_places_for_grades() + 2),
12d6dfc1
DW
191 'value' => $structure->formatted_quiz_grade(),
192 'class' => 'form-control'));
5cac5fa4 193 $output .= html_writer::empty_tag('input', array('type' => 'submit', 'class' => 'btn btn-secondary ml-1',
e1a2d0d9
CC
194 'name' => 'savechanges', 'value' => get_string('save', 'quiz')));
195 $output .= html_writer::end_tag('fieldset');
196 $output .= html_writer::end_tag('form');
197 $output .= html_writer::end_tag('div');
198 return $output;
199 }
200
201 /**
202 * Return the repaginate button
203 * @param structure $structure the structure of the quiz being edited.
204 * @param \moodle_url $pageurl the canonical URL of this page.
205 * @return string HTML to output.
206 */
207 protected function repaginate_button(structure $structure, \moodle_url $pageurl) {
e1a2d0d9
CC
208 $header = html_writer::tag('span', get_string('repaginatecommand', 'quiz'), array('class' => 'repaginatecommand'));
209 $form = $this->repaginate_form($structure, $pageurl);
e1a2d0d9
CC
210
211 $buttonoptions = array(
212 'type' => 'submit',
213 'name' => 'repaginate',
214 'id' => 'repaginatecommand',
215 'value' => get_string('repaginatecommand', 'quiz'),
398f0ae1 216 'class' => 'btn btn-secondary',
f37cffb6
CC
217 'data-header' => $header,
218 'data-form' => $form,
e1a2d0d9
CC
219 );
220 if (!$structure->can_be_repaginated()) {
221 $buttonoptions['disabled'] = 'disabled';
222 } else {
6bafb66c 223 $this->page->requires->js_call_amd('mod_quiz/repaginate', 'init');
e1a2d0d9
CC
224 }
225
f37cffb6 226 return html_writer::empty_tag('input', $buttonoptions);
5d803e53
LB
227 }
228
229 /**
f37cffb6
CC
230 * Generate the bulk action button.
231 *
5d803e53
LB
232 * @param structure $structure the structure of the quiz being edited.
233 * @return string HTML to output.
234 */
f37cffb6 235 protected function selectmultiple_button(structure $structure) {
5d803e53
LB
236 $buttonoptions = array(
237 'type' => 'button',
f37cffb6
CC
238 'name' => 'selectmultiple',
239 'id' => 'selectmultiplecommand',
240 'value' => get_string('selectmultipleitems', 'quiz'),
398f0ae1 241 'class' => 'btn btn-secondary'
5d803e53 242 );
f37cffb6 243 if (!$structure->can_be_edited()) {
5d803e53
LB
244 $buttonoptions['disabled'] = 'disabled';
245 }
f37cffb6
CC
246
247 return html_writer::tag('button', get_string('selectmultipleitems', 'quiz'), $buttonoptions);
248 }
249
250 /**
251 * Generate the controls that appear when the bulk action button is pressed.
252 *
253 * @param structure $structure the structure of the quiz being edited.
254 * @return string HTML to output.
255 */
256 protected function selectmultiple_controls(structure $structure) {
257 $output = '';
258
259 // Bulk action button delete and bulk action button cancel.
260 $buttondeleteoptions = array(
261 'type' => 'button',
262 'id' => 'selectmultipledeletecommand',
263 'value' => get_string('deleteselected', 'mod_quiz'),
62e72577
JP
264 'class' => 'btn btn-secondary',
265 'data-action' => 'toggle',
266 'data-togglegroup' => $this->togglegroup,
267 'data-toggle' => 'action',
268 'disabled' => true
f37cffb6
CC
269 );
270 $buttoncanceloptions = array(
271 'type' => 'button',
272 'id' => 'selectmultiplecancelcommand',
273 'value' => get_string('cancel', 'moodle'),
274 'class' => 'btn btn-secondary'
275 );
276
277 $groupoptions = array(
62e72577 278 'class' => 'btn-group selectmultiplecommand actions m-1',
f37cffb6
CC
279 'role' => 'group'
280 );
281
282 $output .= html_writer::tag('div',
283 html_writer::tag('button', get_string('deleteselected', 'mod_quiz'), $buttondeleteoptions) .
284 " " .
285 html_writer::tag('button', get_string('cancel', 'moodle'),
286 $buttoncanceloptions), $groupoptions);
287
288 $toolbaroptions = array(
62e72577 289 'class' => 'btn-toolbar m-1',
f37cffb6
CC
290 'role' => 'toolbar',
291 'aria-label' => get_string('selectmultipletoolbar', 'quiz'),
292 );
293
294 // Select all/deselect all questions.
62e72577
JP
295 $selectallid = 'questionselectall';
296 $selectalltext = get_string('selectall', 'moodle');
297 $deselectalltext = get_string('deselectall', 'moodle');
298 $mastercheckbox = new \core\output\checkbox_toggleall($this->togglegroup, true, [
299 'id' => $selectallid,
300 'name' => $selectallid,
301 'value' => 1,
302 'label' => $selectalltext,
303 'selectall' => $selectalltext,
304 'deselectall' => $deselectalltext,
305 ], true);
306
307 $selectdeselect = html_writer::div($this->render($mastercheckbox), 'selectmultiplecommandbuttons');
308 $output .= html_writer::tag('div', $selectdeselect, $toolbaroptions);
f37cffb6 309 return $output;
e1a2d0d9
CC
310 }
311
312 /**
313 * Return the repaginate form
314 * @param structure $structure the structure of the quiz being edited.
315 * @param \moodle_url $pageurl the canonical URL of this page.
316 * @return string HTML to output.
317 */
318 protected function repaginate_form(structure $structure, \moodle_url $pageurl) {
319 $perpage = array();
320 $perpage[0] = get_string('allinone', 'quiz');
321 for ($i = 1; $i <= 50; ++$i) {
322 $perpage[$i] = $i;
323 }
324
325 $hiddenurl = clone($pageurl);
326 $hiddenurl->param('sesskey', sesskey());
327
328 $select = html_writer::select($perpage, 'questionsperpage',
12d6dfc1 329 $structure->get_questions_per_page(), false, array('class' => 'custom-select'));
e1a2d0d9 330
12d6dfc1
DW
331 $buttonattributes = array(
332 'type' => 'submit',
333 'name' => 'repaginate',
334 'value' => get_string('go'),
5cac5fa4 335 'class' => 'btn btn-secondary ml-1'
12d6dfc1 336 );
e1a2d0d9
CC
337
338 $formcontent = html_writer::tag('form', html_writer::div(
339 html_writer::input_hidden_params($hiddenurl) .
340 get_string('repaginate', 'quiz', $select) .
341 html_writer::empty_tag('input', $buttonattributes)
342 ), array('action' => 'edit.php', 'method' => 'post'));
343
344 return html_writer::div($formcontent, '', array('id' => 'repaginatedialog'));
345 }
346
347 /**
348 * Render the total marks available for the quiz.
349 *
350 * @param \stdClass $quiz the quiz settings from the database.
351 * @return string HTML to output.
352 */
353 public function total_marks($quiz) {
354 $totalmark = html_writer::span(quiz_format_grade($quiz, $quiz->sumgrades), 'mod_quiz_summarks');
355 return html_writer::tag('span',
356 get_string('totalmarksx', 'quiz', $totalmark),
357 array('class' => 'totalpoints'));
358 }
359
360 /**
361 * Generate the starting container html for the start of a list of sections
6d19eb4b 362 * @param structure $structure the structure of the quiz being edited.
e1a2d0d9
CC
363 * @return string HTML to output.
364 */
6d19eb4b
TH
365 protected function start_section_list(structure $structure) {
366 $class = 'slots';
367 if ($structure->get_section_count() == 1) {
368 $class .= ' only-one-section';
369 }
370 return html_writer::start_tag('ul', array('class' => $class));
e1a2d0d9
CC
371 }
372
373 /**
374 * Generate the closing container html for the end of a list of sections
375 * @return string HTML to output.
376 */
377 protected function end_section_list() {
378 return html_writer::end_tag('ul');
379 }
380
381 /**
382 * Display the start of a section, before the questions.
383 *
5d949702 384 * @param structure $structure the structure of the quiz being edited.
e1a2d0d9
CC
385 * @param \stdClass $section The quiz_section entry from DB
386 * @return string HTML to output.
387 */
5d949702 388 protected function start_section($structure, $section) {
e1a2d0d9
CC
389
390 $output = '';
5d949702 391
e1a2d0d9 392 $sectionstyle = '';
5d949702
K
393 if ($structure->is_only_one_slot_in_section($section)) {
394 $sectionstyle = ' only-has-one-slot';
395 }
e1a2d0d9
CC
396
397 $output .= html_writer::start_tag('li', array('id' => 'section-'.$section->id,
398 'class' => 'section main clearfix'.$sectionstyle, 'role' => 'region',
399 'aria-label' => $section->heading));
400
e1a2d0d9
CC
401 $output .= html_writer::start_div('content');
402
5d949702
K
403 $output .= html_writer::start_div('section-heading');
404
405 $headingtext = $this->heading(html_writer::span(
406 html_writer::span($section->heading, 'instancesection'), 'sectioninstance'), 3);
407
408 if (!$structure->can_be_edited()) {
409 $editsectionheadingicon = '';
410 } else {
411 $editsectionheadingicon = html_writer::link(new \moodle_url('#'),
412 $this->pix_icon('t/editstring', get_string('sectionheadingedit', 'quiz', $section->heading),
413 'moodle', array('class' => 'editicon visibleifjs')),
414 array('class' => 'editing_section', 'data-action' => 'edit_section_title'));
415 }
416 $output .= html_writer::div($headingtext . $editsectionheadingicon, 'instancesectioncontainer');
417
418 if (!$structure->is_first_section($section) && $structure->can_be_edited()) {
419 $output .= $this->section_remove_icon($section);
420 }
421 $output .= $this->section_shuffle_questions($structure, $section);
422
423 $output .= html_writer::end_div($output, 'section-heading');
424
e1a2d0d9
CC
425 return $output;
426 }
427
428 /**
5d949702 429 * Display a checkbox for shuffling question within a section.
e1a2d0d9 430 *
5d949702
K
431 * @param structure $structure object containing the structure of the quiz.
432 * @param \stdClass $section data from the quiz_section table.
e1a2d0d9
CC
433 * @return string HTML to output.
434 */
5d949702
K
435 public function section_shuffle_questions(structure $structure, $section) {
436 $checkboxattributes = array(
437 'type' => 'checkbox',
438 'id' => 'shuffle-' . $section->id,
439 'value' => 1,
440 'data-action' => 'shuffle_questions',
441 'class' => 'cm-edit-action',
442 );
e1a2d0d9 443
5d949702
K
444 if (!$structure->can_be_edited()) {
445 $checkboxattributes['disabled'] = 'disabled';
446 }
447 if ($section->shufflequestions) {
448 $checkboxattributes['checked'] = 'checked';
449 }
450
451 if ($structure->is_first_section($section)) {
452 $help = $this->help_icon('shufflequestions', 'quiz');
453 } else {
454 $help = '';
455 }
456
aa6b85d2 457 $helpspan = html_writer::span($help, 'shuffle-help-tip');
5d949702
K
458 $progressspan = html_writer::span('', 'shuffle-progress');
459 $checkbox = html_writer::empty_tag('input', $checkboxattributes);
aa6b85d2 460 $label = html_writer::label(get_string('shufflequestions', 'quiz'),
5d949702 461 $checkboxattributes['id'], false);
aa6b85d2 462 return html_writer::span($progressspan . $checkbox . $label. ' ' . $helpspan,
5d949702 463 'instanceshufflequestions', array('data-action' => 'shuffle_questions'));
e1a2d0d9
CC
464 }
465
466 /**
5d949702 467 * Display the end of a section, after the questions.
e1a2d0d9 468 *
e1a2d0d9
CC
469 * @return string HTML to output.
470 */
5d949702
K
471 protected function end_section() {
472 $output = html_writer::end_tag('div');
473 $output .= html_writer::end_tag('li');
474
475 return $output;
e1a2d0d9
CC
476 }
477
478 /**
5d949702 479 * Render an icon to remove a section from the quiz.
e1a2d0d9 480 *
5d949702 481 * @param object $section the section to be removed.
e1a2d0d9
CC
482 * @return string HTML to output.
483 */
5d949702
K
484 public function section_remove_icon($section) {
485 $title = get_string('sectionheadingremove', 'quiz', $section->heading);
486 $url = new \moodle_url('/mod/quiz/edit.php',
487 array('sesskey' => sesskey(), 'removesection' => '1', 'sectionid' => $section->id));
488 $image = $this->pix_icon('t/delete', $title);
489 return $this->action_link($url, $image, null, array(
490 'class' => 'cm-edit-action editing_delete', 'data-action' => 'deletesection'));
e1a2d0d9
CC
491 }
492
493 /**
494 * Renders HTML to display the questions in a section of the quiz.
495 *
496 * This function calls {@link core_course_renderer::quiz_section_question()}
497 *
498 * @param structure $structure object containing the structure of the quiz.
499 * @param \stdClass $section information about the section.
500 * @param \question_edit_contexts $contexts the relevant question bank contexts.
501 * @param array $pagevars the variables from {@link \question_edit_setup()}.
502 * @param \moodle_url $pageurl the canonical URL of this page.
503 * @return string HTML to output.
504 */
505 public function questions_in_section(structure $structure, $section,
506 $contexts, $pagevars, $pageurl) {
507
508 $output = '';
441d284a
TH
509 foreach ($structure->get_slots_in_section($section->id) as $slot) {
510 $output .= $this->question_row($structure, $slot, $contexts, $pagevars, $pageurl);
e1a2d0d9 511 }
e1a2d0d9
CC
512 return html_writer::tag('ul', $output, array('class' => 'section img-text'));
513 }
514
515 /**
516 * Displays one question with the surrounding controls.
517 *
518 * @param structure $structure object containing the structure of the quiz.
441d284a 519 * @param int $slot which slot we are outputting.
e1a2d0d9
CC
520 * @param \question_edit_contexts $contexts the relevant question bank contexts.
521 * @param array $pagevars the variables from {@link \question_edit_setup()}.
522 * @param \moodle_url $pageurl the canonical URL of this page.
523 * @return string HTML to output.
524 */
441d284a 525 public function question_row(structure $structure, $slot, $contexts, $pagevars, $pageurl) {
e1a2d0d9
CC
526 $output = '';
527
441d284a 528 $output .= $this->page_row($structure, $slot, $contexts, $pagevars, $pageurl);
a69f81f0
CC
529
530 // Page split/join icon.
531 $joinhtml = '';
5d949702
K
532 if ($structure->can_be_edited() && !$structure->is_last_slot_in_quiz($slot) &&
533 !$structure->is_last_slot_in_section($slot)) {
441d284a 534 $joinhtml = $this->page_split_join_button($structure, $slot);
a69f81f0 535 }
a69f81f0 536 // Question HTML.
441d284a
TH
537 $questionhtml = $this->question($structure, $slot, $pageurl);
538 $qtype = $structure->get_question_type_for_slot($slot);
539 $questionclasses = 'activity ' . $qtype . ' qtype_' . $qtype . ' slot';
a69f81f0
CC
540
541 $output .= html_writer::tag('li', $questionhtml . $joinhtml,
441d284a
TH
542 array('class' => $questionclasses, 'id' => 'slot-' . $structure->get_slot_id_for_slot($slot),
543 'data-canfinish' => $structure->can_finish_during_the_attempt($slot)));
a69f81f0
CC
544
545 return $output;
546 }
547
548 /**
549 * Displays one question with the surrounding controls.
550 *
551 * @param structure $structure object containing the structure of the quiz.
441d284a 552 * @param int $slot the first slot on the page we are outputting.
a69f81f0
CC
553 * @param \question_edit_contexts $contexts the relevant question bank contexts.
554 * @param array $pagevars the variables from {@link \question_edit_setup()}.
555 * @param \moodle_url $pageurl the canonical URL of this page.
556 * @return string HTML to output.
557 */
441d284a 558 public function page_row(structure $structure, $slot, $contexts, $pagevars, $pageurl) {
a69f81f0
CC
559 $output = '';
560
441d284a
TH
561 $pagenumber = $structure->get_page_number_for_slot($slot);
562
5d949702
K
563 // Put page in a heading for accessibility and styling.
564 $page = $this->heading(get_string('page') . ' ' . $pagenumber, 4);
e1a2d0d9 565
441d284a 566 if ($structure->is_first_slot_on_page($slot)) {
e1a2d0d9
CC
567 // Add the add-menu at the page level.
568 $addmenu = html_writer::tag('span', $this->add_menu_actions($structure,
441d284a 569 $pagenumber, $pageurl, $contexts, $pagevars),
e1a2d0d9
CC
570 array('class' => 'add-menu-outer'));
571
572 $addquestionform = $this->add_question_form($structure,
441d284a 573 $pagenumber, $pageurl, $pagevars);
e1a2d0d9
CC
574
575 $output .= html_writer::tag('li', $page . $addmenu . $addquestionform,
441d284a 576 array('class' => 'pagenumber activity yui3-dd-drop page', 'id' => 'page-' . $pagenumber));
e1a2d0d9
CC
577 }
578
e1a2d0d9
CC
579 return $output;
580 }
581
582 /**
583 * Returns the add menu that is output once per page.
584 * @param structure $structure object containing the structure of the quiz.
585 * @param int $page the page number that this menu will add to.
586 * @param \moodle_url $pageurl the canonical URL of this page.
587 * @param \question_edit_contexts $contexts the relevant question bank contexts.
588 * @param array $pagevars the variables from {@link \question_edit_setup()}.
589 * @return string HTML to output.
590 */
591 public function add_menu_actions(structure $structure, $page, \moodle_url $pageurl,
592 \question_edit_contexts $contexts, array $pagevars) {
593
594 $actions = $this->edit_menu_actions($structure, $page, $pageurl, $pagevars);
595 if (empty($actions)) {
596 return '';
597 }
598 $menu = new \action_menu();
5d949702 599 $menu->set_alignment(\action_menu::TR, \action_menu::TR);
77dca4d0 600 $menu->set_constraint('.mod-quiz-edit-content');
e1a2d0d9
CC
601 $trigger = html_writer::tag('span', get_string('add', 'quiz'), array('class' => 'add-menu'));
602 $menu->set_menu_trigger($trigger);
8a375d27
SH
603 // The menu appears within an absolutely positioned element causing width problems.
604 // Make sure no-wrap is set so that we don't get a squashed menu.
605 $menu->set_nowrap_on_items(true);
e1a2d0d9
CC
606
607 // Disable the link if quiz has attempts.
608 if (!$structure->can_be_edited()) {
609 return $this->render($menu);
610 }
611
612 foreach ($actions as $action) {
613 if ($action instanceof \action_menu_link) {
614 $action->add_class('add-menu');
615 }
616 $menu->add($action);
617 }
618 $menu->attributes['class'] .= ' page-add-actions commands';
619
620 // Prioritise the menu ahead of all other actions.
621 $menu->prioritise = true;
622
623 return $this->render($menu);
624 }
625
626 /**
627 * Returns the list of actions to go in the add menu.
628 * @param structure $structure object containing the structure of the quiz.
629 * @param int $page the page number that this menu will add to.
630 * @param \moodle_url $pageurl the canonical URL of this page.
631 * @param array $pagevars the variables from {@link \question_edit_setup()}.
632 * @return array the actions.
633 */
634 public function edit_menu_actions(structure $structure, $page,
635 \moodle_url $pageurl, array $pagevars) {
636 $questioncategoryid = question_get_category_id_from_pagevars($pagevars);
637 static $str;
638 if (!isset($str)) {
5d949702 639 $str = get_strings(array('addasection', 'addaquestion', 'addarandomquestion',
e1a2d0d9
CC
640 'addarandomselectedquestion', 'questionbank'), 'quiz');
641 }
642
643 // Get section, page, slotnumber and maxmark.
644 $actions = array();
645
646 // Add a new question to the quiz.
647 $returnurl = new \moodle_url($pageurl, array('addonpage' => $page));
648 $params = array('returnurl' => $returnurl->out_as_local_url(false),
649 'cmid' => $structure->get_cmid(), 'category' => $questioncategoryid,
650 'addonpage' => $page, 'appendqnumstring' => 'addquestion');
651
652 $actions['addaquestion'] = new \action_menu_link_secondary(
653 new \moodle_url('/question/addquestion.php', $params),
654 new \pix_icon('t/add', $str->addaquestion, 'moodle', array('class' => 'iconsmall', 'title' => '')),
655 $str->addaquestion, array('class' => 'cm-edit-action addquestion', 'data-action' => 'addquestion')
656 );
657
658 // Call question bank.
659 $icon = new \pix_icon('t/add', $str->questionbank, 'moodle', array('class' => 'iconsmall', 'title' => ''));
ca79762a
TH
660 if ($page) {
661 $title = get_string('addquestionfrombanktopage', 'quiz', $page);
662 } else {
663 $title = get_string('addquestionfrombankatend', 'quiz');
664 }
e1a2d0d9
CC
665 $attributes = array('class' => 'cm-edit-action questionbank',
666 'data-header' => $title, 'data-action' => 'questionbank', 'data-addonpage' => $page);
667 $actions['questionbank'] = new \action_menu_link_secondary($pageurl, $icon, $str->questionbank, $attributes);
668
669 // Add a random question.
38405407
SR
670 if ($structure->can_add_random_questions()) {
671 $returnurl = new \moodle_url('/mod/quiz/edit.php', array('cmid' => $structure->get_cmid(), 'data-addonpage' => $page));
672 $params = ['returnurl' => $returnurl, 'cmid' => $structure->get_cmid(), 'appendqnumstring' => 'addarandomquestion'];
673 $url = new \moodle_url('/mod/quiz/addrandom.php', $params);
674 $icon = new \pix_icon('t/add', $str->addarandomquestion, 'moodle', array('class' => 'iconsmall', 'title' => ''));
675 $attributes = array('class' => 'cm-edit-action addarandomquestion', 'data-action' => 'addarandomquestion');
676 if ($page) {
677 $title = get_string('addrandomquestiontopage', 'quiz', $page);
678 } else {
679 $title = get_string('addrandomquestionatend', 'quiz');
680 }
681 $attributes = array_merge(array('data-header' => $title, 'data-addonpage' => $page), $attributes);
682 $actions['addarandomquestion'] = new \action_menu_link_secondary($url, $icon, $str->addarandomquestion, $attributes);
ca79762a 683 }
e1a2d0d9 684
11f2cec1
LB
685 // Add a new section to the add_menu if possible. This is always added to the HTML
686 // then hidden with CSS when no needed, so that as things are re-ordered, etc. with
687 // Ajax it can be relevaled again when necessary.
688 $params = array('cmid' => $structure->get_cmid(), 'addsectionatpage' => $page);
689
690 $actions['addasection'] = new \action_menu_link_secondary(
691 new \moodle_url($pageurl, $params),
692 new \pix_icon('t/add', $str->addasection, 'moodle', array('class' => 'iconsmall', 'title' => '')),
693 $str->addasection, array('class' => 'cm-edit-action addasection', 'data-action' => 'addasection')
694 );
695
e1a2d0d9
CC
696 return $actions;
697 }
698
699 /**
700 * Render the form that contains the data for adding a new question to the quiz.
701 *
702 * @param structure $structure object containing the structure of the quiz.
703 * @param int $page the page number that this menu will add to.
704 * @param \moodle_url $pageurl the canonical URL of this page.
705 * @param array $pagevars the variables from {@link \question_edit_setup()}.
706 * @return string HTML to output.
707 */
708 protected function add_question_form(structure $structure, $page, \moodle_url $pageurl, array $pagevars) {
709
710 $questioncategoryid = question_get_category_id_from_pagevars($pagevars);
711
712 $output = html_writer::tag('input', null,
713 array('type' => 'hidden', 'name' => 'returnurl',
714 'value' => $pageurl->out_as_local_url(false, array('addonpage' => $page))));
715 $output .= html_writer::tag('input', null,
716 array('type' => 'hidden', 'name' => 'cmid', 'value' => $structure->get_cmid()));
717 $output .= html_writer::tag('input', null,
718 array('type' => 'hidden', 'name' => 'appendqnumstring', 'value' => 'addquestion'));
719 $output .= html_writer::tag('input', null,
720 array('type' => 'hidden', 'name' => 'category', 'value' => $questioncategoryid));
721
722 return html_writer::tag('form', html_writer::div($output),
723 array('class' => 'addnewquestion', 'method' => 'post',
724 'action' => new \moodle_url('/question/addquestion.php')));
725 }
726
727 /**
728 * Display a question.
729 *
730 * @param structure $structure object containing the structure of the quiz.
441d284a 731 * @param int $slot the first slot on the page we are outputting.
e1a2d0d9
CC
732 * @param \moodle_url $pageurl the canonical URL of this page.
733 * @return string HTML to output.
734 */
441d284a 735 public function question(structure $structure, $slot, \moodle_url $pageurl) {
e1a2d0d9 736 $output = '';
e1a2d0d9
CC
737 $output .= html_writer::start_tag('div');
738
739 if ($structure->can_be_edited()) {
441d284a 740 $output .= $this->question_move_icon($structure, $slot);
e1a2d0d9
CC
741 }
742
743 $output .= html_writer::start_div('mod-indent-outer');
62e72577
JP
744 $checkbox = new \core\output\checkbox_toggleall($this->togglegroup, false, [
745 'id' => 'selectquestion-' . $structure->get_displayed_number_for_slot($slot),
746 'name' => 'selectquestion[]',
747 'value' => $structure->get_displayed_number_for_slot($slot),
748 'classes' => 'select-multiple-checkbox',
749 ]);
750 $output .= $this->render($checkbox);
441d284a 751 $output .= $this->question_number($structure->get_displayed_number_for_slot($slot));
e1a2d0d9
CC
752
753 // This div is used to indent the content.
754 $output .= html_writer::div('', 'mod-indent');
755
756 // Display the link to the question (or do nothing if question has no url).
441d284a
TH
757 if ($structure->get_question_type_for_slot($slot) == 'random') {
758 $questionname = $this->random_question($structure, $slot, $pageurl);
e1a2d0d9 759 } else {
441d284a 760 $questionname = $this->question_name($structure, $slot, $pageurl);
e1a2d0d9
CC
761 }
762
763 // Start the div for the activity title, excluding the edit icons.
764 $output .= html_writer::start_div('activityinstance');
765 $output .= $questionname;
766
767 // Closing the tag which contains everything but edit icons. Content part of the module should not be part of this.
768 $output .= html_writer::end_tag('div'); // .activityinstance.
769
770 // Action icons.
771 $questionicons = '';
441d284a 772 $questionicons .= $this->question_preview_icon($structure->get_quiz(), $structure->get_question_in_slot($slot));
e1a2d0d9 773 if ($structure->can_be_edited()) {
441d284a 774 $questionicons .= $this->question_remove_icon($structure, $slot, $pageurl);
e1a2d0d9 775 }
441d284a 776 $questionicons .= $this->marked_out_of_field($structure, $slot);
e1a2d0d9 777 $output .= html_writer::span($questionicons, 'actions'); // Required to add js spinner icon.
441d284a
TH
778 if ($structure->can_be_edited()) {
779 $output .= $this->question_dependency_icon($structure, $slot);
780 }
e1a2d0d9
CC
781
782 // End of indentation div.
783 $output .= html_writer::end_tag('div');
784 $output .= html_writer::end_tag('div');
785
786 return $output;
787 }
788
789 /**
790 * Render the move icon.
791 *
441d284a
TH
792 * @param structure $structure object containing the structure of the quiz.
793 * @param int $slot the first slot on the page we are outputting.
794 * @return string The markup for the move action.
e1a2d0d9 795 */
441d284a 796 public function question_move_icon(structure $structure, $slot) {
e1a2d0d9
CC
797 return html_writer::link(new \moodle_url('#'),
798 $this->pix_icon('i/dragdrop', get_string('move'), 'moodle', array('class' => 'iconsmall', 'title' => '')),
799 array('class' => 'editing_move', 'data-action' => 'move')
800 );
801 }
802
803 /**
804 * Output the question number.
805 * @param string $number The number, or 'i'.
806 * @return string HTML to output.
807 */
808 public function question_number($number) {
809 if (is_numeric($number)) {
f7785e42 810 $number = html_writer::span(get_string('question'), 'accesshide') . ' ' . $number;
e1a2d0d9
CC
811 }
812 return html_writer::tag('span', $number, array('class' => 'slotnumber'));
813 }
814
815 /**
816 * Render the preview icon.
817 *
818 * @param \stdClass $quiz the quiz settings from the database.
819 * @param \stdClass $question data from the question and quiz_slots tables.
820 * @param bool $label if true, show the preview question label after the icon
de3b803d 821 * @param int $variant which question variant to preview (optional).
e1a2d0d9
CC
822 * @return string HTML to output.
823 */
de3b803d
TH
824 public function question_preview_icon($quiz, $question, $label = null, $variant = null) {
825 $url = quiz_question_preview_url($quiz, $question, $variant);
e1a2d0d9
CC
826
827 // Do we want a label?
828 $strpreviewlabel = '';
829 if ($label) {
830 $strpreviewlabel = ' ' . get_string('preview', 'quiz');
831 }
832
833 // Build the icon.
834 $strpreviewquestion = get_string('previewquestion', 'quiz');
835 $image = $this->pix_icon('t/preview', $strpreviewquestion);
836
837 $action = new \popup_action('click', $url, 'questionpreview',
838 question_preview_popup_params());
839
840 return $this->action_link($url, $image . $strpreviewlabel, $action,
841 array('title' => $strpreviewquestion, 'class' => 'preview'));
842 }
843
844 /**
845 * Render an icon to remove a question from the quiz.
846 *
441d284a
TH
847 * @param structure $structure object containing the structure of the quiz.
848 * @param int $slot the first slot on the page we are outputting.
e1a2d0d9
CC
849 * @param \moodle_url $pageurl the canonical URL of the edit page.
850 * @return string HTML to output.
851 */
441d284a
TH
852 public function question_remove_icon(structure $structure, $slot, $pageurl) {
853 $url = new \moodle_url($pageurl, array('sesskey' => sesskey(), 'remove' => $slot));
e1a2d0d9
CC
854 $strdelete = get_string('delete');
855
856 $image = $this->pix_icon('t/delete', $strdelete);
857
858 return $this->action_link($url, $image, null, array('title' => $strdelete,
859 'class' => 'cm-edit-action editing_delete', 'data-action' => 'delete'));
860 }
861
862 /**
863 * Display an icon to split or join two pages of the quiz.
864 *
441d284a
TH
865 * @param structure $structure object containing the structure of the quiz.
866 * @param int $slot the first slot on the page we are outputting.
e1a2d0d9
CC
867 * @return string HTML to output.
868 */
441d284a
TH
869 public function page_split_join_button($structure, $slot) {
870 $insertpagebreak = !$structure->is_last_slot_on_page($slot);
871 $url = new \moodle_url('repaginate.php', array('quizid' => $structure->get_quizid(),
872 'slot' => $slot, 'repag' => $insertpagebreak ? 2 : 1, 'sesskey' => sesskey()));
e1a2d0d9
CC
873
874 if ($insertpagebreak) {
a69f81f0 875 $title = get_string('addpagebreak', 'quiz');
3e6adcd6 876 $image = $this->image_icon('e/insert_page_break', $title);
a69f81f0 877 $action = 'addpagebreak';
e1a2d0d9 878 } else {
a69f81f0 879 $title = get_string('removepagebreak', 'quiz');
3e6adcd6 880 $image = $this->image_icon('e/remove_page_break', $title);
a69f81f0 881 $action = 'removepagebreak';
e1a2d0d9
CC
882 }
883
884 // Disable the link if quiz has attempts.
885 $disabled = null;
441d284a
TH
886 if (!$structure->can_be_edited()) {
887 $disabled = 'disabled';
e1a2d0d9
CC
888 }
889 return html_writer::span($this->action_link($url, $image, null, array('title' => $title,
a69f81f0 890 'class' => 'page_split_join cm-edit-action', 'disabled' => $disabled, 'data-action' => $action)),
f7785e42
K
891 'page_split_join_wrapper');
892 }
893
894 /**
441d284a
TH
895 * Display the icon for whether this question can only be seen if the previous
896 * one has been answered.
f7785e42 897 *
441d284a
TH
898 * @param structure $structure object containing the structure of the quiz.
899 * @param int $slot the first slot on the page we are outputting.
900 * @return string HTML to output.
f7785e42 901 */
441d284a
TH
902 public function question_dependency_icon($structure, $slot) {
903 $a = array(
904 'thisq' => $structure->get_displayed_number_for_slot($slot),
905 'previousq' => $structure->get_displayed_number_for_slot(max($slot - 1, 1)),
906 );
907 if ($structure->is_question_dependent_on_previous_slot($slot)) {
908 $title = get_string('questiondependencyremove', 'quiz', $a);
909 $image = $this->pix_icon('t/locked', get_string('questiondependsonprevious', 'quiz'),
910 'moodle', array('title' => ''));
911 $action = 'removedependency';
f7785e42 912 } else {
441d284a
TH
913 $title = get_string('questiondependencyadd', 'quiz', $a);
914 $image = $this->pix_icon('t/unlocked', get_string('questiondependencyfree', 'quiz'),
915 'moodle', array('title' => ''));
916 $action = 'adddependency';
f7785e42
K
917 }
918
919 // Disable the link if quiz has attempts.
920 $disabled = null;
441d284a
TH
921 if (!$structure->can_be_edited()) {
922 $disabled = 'disabled';
f7785e42 923 }
441d284a
TH
924 $extraclass = '';
925 if (!$structure->can_question_depend_on_previous_slot($slot)) {
926 $extraclass = ' question_dependency_cannot_depend';
927 }
928 return html_writer::span($this->action_link('#', $image, null, array('title' => $title,
929 'class' => 'cm-edit-action', 'disabled' => $disabled, 'data-action' => $action)),
930 'question_dependency_wrapper' . $extraclass);
e1a2d0d9
CC
931 }
932
933 /**
934 * Renders html to display a name with the link to the question on a quiz edit page
935 *
936 * If the user does not have permission to edi the question, it is rendered
937 * without a link
938 *
939 * @param structure $structure object containing the structure of the quiz.
441d284a 940 * @param int $slot which slot we are outputting.
e1a2d0d9
CC
941 * @param \moodle_url $pageurl the canonical URL of this page.
942 * @return string HTML to output.
943 */
441d284a 944 public function question_name(structure $structure, $slot, $pageurl) {
e1a2d0d9
CC
945 $output = '';
946
441d284a 947 $question = $structure->get_question_in_slot($slot);
e1a2d0d9
CC
948 $editurl = new \moodle_url('/question/question.php', array(
949 'returnurl' => $pageurl->out_as_local_url(),
950 'cmid' => $structure->get_cmid(), 'id' => $question->id));
951
952 $instancename = quiz_question_tostring($question);
953
954 $qtype = \question_bank::get_qtype($question->qtype, false);
955 $namestr = $qtype->local_name();
956
957 $icon = $this->pix_icon('icon', $namestr, $qtype->plugin_name(), array('title' => $namestr,
87174e0a 958 'class' => 'activityicon', 'alt' => ' ', 'role' => 'presentation'));
e1a2d0d9
CC
959
960 $editicon = $this->pix_icon('t/edit', '', 'moodle', array('title' => ''));
961
962 // Need plain question name without html tags for link title.
963 $title = shorten_text(format_string($question->name), 100);
964
965 // Display the link itself.
966 $activitylink = $icon . html_writer::tag('span', $editicon . $instancename, array('class' => 'instancename'));
967 $output .= html_writer::link($editurl, $activitylink,
968 array('title' => get_string('editquestion', 'quiz').' '.$title));
969
970 return $output;
971 }
972
973 /**
974 * Renders html to display a random question the link to edit the configuration
975 * and also to see that category in the question bank.
976 *
977 * @param structure $structure object containing the structure of the quiz.
6650c66b 978 * @param int $slotnumber which slot we are outputting.
e1a2d0d9
CC
979 * @param \moodle_url $pageurl the canonical URL of this page.
980 * @return string HTML to output.
981 */
6650c66b 982 public function random_question(structure $structure, $slotnumber, $pageurl) {
e1a2d0d9 983
6650c66b
SR
984 $question = $structure->get_question_in_slot($slotnumber);
985 $slot = $structure->get_slot_by_number($slotnumber);
8f0f0a59 986 $slottags = $structure->get_slot_tags_for_slot_id($slot->id);
6650c66b
SR
987 $editurl = new \moodle_url('/mod/quiz/editrandom.php',
988 array('returnurl' => $pageurl->out_as_local_url(), 'slotid' => $slot->id));
e1a2d0d9
CC
989
990 $temp = clone($question);
991 $temp->questiontext = '';
992 $instancename = quiz_question_tostring($temp);
993
994 $configuretitle = get_string('configurerandomquestion', 'quiz');
995 $qtype = \question_bank::get_qtype($question->qtype, false);
996 $namestr = $qtype->local_name();
997 $icon = $this->pix_icon('icon', $namestr, $qtype->plugin_name(), array('title' => $namestr,
998 'class' => 'icon activityicon', 'alt' => ' ', 'role' => 'presentation'));
999
1000 $editicon = $this->pix_icon('t/edit', $configuretitle, 'moodle', array('title' => ''));
8f0f0a59
RW
1001 $qbankurlparams = array(
1002 'cmid' => $structure->get_cmid(),
1003 'cat' => $question->category . ',' . $question->contextid,
1004 'recurse' => !empty($question->questiontext)
1005 );
1006
1007 foreach ($slottags as $index => $slottag) {
1008 $qbankurlparams["qtagids[{$index}]"] = $slottag->tagid;
1009 }
e1a2d0d9
CC
1010
1011 // If this is a random question, display a link to show the questions
1012 // selected from in the question bank.
8f0f0a59 1013 $qbankurl = new \moodle_url('/question/edit.php', $qbankurlparams);
e1a2d0d9
CC
1014 $qbanklink = ' ' . \html_writer::link($qbankurl,
1015 get_string('seequestions', 'quiz'), array('class' => 'mod_quiz_random_qbank_link'));
1016
1017 return html_writer::link($editurl, $icon . $editicon, array('title' => $configuretitle)) .
1018 ' ' . $instancename . ' ' . $qbanklink;
1019 }
1020
1021 /**
1022 * Display the 'marked out of' information for a question.
1023 * Along with the regrade action.
441d284a
TH
1024 * @param structure $structure object containing the structure of the quiz.
1025 * @param int $slot which slot we are outputting.
e1a2d0d9
CC
1026 * @return string HTML to output.
1027 */
441d284a
TH
1028 public function marked_out_of_field(structure $structure, $slot) {
1029 if (!$structure->is_real_question($slot)) {
97ef7ea2 1030 $output = html_writer::span('',
441d284a 1031 'instancemaxmark decimalplaces_' . $structure->get_decimal_places_for_question_marks());
97ef7ea2
TH
1032
1033 $output .= html_writer::span(
1034 $this->pix_icon('spacer', '', 'moodle', array('class' => 'editicon visibleifjs', 'title' => '')),
1035 'editing_maxmark');
1036 return html_writer::span($output, 'instancemaxmarkcontainer infoitem');
1037 }
1038
441d284a
TH
1039 $output = html_writer::span($structure->formatted_question_grade($slot),
1040 'instancemaxmark decimalplaces_' . $structure->get_decimal_places_for_question_marks(),
e1a2d0d9
CC
1041 array('title' => get_string('maxmark', 'quiz')));
1042
1043 $output .= html_writer::span(
1044 html_writer::link(
1045 new \moodle_url('#'),
97ef7ea2 1046 $this->pix_icon('t/editstring', '', 'moodle', array('class' => 'editicon visibleifjs', 'title' => '')),
e1a2d0d9
CC
1047 array(
1048 'class' => 'editing_maxmark',
1049 'data-action' => 'editmaxmark',
1050 'title' => get_string('editmaxmark', 'quiz'),
1051 )
1052 )
1053 );
1054 return html_writer::span($output, 'instancemaxmarkcontainer');
1055 }
1056
91898889
FM
1057 /**
1058 * Renders the question chooser.
1059 *
1060 * @param renderable
1061 * @return string
1062 */
1063 public function render_question_chooser(renderable $chooser) {
1064 return $this->render_from_template('mod_quiz/question_chooser', $chooser->export_for_template($this));
1065 }
1066
e1a2d0d9
CC
1067 /**
1068 * Render the question type chooser dialogue.
1069 * @return string HTML to output.
1070 */
1071 public function question_chooser() {
91898889
FM
1072 $chooser = \mod_quiz\output\question_chooser::get($this->page->course, [], null);
1073 $container = html_writer::div($this->render($chooser), '', array('id' => 'qtypechoicecontainer'));
e1a2d0d9
CC
1074 return html_writer::div($container, 'createnewquestion');
1075 }
1076
1077 /**
1078 * Render the contents of the question bank pop-up in its initial state,
1079 * when it just contains a loading progress indicator.
1080 * @return string HTML to output.
1081 */
1082 public function question_bank_loading() {
8857c715 1083 return html_writer::div($this->pix_icon('i/loading', get_string('loading')), 'questionbankloading');
e1a2d0d9
CC
1084 }
1085
e1a2d0d9
CC
1086 /**
1087 * Initialise the JavaScript for the general editing. (JavaScript for popups
1088 * is handled with the specific code for those.)
1089 *
a69f81f0
CC
1090 * @param structure $structure object containing the structure of the quiz.
1091 * @param \question_edit_contexts $contexts the relevant question bank contexts.
1092 * @param array $pagevars the variables from {@link \question_edit_setup()}.
1093 * @param \moodle_url $pageurl the canonical URL of this page.
e1a2d0d9
CC
1094 * @return bool Always returns true
1095 */
441d284a 1096 protected function initialise_editing_javascript(structure $structure,
a69f81f0 1097 \question_edit_contexts $contexts, array $pagevars, \moodle_url $pageurl) {
e1a2d0d9
CC
1098
1099 $config = new \stdClass();
1100 $config->resourceurl = '/mod/quiz/edit_rest.php';
1101 $config->sectionurl = '/mod/quiz/edit_rest.php';
1102 $config->pageparams = array();
441d284a 1103 $config->questiondecimalpoints = $structure->get_decimal_places_for_question_marks();
a69f81f0 1104 $config->pagehtml = $this->new_page_template($structure, $contexts, $pagevars, $pageurl);
441d284a 1105 $config->addpageiconhtml = $this->add_page_icon_template($structure);
e1a2d0d9 1106
e1a2d0d9
CC
1107 $this->page->requires->yui_module('moodle-mod_quiz-toolboxes',
1108 'M.mod_quiz.init_resource_toolbox',
1109 array(array(
441d284a
TH
1110 'courseid' => $structure->get_courseid(),
1111 'quizid' => $structure->get_quizid(),
e1a2d0d9
CC
1112 'ajaxurl' => $config->resourceurl,
1113 'config' => $config,
1114 ))
1115 );
a69f81f0
CC
1116 unset($config->pagehtml);
1117 unset($config->addpageiconhtml);
1118
5d803e53 1119 $this->page->requires->strings_for_js(array('areyousureremoveselected'), 'quiz');
e1a2d0d9
CC
1120 $this->page->requires->yui_module('moodle-mod_quiz-toolboxes',
1121 'M.mod_quiz.init_section_toolbox',
1122 array(array(
441d284a
TH
1123 'courseid' => $structure,
1124 'quizid' => $structure->get_quizid(),
e1a2d0d9
CC
1125 'ajaxurl' => $config->sectionurl,
1126 'config' => $config,
1127 ))
1128 );
1129
e1a2d0d9
CC
1130 $this->page->requires->yui_module('moodle-mod_quiz-dragdrop', 'M.mod_quiz.init_section_dragdrop',
1131 array(array(
441d284a
TH
1132 'courseid' => $structure,
1133 'quizid' => $structure->get_quizid(),
e1a2d0d9
CC
1134 'ajaxurl' => $config->sectionurl,
1135 'config' => $config,
1136 )), null, true);
1137
1138 $this->page->requires->yui_module('moodle-mod_quiz-dragdrop', 'M.mod_quiz.init_resource_dragdrop',
1139 array(array(
441d284a
TH
1140 'courseid' => $structure,
1141 'quizid' => $structure->get_quizid(),
e1a2d0d9
CC
1142 'ajaxurl' => $config->resourceurl,
1143 'config' => $config,
1144 )), null, true);
1145
1146 // Require various strings for the command toolbox.
1147 $this->page->requires->strings_for_js(array(
84b45a24 1148 'clicktohideshow',
e1a2d0d9
CC
1149 'deletechecktype',
1150 'deletechecktypename',
1151 'edittitle',
1152 'edittitleinstructions',
84b45a24 1153 'emptydragdropregion',
e1a2d0d9 1154 'hide',
e1a2d0d9 1155 'markedthistopic',
84b45a24 1156 'markthistopic',
e1a2d0d9 1157 'move',
e1a2d0d9 1158 'movecontent',
84b45a24
TH
1159 'moveleft',
1160 'movesection',
a69f81f0
CC
1161 'page',
1162 'question',
e1a2d0d9 1163 'selectall',
84b45a24 1164 'show',
e1a2d0d9 1165 'tocontent',
e1a2d0d9
CC
1166 ), 'moodle');
1167
1168 $this->page->requires->strings_for_js(array(
a69f81f0 1169 'addpagebreak',
aa6a8b98
SR
1170 'cannotremoveallsectionslots',
1171 'cannotremoveslots',
5d949702 1172 'confirmremovesectionheading',
e1a2d0d9
CC
1173 'confirmremovequestion',
1174 'dragtoafter',
1175 'dragtostart',
6375e98c 1176 'numquestionsx',
5d949702
K
1177 'sectionheadingedit',
1178 'sectionheadingremove',
a69f81f0 1179 'removepagebreak',
441d284a
TH
1180 'questiondependencyadd',
1181 'questiondependencyfree',
1182 'questiondependencyremove',
1183 'questiondependsonprevious',
e1a2d0d9
CC
1184 ), 'quiz');
1185
1186 foreach (\question_bank::get_all_qtypes() as $qtype => $notused) {
1187 $this->page->requires->string_for_js('pluginname', 'qtype_' . $qtype);
1188 }
1189
1190 return true;
1191 }
1192
a69f81f0
CC
1193 /**
1194 * HTML for a page, with ids stripped, so it can be used as a javascript template.
1195 *
1196 * @param structure $structure object containing the structure of the quiz.
1197 * @param \question_edit_contexts $contexts the relevant question bank contexts.
1198 * @param array $pagevars the variables from {@link \question_edit_setup()}.
1199 * @param \moodle_url $pageurl the canonical URL of this page.
1200 * @return string HTML for a new page.
1201 */
1202 protected function new_page_template(structure $structure,
1203 \question_edit_contexts $contexts, array $pagevars, \moodle_url $pageurl) {
1204 if (!$structure->has_questions()) {
1205 return '';
1206 }
1207
441d284a 1208 $pagehtml = $this->page_row($structure, 1, $contexts, $pagevars, $pageurl);
a69f81f0
CC
1209
1210 // Normalise the page number.
441d284a 1211 $pagenumber = $structure->get_page_number_for_slot(1);
a69f81f0
CC
1212 $strcontexts = array();
1213 $strcontexts[] = 'page-';
1214 $strcontexts[] = get_string('page') . ' ';
1215 $strcontexts[] = 'addonpage%3D';
1216 $strcontexts[] = 'addonpage=';
1217 $strcontexts[] = 'addonpage="';
1218 $strcontexts[] = get_string('addquestionfrombanktopage', 'quiz', '');
1219 $strcontexts[] = 'data-addonpage%3D';
1220 $strcontexts[] = 'action-menu-';
1221
1222 foreach ($strcontexts as $strcontext) {
1223 $pagehtml = str_replace($strcontext . $pagenumber, $strcontext . '%%PAGENUMBER%%', $pagehtml);
1224 }
1225
1226 return $pagehtml;
1227 }
1228
1229 /**
1230 * HTML for a page, with ids stripped, so it can be used as a javascript template.
1231 *
1232 * @param structure $structure object containing the structure of the quiz.
a69f81f0
CC
1233 * @return string HTML for a new icon
1234 */
441d284a 1235 protected function add_page_icon_template(structure $structure) {
a69f81f0
CC
1236
1237 if (!$structure->has_questions()) {
1238 return '';
1239 }
1240
441d284a 1241 $html = $this->page_split_join_button($structure, 1);
a69f81f0
CC
1242 return str_replace('&amp;slot=1&amp;', '&amp;slot=%%SLOT%%&amp;', $html);
1243 }
1244
e1a2d0d9
CC
1245 /**
1246 * Return the contents of the question bank, to be displayed in the question-bank pop-up.
1247 *
8aa5e0cf 1248 * @param \mod_quiz\question\bank\custom_view $questionbank the question bank view object.
e1a2d0d9
CC
1249 * @param array $pagevars the variables from {@link \question_edit_setup()}.
1250 * @return string HTML to output / send back in response to an AJAX request.
1251 */
8aa5e0cf 1252 public function question_bank_contents(\mod_quiz\question\bank\custom_view $questionbank, array $pagevars) {
e1a2d0d9
CC
1253
1254 $qbank = $questionbank->render('editq', $pagevars['qpage'], $pagevars['qperpage'],
fff03332
RW
1255 $pagevars['cat'], $pagevars['recurse'], $pagevars['showhidden'], $pagevars['qbshowtext'],
1256 $pagevars['qtagids']);
e1a2d0d9
CC
1257 return html_writer::div(html_writer::div($qbank, 'bd'), 'questionbankformforpopup');
1258 }
1259}