MDL-43089 quiz: improved interface for building quizzes
[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;
30
31/**
32 * Renderer outputting the quiz editing UI.
33 *
34 * @copyright 2013 The Open University.
35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36 * @since Moodle 2.7
37 */
38class edit_renderer extends \plugin_renderer_base {
39
40 /**
41 * Render the edit page
42 *
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.
49 */
50 public function edit_page(\quiz $quizobj, structure $structure,
51 \question_edit_contexts $contexts, \moodle_url $pageurl, array $pagevars) {
52 $output = '';
53
54 // Page title.
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);
58
59 // Information at the top.
60 $output .= $this->quiz_state_warnings($structure);
61 $output .= $this->quiz_information($structure);
62 $output .= $this->maximum_grade_input($quizobj->get_quiz(), $this->page->url);
63 $output .= $this->repaginate_button($structure, $pageurl);
64 $output .= $this->total_marks($quizobj->get_quiz());
65
66 // Show the questions organised into sections and pages.
67 $output .= $this->start_section_list();
68
69 $sections = $structure->get_quiz_sections();
70 $lastsection = end($sections);
71 foreach ($sections as $section) {
72 $output .= $this->start_section($section);
73 $output .= $this->questions_in_section($structure, $section, $contexts, $pagevars, $pageurl);
74 if ($section === $lastsection) {
75 $output .= \html_writer::start_div('last-add-menu');
76 $output .= html_writer::tag('span', $this->add_menu_actions($structure, 0,
77 $pageurl, $contexts, $pagevars), array('class' => 'add-menu-outer'));
78 $output .= \html_writer::end_div();
79 }
80 $output .= $this->end_section();
81 }
82
83 $output .= $this->end_section_list();
84
85 // Inialise the JavaScript.
86 $this->initialise_editing_javascript($quizobj->get_course(), $quizobj->get_quiz());
87
88 // Include the contents of any other popups required.
89 if ($structure->can_be_edited()) {
90 $popups = '';
91
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()));
96
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');
100
101 $output .= html_writer::div($popups, 'mod_quiz_edit_forms');
102
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');
106 }
107
108 return $output;
109 }
110
111 /**
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
114 * turned on.
115 *
116 * @param structure $structure the quiz structure.
117 * @return string HTML to output.
118 */
119 public function quiz_state_warnings(structure $structure) {
120 $warnings = $structure->get_edit_page_warnings();
121
122 if (empty($warnings)) {
123 return '';
124 }
125
126 $output = array();
127 foreach ($warnings as $warning) {
128 $output[] = \html_writer::tag('p', $warning);
129 }
130 return $this->box(implode("\n", $output), 'statusdisplay');
131 }
132
133 /**
134 * Render the status bar.
135 *
136 * @param structure $structure the quiz structure.
137 * @return string HTML to output.
138 */
139 public function quiz_information(structure $structure) {
140 list($currentstatus, $explanation) = $structure->get_dates_summary();
141
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));
147
148 return html_writer::div($output, 'statusbar');
149 }
150
151 /**
152 * Render the form for setting a quiz' overall grade
153 *
154 * @param \stdClass $quiz the quiz settings from the database.
155 * @param \moodle_url $pageurl the canonical URL of this page.
156 * @return string HTML to output.
157 */
158 public function maximum_grade_input($quiz, \moodle_url $pageurl) {
159 $output = '';
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' => ($quiz->decimalpoints + 2),
168 'value' => quiz_format_grade($quiz, $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');
176 return $output;
177 }
178
179 /**
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.
184 */
185 protected function repaginate_button(structure $structure, \moodle_url $pageurl) {
186
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(),
192 'header' => $header,
193 'form' => $form,
194 );
195
196 $buttonoptions = array(
197 'type' => 'submit',
198 'name' => 'repaginate',
199 'id' => 'repaginatecommand',
200 'value' => get_string('repaginatecommand', 'quiz'),
201 );
202 if (!$structure->can_be_repaginated()) {
203 $buttonoptions['disabled'] = 'disabled';
204 } else {
205 $this->page->requires->yui_module('moodle-mod_quiz-repaginate', 'M.mod_quiz.repaginate.init');
206 }
207
208 return html_writer::tag('div',
209 html_writer::empty_tag('input', $buttonoptions), $containeroptions);
210 }
211
212 /**
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.
217 */
218 protected function repaginate_form(structure $structure, \moodle_url $pageurl) {
219 $perpage = array();
220 $perpage[0] = get_string('allinone', 'quiz');
221 for ($i = 1; $i <= 50; ++$i) {
222 $perpage[$i] = $i;
223 }
224
225 $hiddenurl = clone($pageurl);
226 $hiddenurl->param('sesskey', sesskey());
227
228 $select = html_writer::select($perpage, 'questionsperpage',
229 $structure->get_questions_per_page(), false);
230
231 $buttonattributes = array('type' => 'submit', 'name' => 'repaginate', 'value' => get_string('go'));
232
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'));
238
239 return html_writer::div($formcontent, '', array('id' => 'repaginatedialog'));
240 }
241
242 /**
243 * Render the total marks available for the quiz.
244 *
245 * @param \stdClass $quiz the quiz settings from the database.
246 * @return string HTML to output.
247 */
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'));
253 }
254
255 /**
256 * Generate the starting container html for the start of a list of sections
257 * @return string HTML to output.
258 */
259 protected function start_section_list() {
260 return html_writer::start_tag('ul', array('class' => 'slots'));
261 }
262
263 /**
264 * Generate the closing container html for the end of a list of sections
265 * @return string HTML to output.
266 */
267 protected function end_section_list() {
268 return html_writer::end_tag('ul');
269 }
270
271 /**
272 * Display the start of a section, before the questions.
273 *
274 * @param \stdClass $section The quiz_section entry from DB
275 * @return string HTML to output.
276 */
277 protected function start_section($section) {
278
279 $output = '';
280 $sectionstyle = '';
281
282 $output .= html_writer::start_tag('li', array('id' => 'section-'.$section->id,
283 'class' => 'section main clearfix'.$sectionstyle, 'role' => 'region',
284 'aria-label' => $section->heading));
285
286 $leftcontent = $this->section_left_content($section);
287 $output .= html_writer::div($leftcontent, 'left side');
288
289 $rightcontent = $this->section_right_content($section);
290 $output .= html_writer::div($rightcontent, 'right side');
291 $output .= html_writer::start_div('content');
292
293 return $output;
294 }
295
296 /**
297 * Display the end of a section, after the questions.
298 *
299 * @return string HTML to output.
300 */
301 protected function end_section() {
302 $output = html_writer::end_tag('div');
303 $output .= html_writer::end_tag('li');
304
305 return $output;
306 }
307
308 /**
309 * Generate the content to be displayed on the left part of a section.
310 *
311 * @param \stdClass $section The quiz_section entry from DB
312 * @return string HTML to output.
313 */
314 protected function section_left_content($section) {
315 return $this->output->spacer();
316 }
317
318 /**
319 * Generate the content to displayed on the right part of a section.
320 *
321 * @param \stdClass $section The quiz_section entry from DB
322 * @return string HTML to output.
323 */
324 protected function section_right_content($section) {
325 return $this->output->spacer();
326 }
327
328 /**
329 * Renders HTML to display the questions in a section of the quiz.
330 *
331 * This function calls {@link core_course_renderer::quiz_section_question()}
332 *
333 * @param structure $structure object containing the structure of the quiz.
334 * @param \stdClass $section information about the section.
335 * @param \question_edit_contexts $contexts the relevant question bank contexts.
336 * @param array $pagevars the variables from {@link \question_edit_setup()}.
337 * @param \moodle_url $pageurl the canonical URL of this page.
338 * @return string HTML to output.
339 */
340 public function questions_in_section(structure $structure, $section,
341 $contexts, $pagevars, $pageurl) {
342
343 $output = '';
344 foreach ($structure->get_questions_in_section($section->id) as $question) {
345 $output .= $this->question_row($structure, $question, $contexts, $pagevars, $pageurl);
346 }
347
348 return html_writer::tag('ul', $output, array('class' => 'section img-text'));
349 }
350
351 /**
352 * Displays one question with the surrounding controls.
353 *
354 * @param structure $structure object containing the structure of the quiz.
355 * @param \stdClass $question data from the question and quiz_slots tables.
356 * @param \question_edit_contexts $contexts the relevant question bank contexts.
357 * @param array $pagevars the variables from {@link \question_edit_setup()}.
358 * @param \moodle_url $pageurl the canonical URL of this page.
359 * @return string HTML to output.
360 */
361 public function question_row(structure $structure, $question, $contexts, $pagevars, $pageurl) {
362 $output = '';
363
364 // Put page in a span for easier styling.
365 $page = html_writer::tag('span', get_string('page') . ' ' . $question->page,
366 array('class' => 'text'));
367
368 if ($structure->is_first_slot_on_page($question->slot)) {
369 // Add the add-menu at the page level.
370 $addmenu = html_writer::tag('span', $this->add_menu_actions($structure,
371 $question->page, $pageurl, $contexts, $pagevars),
372 array('class' => 'add-menu-outer'));
373
374 $addquestionform = $this->add_question_form($structure,
375 $question->page, $pageurl, $pagevars);
376
377 $output .= html_writer::tag('li', $page . $addmenu . $addquestionform,
378 array('class' => 'pagenumber activity yui3-dd-drop page', 'id' => 'page-' . $question->page));
379 }
380
381 // Page split/join icon.
382 $joinhtml = '';
383 if (!$structure->is_last_slot_in_quiz($question->slot)) {
384 $joinhtml = $this->page_split_join_button($structure->get_quiz(),
385 $question, !$structure->is_last_slot_on_page($question->slot));
386 }
387
388 // Question HTML.
389 $questionhtml = $this->question($structure, $question, $pageurl);
390 $questionclasses = 'activity ' . $question->qtype . ' qtype_' . $question->qtype . ' slot';
391
392 $output .= html_writer::tag('li', $questionhtml . $joinhtml,
393 array('class' => $questionclasses, 'id' => 'slot-' . $question->slotid));
394
395 return $output;
396 }
397
398 /**
399 * Returns the add menu that is output once per page.
400 * @param structure $structure object containing the structure of the quiz.
401 * @param int $page the page number that this menu will add to.
402 * @param \moodle_url $pageurl the canonical URL of this page.
403 * @param \question_edit_contexts $contexts the relevant question bank contexts.
404 * @param array $pagevars the variables from {@link \question_edit_setup()}.
405 * @return string HTML to output.
406 */
407 public function add_menu_actions(structure $structure, $page, \moodle_url $pageurl,
408 \question_edit_contexts $contexts, array $pagevars) {
409
410 $actions = $this->edit_menu_actions($structure, $page, $pageurl, $pagevars);
411 if (empty($actions)) {
412 return '';
413 }
414 $menu = new \action_menu();
415 $menu->set_alignment(\action_menu::TR, \action_menu::BR);
416 $trigger = html_writer::tag('span', get_string('add', 'quiz'), array('class' => 'add-menu'));
417 $menu->set_menu_trigger($trigger);
418
419 // Disable the link if quiz has attempts.
420 if (!$structure->can_be_edited()) {
421 return $this->render($menu);
422 }
423
424 foreach ($actions as $action) {
425 if ($action instanceof \action_menu_link) {
426 $action->add_class('add-menu');
427 }
428 $menu->add($action);
429 }
430 $menu->attributes['class'] .= ' page-add-actions commands';
431
432 // Prioritise the menu ahead of all other actions.
433 $menu->prioritise = true;
434
435 return $this->render($menu);
436 }
437
438 /**
439 * Returns the list of actions to go in the add menu.
440 * @param structure $structure object containing the structure of the quiz.
441 * @param int $page the page number that this menu will add to.
442 * @param \moodle_url $pageurl the canonical URL of this page.
443 * @param array $pagevars the variables from {@link \question_edit_setup()}.
444 * @return array the actions.
445 */
446 public function edit_menu_actions(structure $structure, $page,
447 \moodle_url $pageurl, array $pagevars) {
448 $questioncategoryid = question_get_category_id_from_pagevars($pagevars);
449 static $str;
450 if (!isset($str)) {
451 $str = get_strings(array('addaquestion', 'addarandomquestion',
452 'addarandomselectedquestion', 'questionbank'), 'quiz');
453 }
454
455 // Get section, page, slotnumber and maxmark.
456 $actions = array();
457
458 // Add a new question to the quiz.
459 $returnurl = new \moodle_url($pageurl, array('addonpage' => $page));
460 $params = array('returnurl' => $returnurl->out_as_local_url(false),
461 'cmid' => $structure->get_cmid(), 'category' => $questioncategoryid,
462 'addonpage' => $page, 'appendqnumstring' => 'addquestion');
463
464 $actions['addaquestion'] = new \action_menu_link_secondary(
465 new \moodle_url('/question/addquestion.php', $params),
466 new \pix_icon('t/add', $str->addaquestion, 'moodle', array('class' => 'iconsmall', 'title' => '')),
467 $str->addaquestion, array('class' => 'cm-edit-action addquestion', 'data-action' => 'addquestion')
468 );
469
470 // Call question bank.
471 $icon = new \pix_icon('t/add', $str->questionbank, 'moodle', array('class' => 'iconsmall', 'title' => ''));
472 $title = get_string('addquestionfrombanktopage', 'quiz', $page);
473 $attributes = array('class' => 'cm-edit-action questionbank',
474 'data-header' => $title, 'data-action' => 'questionbank', 'data-addonpage' => $page);
475 $actions['questionbank'] = new \action_menu_link_secondary($pageurl, $icon, $str->questionbank, $attributes);
476
477 // Add a random question.
478 $returnurl = new \moodle_url('/mod/quiz/edit.php', array('cmid' => $structure->get_cmid(), 'data-addonpage' => $page));
479 $params = array('returnurl' => $returnurl, 'cmid' => $structure->get_cmid(), 'appendqnumstring' => 'addarandomquestion');
480 $url = new \moodle_url('/mod/quiz/addrandom.php', $params);
481 $icon = new \pix_icon('t/add', $str->addarandomquestion, 'moodle', array('class' => 'iconsmall', 'title' => ''));
482 $attributes = array('class' => 'cm-edit-action addarandomquestion', 'data-action' => 'addarandomquestion');
483 $title = get_string('addrandomquestiontopage', 'quiz', $page);
484 $attributes = array_merge(array('data-header' => $title, 'data-addonpage' => $page), $attributes);
485 $actions['addarandomquestion'] = new \action_menu_link_secondary($url, $icon, $str->addarandomquestion, $attributes);
486
487 return $actions;
488 }
489
490 /**
491 * Render the form that contains the data for adding a new question to the quiz.
492 *
493 * @param structure $structure object containing the structure of the quiz.
494 * @param int $page the page number that this menu will add to.
495 * @param \moodle_url $pageurl the canonical URL of this page.
496 * @param array $pagevars the variables from {@link \question_edit_setup()}.
497 * @return string HTML to output.
498 */
499 protected function add_question_form(structure $structure, $page, \moodle_url $pageurl, array $pagevars) {
500
501 $questioncategoryid = question_get_category_id_from_pagevars($pagevars);
502
503 $output = html_writer::tag('input', null,
504 array('type' => 'hidden', 'name' => 'returnurl',
505 'value' => $pageurl->out_as_local_url(false, array('addonpage' => $page))));
506 $output .= html_writer::tag('input', null,
507 array('type' => 'hidden', 'name' => 'cmid', 'value' => $structure->get_cmid()));
508 $output .= html_writer::tag('input', null,
509 array('type' => 'hidden', 'name' => 'appendqnumstring', 'value' => 'addquestion'));
510 $output .= html_writer::tag('input', null,
511 array('type' => 'hidden', 'name' => 'category', 'value' => $questioncategoryid));
512
513 return html_writer::tag('form', html_writer::div($output),
514 array('class' => 'addnewquestion', 'method' => 'post',
515 'action' => new \moodle_url('/question/addquestion.php')));
516 }
517
518 /**
519 * Display a question.
520 *
521 * @param structure $structure object containing the structure of the quiz.
522 * @param \stdClass $question data from the question and quiz_slots tables.
523 * @param \moodle_url $pageurl the canonical URL of this page.
524 * @return string HTML to output.
525 */
526 public function question(structure $structure, $question, \moodle_url $pageurl) {
527 $output = '';
528
529 $output .= html_writer::start_tag('div');
530
531 if ($structure->can_be_edited()) {
532 $output .= $this->question_move_icon($question);
533 }
534
535 $output .= html_writer::start_div('mod-indent-outer');
536 $output .= $this->question_number($question->displayednumber);
537
538 // This div is used to indent the content.
539 $output .= html_writer::div('', 'mod-indent');
540
541 // Display the link to the question (or do nothing if question has no url).
542 if ($question->qtype == 'random') {
543 $questionname = $this->random_question($structure, $question, $pageurl);
544 } else {
545 $questionname = $this->question_name($structure, $question, $pageurl);
546 }
547
548 // Start the div for the activity title, excluding the edit icons.
549 $output .= html_writer::start_div('activityinstance');
550 $output .= $questionname;
551
552 // Closing the tag which contains everything but edit icons. Content part of the module should not be part of this.
553 $output .= html_writer::end_tag('div'); // .activityinstance.
554
555 // Action icons.
556 $questionicons = '';
557 $questionicons .= $this->question_preview_icon($structure->get_quiz(), $question);
558 if ($structure->can_be_edited()) {
559 $questionicons .= $this->question_remove_icon($question, $pageurl);
560 }
561 $questionicons .= $this->marked_out_of_field($structure->get_quiz(), $question);
562 $output .= html_writer::span($questionicons, 'actions'); // Required to add js spinner icon.
563
564 // End of indentation div.
565 $output .= html_writer::end_tag('div');
566 $output .= html_writer::end_tag('div');
567
568 return $output;
569 }
570
571 /**
572 * Render the move icon.
573 *
574 * @param \stdClass $question data from the question and quiz_slots tables.
575 * @return string The markup for the move action, or an empty string if not available.
576 */
577 public function question_move_icon($question) {
578 return html_writer::link(new \moodle_url('#'),
579 $this->pix_icon('i/dragdrop', get_string('move'), 'moodle', array('class' => 'iconsmall', 'title' => '')),
580 array('class' => 'editing_move', 'data-action' => 'move')
581 );
582 }
583
584 /**
585 * Output the question number.
586 * @param string $number The number, or 'i'.
587 * @return string HTML to output.
588 */
589 public function question_number($number) {
590 if (is_numeric($number)) {
591 $number = html_writer::span(get_string('question'), 'accesshide') .
592 ' ' . $number;
593 }
594 return html_writer::tag('span', $number, array('class' => 'slotnumber'));
595 }
596
597 /**
598 * Render the preview icon.
599 *
600 * @param \stdClass $quiz the quiz settings from the database.
601 * @param \stdClass $question data from the question and quiz_slots tables.
602 * @param bool $label if true, show the preview question label after the icon
603 * @return string HTML to output.
604 */
605 public function question_preview_icon($quiz, $question, $label = null) {
606 $url = quiz_question_preview_url($quiz, $question);
607
608 // Do we want a label?
609 $strpreviewlabel = '';
610 if ($label) {
611 $strpreviewlabel = ' ' . get_string('preview', 'quiz');
612 }
613
614 // Build the icon.
615 $strpreviewquestion = get_string('previewquestion', 'quiz');
616 $image = $this->pix_icon('t/preview', $strpreviewquestion);
617
618 $action = new \popup_action('click', $url, 'questionpreview',
619 question_preview_popup_params());
620
621 return $this->action_link($url, $image . $strpreviewlabel, $action,
622 array('title' => $strpreviewquestion, 'class' => 'preview'));
623 }
624
625 /**
626 * Render an icon to remove a question from the quiz.
627 *
628 * @param object $question The module to produce a move button for.
629 * @param \moodle_url $pageurl the canonical URL of the edit page.
630 * @return string HTML to output.
631 */
632 public function question_remove_icon($question, $pageurl) {
633 $url = new \moodle_url($pageurl, array('sesskey' => sesskey(), 'remove' => $question->slot));
634 $strdelete = get_string('delete');
635
636 $image = $this->pix_icon('t/delete', $strdelete);
637
638 return $this->action_link($url, $image, null, array('title' => $strdelete,
639 'class' => 'cm-edit-action editing_delete', 'data-action' => 'delete'));
640 }
641
642 /**
643 * Display an icon to split or join two pages of the quiz.
644 *
645 * @param \stdClass $quiz the quiz settings from the database.
646 * @param \stdClass $question data from the question and quiz_slots tables.
647 * @param string $insertpagebreak if true, show an insert page break icon.
648 * Else show a join pages icon.
649 * @return string HTML to output.
650 */
651 public function page_split_join_button($quiz, $question, $insertpagebreak) {
652 $url = new \moodle_url('repaginate.php', array('cmid' => $quiz->cmid, 'quizid' => $quiz->id,
653 'slot' => $question->slot, 'repag' => $insertpagebreak ? 2 : 1, 'sesskey' => sesskey()));
654
655 if ($insertpagebreak) {
656 $title = get_string('splitpages', 'quiz');
657 $image = $this->pix_icon('e/insert_page_break', $title);
658 $action = 'unlinkpage';
659 } else {
660 $title = get_string('joinpages', 'quiz');
661 $image = $this->pix_icon('e/remove_page_break', $title);
662 $action = 'linkpage';
663 }
664
665 // Disable the link if quiz has attempts.
666 $disabled = null;
667 if (quiz_has_attempts($quiz->id)) {
668 $disabled = "disabled";
669 }
670 return html_writer::span($this->action_link($url, $image, null, array('title' => $title,
671 'class' => 'page_split_join', 'disabled' => $disabled, 'data-action' => $action)),
672 'page_split_join_wrapper');
673 }
674
675 /**
676 * Renders html to display a name with the link to the question on a quiz edit page
677 *
678 * If the user does not have permission to edi the question, it is rendered
679 * without a link
680 *
681 * @param structure $structure object containing the structure of the quiz.
682 * @param \stdClass $question data from the question and quiz_slots tables.
683 * @param \moodle_url $pageurl the canonical URL of this page.
684 * @return string HTML to output.
685 */
686 public function question_name(structure $structure, $question, $pageurl) {
687 $output = '';
688
689 $editurl = new \moodle_url('/question/question.php', array(
690 'returnurl' => $pageurl->out_as_local_url(),
691 'cmid' => $structure->get_cmid(), 'id' => $question->id));
692
693 $instancename = quiz_question_tostring($question);
694
695 $qtype = \question_bank::get_qtype($question->qtype, false);
696 $namestr = $qtype->local_name();
697
698 $icon = $this->pix_icon('icon', $namestr, $qtype->plugin_name(), array('title' => $namestr,
699 'class' => 'icon activityicon', 'alt' => ' ', 'role' => 'presentation'));
700
701 $editicon = $this->pix_icon('t/edit', '', 'moodle', array('title' => ''));
702
703 // Need plain question name without html tags for link title.
704 $title = shorten_text(format_string($question->name), 100);
705
706 // Display the link itself.
707 $activitylink = $icon . html_writer::tag('span', $editicon . $instancename, array('class' => 'instancename'));
708 $output .= html_writer::link($editurl, $activitylink,
709 array('title' => get_string('editquestion', 'quiz').' '.$title));
710
711 return $output;
712 }
713
714 /**
715 * Renders html to display a random question the link to edit the configuration
716 * and also to see that category in the question bank.
717 *
718 * @param structure $structure object containing the structure of the quiz.
719 * @param \stdClass $question data from the question and quiz_slots tables.
720 * @param \moodle_url $pageurl the canonical URL of this page.
721 * @return string HTML to output.
722 */
723 public function random_question(structure $structure, $question, $pageurl) {
724
725 $editurl = new \moodle_url('/question/question.php', array(
726 'returnurl' => $pageurl->out_as_local_url(),
727 'cmid' => $structure->get_cmid(), 'id' => $question->id));
728
729 $temp = clone($question);
730 $temp->questiontext = '';
731 $instancename = quiz_question_tostring($temp);
732
733 $configuretitle = get_string('configurerandomquestion', 'quiz');
734 $qtype = \question_bank::get_qtype($question->qtype, false);
735 $namestr = $qtype->local_name();
736 $icon = $this->pix_icon('icon', $namestr, $qtype->plugin_name(), array('title' => $namestr,
737 'class' => 'icon activityicon', 'alt' => ' ', 'role' => 'presentation'));
738
739 $editicon = $this->pix_icon('t/edit', $configuretitle, 'moodle', array('title' => ''));
740
741 // If this is a random question, display a link to show the questions
742 // selected from in the question bank.
743 $qbankurl = new \moodle_url('/question/edit.php', array(
744 'cmid' => $structure->get_cmid(),
745 'cat' => $question->category . ',' . $question->contextid,
746 'recurse' => !empty($question->questiontext)));
747 $qbanklink = ' ' . \html_writer::link($qbankurl,
748 get_string('seequestions', 'quiz'), array('class' => 'mod_quiz_random_qbank_link'));
749
750 return html_writer::link($editurl, $icon . $editicon, array('title' => $configuretitle)) .
751 ' ' . $instancename . ' ' . $qbanklink;
752 }
753
754 /**
755 * Display the 'marked out of' information for a question.
756 * Along with the regrade action.
757 * @param \stdClass $quiz the quiz settings from the database.
758 * @param \stdClass $question data from the question and quiz_slots tables.
759 * @return string HTML to output.
760 */
761 public function marked_out_of_field($quiz, $question) {
762 $output = html_writer::span(quiz_format_question_grade($quiz, $question->maxmark),
763 'instancemaxmark decimalplaces_' . quiz_get_grade_format($quiz),
764 array('title' => get_string('maxmark', 'quiz')));
765
766 $output .= html_writer::span(
767 html_writer::link(
768 new \moodle_url('#'),
769 $this->pix_icon('t/editstring', '', 'moodle', array('class' => 'smallicon visibleifjs', 'title' => '')),
770 array(
771 'class' => 'editing_maxmark',
772 'data-action' => 'editmaxmark',
773 'title' => get_string('editmaxmark', 'quiz'),
774 )
775 )
776 );
777 return html_writer::span($output, 'instancemaxmarkcontainer');
778 }
779
780 /**
781 * Render the question type chooser dialogue.
782 * @return string HTML to output.
783 */
784 public function question_chooser() {
785 $container = html_writer::div(print_choose_qtype_to_add_form(array(), null, false), '',
786 array('id' => 'qtypechoicecontainer'));
787 return html_writer::div($container, 'createnewquestion');
788 }
789
790 /**
791 * Render the contents of the question bank pop-up in its initial state,
792 * when it just contains a loading progress indicator.
793 * @return string HTML to output.
794 */
795 public function question_bank_loading() {
796 return html_writer::div(html_writer::empty_tag('img',
797 array('alt' => 'loading', 'class' => 'loading-icon', 'src' => $this->pix_url('i/loading'))),
798 'questionbankloading');
799 }
800
801 /**
802 * Return random question form.
803 * @param \moodle_url $thispageurl the canonical URL of this page.
804 * @param \question_edit_contexts $contexts the relevant question bank contexts.
805 * @param array $pagevars the variables from {@link \question_edit_setup()}.
806 * @return string HTML to output.
807 */
808 protected function random_question_form(\moodle_url $thispageurl, \question_edit_contexts $contexts, array $pagevars) {
809
810 if (!$contexts->have_cap('moodle/question:useall')) {
811 return '';
812 }
813 $randomform = new \quiz_add_random_form(new \moodle_url('/mod/quiz/addrandom.php'),
814 array('contexts' => $contexts, 'cat' => $pagevars['cat']));
815 $randomform->set_data(array(
816 'category' => $pagevars['cat'],
817 'returnurl' => $thispageurl->out_as_local_url(true),
818 'randomnumber' => 1,
819 'cmid' => $thispageurl->param('cmid'),
820 ));
821 return html_writer::div($randomform->render(), 'randomquestionformforpopup');
822 }
823
824 /**
825 * Initialise the JavaScript for the general editing. (JavaScript for popups
826 * is handled with the specific code for those.)
827 *
828 * @param \stdClass $course the course settings from the database.
829 * @param \stdClass $quiz the quiz settings from the database.
830 * @return bool Always returns true
831 */
832 protected function initialise_editing_javascript($course, $quiz) {
833
834 $config = new \stdClass();
835 $config->resourceurl = '/mod/quiz/edit_rest.php';
836 $config->sectionurl = '/mod/quiz/edit_rest.php';
837 $config->pageparams = array();
838 $config->questiondecimalpoints = $quiz->questiondecimalpoints;
839
840 // Include toolboxes.
841 $this->page->requires->yui_module('moodle-mod_quiz-toolboxes',
842 'M.mod_quiz.init_resource_toolbox',
843 array(array(
844 'courseid' => $course->id,
845 'quizid' => $quiz->id,
846 'ajaxurl' => $config->resourceurl,
847 'config' => $config,
848 ))
849 );
850 $this->page->requires->yui_module('moodle-mod_quiz-toolboxes',
851 'M.mod_quiz.init_section_toolbox',
852 array(array(
853 'courseid' => $course->id,
854 'quizid' => $quiz->id,
855 'format' => $course->format,
856 'ajaxurl' => $config->sectionurl,
857 'config' => $config,
858 ))
859 );
860
861 // Include course dragdrop.
862 $this->page->requires->yui_module('moodle-mod_quiz-dragdrop', 'M.mod_quiz.init_section_dragdrop',
863 array(array(
864 'courseid' => $course->id,
865 'quizid' => $quiz->id,
866 'ajaxurl' => $config->sectionurl,
867 'config' => $config,
868 )), null, true);
869
870 $this->page->requires->yui_module('moodle-mod_quiz-dragdrop', 'M.mod_quiz.init_resource_dragdrop',
871 array(array(
872 'courseid' => $course->id,
873 'quizid' => $quiz->id,
874 'ajaxurl' => $config->resourceurl,
875 'config' => $config,
876 )), null, true);
877
878 // Require various strings for the command toolbox.
879 $this->page->requires->strings_for_js(array(
880 'moveleft',
881 'deletechecktype',
882 'deletechecktypename',
883 'edittitle',
884 'edittitleinstructions',
885 'show',
886 'hide',
887 'clicktochangeinbrackets',
888 'markthistopic',
889 'markedthistopic',
890 'move',
891 'movesection',
892 'movecontent',
893 'selectall',
894 'tocontent',
895 'emptydragdropregion'
896 ), 'moodle');
897
898 $this->page->requires->strings_for_js(array(
899 'confirmremovequestion',
900 'dragtoafter',
901 'dragtostart',
902 ), 'quiz');
903
904 foreach (\question_bank::get_all_qtypes() as $qtype => $notused) {
905 $this->page->requires->string_for_js('pluginname', 'qtype_' . $qtype);
906 }
907
908 return true;
909 }
910
911 /**
912 * Return the contents of the question bank, to be displayed in the question-bank pop-up.
913 *
914 * @param \quiz_question_bank_view $questionbank the question bank view object.
915 * @param array $pagevars the variables from {@link \question_edit_setup()}.
916 * @return string HTML to output / send back in response to an AJAX request.
917 */
918 public function question_bank_contents(\quiz_question_bank_view $questionbank, array $pagevars) {
919
920 $qbank = $questionbank->render('editq', $pagevars['qpage'], $pagevars['qperpage'],
921 $pagevars['cat'], $pagevars['recurse'], $pagevars['showhidden'], $pagevars['qbshowtext']);
922 return html_writer::div(html_writer::div($qbank, 'bd'), 'questionbankformforpopup');
923 }
924}