Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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