Proyectos de Subversion Moodle

Rev

Rev 711 | Ir a la última revisión | | Comparar con el anterior | 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
defined('MOODLE_INTERNAL') || die();
18
 
19
 
20
// Quiz.
21
require_once($CFG->dirroot . "/mod/quiz/renderer.php");
710 ariadna 22
class theme_universe_mod_quiz_renderer extends mod_quiz\output\renderer
23
{
1 efrain 24
    /**
25
     * Outputs a box.
26
     *
27
     * @param string $contents The contents of the box
28
     * @param string $classes A space-separated list of CSS classes
29
     * @param string $id An optional ID
30
     * @param array $attributes An array of other attributes to give the box.
31
     * @return string the HTML to output.
32
     */
710 ariadna 33
    public function space_box($contents, $classes = 'generalbox', $id = null, $attributes = array())
34
    {
1 efrain 35
        return $this->space_box_start($classes, $id, $attributes) . $contents . $this->space_box_end();
36
    }
37
 
38
    /**
39
     * Outputs the opening section of a box.
40
     *
41
     * @param string $classes A space-separated list of CSS classes
42
     * @param string $id An optional ID
43
     * @param array $attributes An array of other attributes to give the box.
44
     * @return string the HTML to output.
45
     */
710 ariadna 46
    public function space_box_start($classes = 'generalbox', $id = null, $attributes = array())
47
    {
1 efrain 48
        $this->opencontainers->push('box', html_writer::end_tag('div'));
49
        $attributes['id'] = $id;
50
        $attributes['class'] = 'box ' . renderer_base::prepare_classes($classes);
51
        return html_writer::start_tag('div', $attributes);
52
    }
53
 
54
    /**
55
     * Outputs the closing section of a box.
56
     *
57
     * @return string the HTML to output.
58
     */
710 ariadna 59
    public function space_box_end()
60
    {
1 efrain 61
        return $this->opencontainers->pop('box');
62
    }
63
 
64
    /**
65
     * Output the page information
66
     *
67
     * @param object $quiz the quiz settings.
68
     * @param object $cm the course_module object.
69
     * @param context $context the quiz context.
70
     * @param array $messages any access messages that should be described.
71
     * @param bool $quizhasquestions does quiz has questions added.
72
     * @return string HTML to output.
73
     */
710 ariadna 74
    public function view_information($quiz, $cm, $context, $messages, bool $quizhasquestions = false)
75
    {
1 efrain 76
        $output = '';
77
 
78
        // Output any access messages.
79
        if ($messages) {
80
            $output .= $this->box($this->access_messages($messages), 'rui-quizinfo quizinfo mt-3');
81
        }
82
 
83
        // Show number of attempts summary to those who can view reports.
84
        if (has_capability('mod/quiz:viewreports', $context)) {
85
            if (
86
                $strattemptnum = $this->quiz_attempt_summary_link_to_reports(
87
                    $quiz,
88
                    $cm,
89
                    $context
90
                )
91
            ) {
92
                $output .= html_writer::tag(
93
                    'div',
94
                    $strattemptnum,
95
                    array('class' => 'rui-quizattemptcounts quizattemptcounts my-4')
96
                );
97
            }
98
        }
99
 
100
        if (has_any_capability(['mod/quiz:manageoverrides', 'mod/quiz:viewoverrides'], $context)) {
101
            if ($overrideinfo = $this->quiz_override_summary_links($quiz, $cm)) {
102
                $output .= html_writer::tag('div', $overrideinfo, ['class' => 'rui-quizattemptcounts quizattemptcounts my-4']);
103
            }
104
        }
105
 
106
        return $output;
107
    }
108
 
109
    /**
110
     * Generates the table of data
111
     *
112
     * @param array $quiz Array contining quiz data
113
     * @param int $context The page context ID
114
     * @param mod_quiz_view_object $viewobj
115
     */
710 ariadna 116
    public function view_table($quiz, $context, $viewobj)
117
    {
1 efrain 118
        if (!$viewobj->attempts) {
119
            return '';
120
        }
121
 
122
        // Prepare table header.
123
        $table = new html_table();
124
        $table->attributes['class'] = 'generaltable rui-quizattemptsummary mt-2 mb-0';
125
        $table->head = array();
126
        $table->align = array();
127
        $table->size = array();
128
        if ($viewobj->attemptcolumn) {
129
            $table->head[] = get_string('attemptnumber', 'quiz');
130
            $table->size[] = '';
131
        }
132
        $table->head[] = get_string('attemptstate', 'quiz');
133
        $table->align[] = 'left';
134
        $table->size[] = '';
135
        if ($viewobj->markcolumn) {
136
            $table->head[] = get_string('marks', 'quiz') . ' / ' .
137
                quiz_format_grade($quiz, $quiz->sumgrades);
138
            $table->size[] = '';
139
        }
140
        if ($viewobj->gradecolumn) {
141
            $table->head[] = get_string('grade', 'quiz') . ' / ' .
142
                quiz_format_grade($quiz, $quiz->grade);
143
            $table->size[] = '';
144
        }
145
        if ($viewobj->canreviewmine) {
146
            $table->head[] = get_string('review', 'quiz');
147
            $table->size[] = '';
148
        }
149
        if ($viewobj->feedbackcolumn) {
150
            $table->head[] = get_string('feedback', 'quiz');
151
            $table->align[] = 'left';
152
            $table->size[] = '';
153
        }
154
 
155
        // One row for each attempt.
156
        foreach ($viewobj->attemptobjs as $attemptobj) {
157
            $attemptoptions = $attemptobj->get_display_options(true);
158
            $row = array();
159
 
160
            // Add the attempt number.
161
            if ($viewobj->attemptcolumn) {
162
                if ($attemptobj->is_preview()) {
163
                    $row[] = get_string('preview', 'quiz');
164
                } else {
165
                    $row[] = $attemptobj->get_attempt_number();
166
                }
167
            }
168
 
169
            $row[] = $this->attempt_state($attemptobj);
170
 
171
            if ($viewobj->markcolumn) {
172
                if (
173
                    $attemptoptions->marks >= question_display_options::MARK_AND_MAX &&
174
                    $attemptobj->is_finished()
175
                ) {
176
                    $row[] = quiz_format_grade($quiz, $attemptobj->get_sum_marks());
177
                } else {
178
                    $row[] = '';
179
                }
180
            }
181
 
182
            // Ouside the if because we may be showing feedback but not grades.
183
            $attemptgrade = quiz_rescale_grade($attemptobj->get_sum_marks(), $quiz, false);
184
 
185
            if ($viewobj->gradecolumn) {
186
                if (
187
                    $attemptoptions->marks >= question_display_options::MARK_AND_MAX &&
188
                    $attemptobj->is_finished()
189
                ) {
190
 
191
                    // Highlight the highest grade if appropriate.
192
                    if (
193
                        $viewobj->overallstats && !$attemptobj->is_preview()
194
                        && $viewobj->numattempts > 1 && !is_null($viewobj->mygrade)
195
                        && $attemptobj->get_state() == quiz_attempt::FINISHED
196
                        && $attemptgrade == $viewobj->mygrade
197
                        && $quiz->grademethod == QUIZ_GRADEHIGHEST
198
                    ) {
199
                        $table->rowclasses[$attemptobj->get_attempt_number()] = 'bestrow';
200
                    }
201
 
202
                    $row[] = quiz_format_grade($quiz, $attemptgrade);
203
                } else {
204
                    $row[] = '';
205
                }
206
            }
207
 
208
            if ($viewobj->canreviewmine) {
209
                $row[] = $viewobj->accessmanager->make_review_link(
210
                    $attemptobj->get_attempt(),
211
                    $attemptoptions,
212
                    $this
213
                );
214
            }
215
 
216
            if ($viewobj->feedbackcolumn && $attemptobj->is_finished()) {
217
                if ($attemptoptions->overallfeedback) {
218
                    $row[] = quiz_feedback_for_grade($attemptgrade, $quiz, $context);
219
                } else {
220
                    $row[] = '';
221
                }
222
            }
223
 
224
            if ($attemptobj->is_preview()) {
225
                $table->data['preview'] = $row;
226
            } else {
227
                $table->data[$attemptobj->get_attempt_number()] = $row;
228
            }
229
        } // End. of loop over attempts.
230
 
231
        $output = '';
232
        $output .= $this->view_table_heading();
233
        $output .= html_writer::start_tag('div', array('class' => 'table-overflow mb-4'));
234
        $output .= html_writer::table($table);
235
        $output .= html_writer::end_tag('div');
236
        return $output;
237
    }
238
 
239
    /*
240
     * View Page
241
     */
242
    /**
243
     * Generates the view page
244
     *
245
     * @param stdClass $course the course settings row from the database.
246
     * @param stdClass $quiz the quiz settings row from the database.
247
     * @param stdClass $cm the course_module settings row from the database.
248
     * @param context_module $context the quiz context.
249
     * @param mod_quiz_view_object $viewobj
250
     * @return string HTML to display
251
     */
710 ariadna 252
    public function view_page($course, $quiz, $cm, $context, $viewobj)
253
    {
1 efrain 254
        $output = '';
255
 
711 ariadna 256
        $output .= $this->view_page_tertiary_nav($viewobj);
1 efrain 257
        $output .= $this->view_information($quiz, $cm, $context, $viewobj->infomessages);
258
        $output .= $this->view_table($quiz, $context, $viewobj);
259
        $output .= $this->view_result_info($quiz, $context, $cm, $viewobj);
260
        $output .= $this->box($this->view_page_buttons($viewobj), 'rui-quizattempt');
261
        return $output;
262
    }
263
 
264
    /**
265
     * Outputs the table containing data from summary data array
266
     *
267
     * @param array $summarydata contains row data for table
268
     * @param int $page contains the current page number
269
     */
710 ariadna 270
    public function review_summary_table($summarydata, $page)
271
    {
1 efrain 272
        $summarydata = $this->filter_review_summary_table($summarydata, $page);
273
        if (empty($summarydata)) {
274
            return '';
275
        }
276
 
277
        $output = '';
278
 
279
        $output .= html_writer::start_tag('div', array('class' => 'rui-summary-table'));
280
 
281
        $output .= html_writer::start_tag('div', array('class' => 'rui-info-container rui-quizreviewsummary'));
282
 
283
        foreach ($summarydata as $rowdata => $val) {
284
 
285
            $csstitle = $rowdata;
286
 
287
            if ($val['title'] instanceof renderable) {
288
                $title = $this->render($val['title']);
289
            } else {
290
                $title = $val['title'];
291
            }
292
 
293
            if ($val['content'] instanceof renderable) {
294
                $content = $this->render($val['content']);
295
            } else {
296
                $content = $val['content'];
297
            }
298
 
299
            if ($val['title'] instanceof renderable) {
300
                $output .= html_writer::tag(
301
                    'div',
302
                    html_writer::tag('h5', $title, array('class' => 'rui-infobox-title')) .
303
                        html_writer::tag('div', $content, array('class' => 'rui-infobox-content--small')),
304
                    array('class' => 'rui-infobox rui-infobox--avatar')
305
                );
306
            } else {
307
                $output .= html_writer::tag(
308
                    'div',
309
                    html_writer::tag('h5', $title, array('class' => 'rui-infobox-title')) .
310
                        html_writer::tag('div', $content, array('class' => 'rui-infobox-content--small')),
311
                    array('class' => 'rui-infobox rui-infobox--' . strtolower(str_replace(' ', '', $csstitle)))
312
                );
313
            }
314
        }
315
 
316
        $output .= html_writer::end_tag('div');
317
        $output .= html_writer::end_tag('div');
318
 
319
        return $output;
320
    }
321
 
322
    /**
323
     * Generates the table of summarydata
324
     *
325
     * @param quiz_attempt $attemptobj
326
     * @param mod_quiz_display_options $displayoptions
327
     */
710 ariadna 328
    public function summary_table($attemptobj, $displayoptions)
329
    {
1 efrain 330
        // Prepare the summary table header.
331
        $table = new html_table();
332
        $table->attributes['class'] = 'generaltable quizsummaryofattempt';
333
        $table->head = array(get_string('question', 'quiz'), get_string('status', 'quiz'));
334
        $table->align = array('left', 'left');
335
        $table->size = array('', '');
336
        $markscolumn = $displayoptions->marks >= question_display_options::MARK_AND_MAX;
337
        if ($markscolumn) {
338
            $table->head[] = get_string('marks', 'quiz');
339
            $table->align[] = 'left';
340
            $table->size[] = '';
341
        }
342
        $tablewidth = count($table->align);
343
        $table->data = array();
344
 
345
        // Get the summary info for each question.
346
        $slots = $attemptobj->get_slots();
347
        foreach ($slots as $slot) {
348
            // Add a section headings if we need one here.
349
            $heading = $attemptobj->get_heading_before_slot($slot);
350
 
351
            if ($heading !== null) {
352
                // There is a heading here.
353
                $rowclasses = 'quizsummaryheading';
354
                if ($heading) {
355
                    $heading = format_string($heading);
356
                } else if (count($attemptobj->get_quizobj()->get_sections()) > 1) {
357
                    // If this is the start of an unnamed section, and the quiz has more
358
                    // than one section, then add a default heading.
359
                    $heading = get_string('sectionnoname', 'quiz');
360
                    $rowclasses .= ' dimmed_text';
361
                }
362
                $cell = new html_table_cell(format_string($heading));
363
                $cell->header = true;
364
                $cell->colspan = $tablewidth;
365
                $table->data[] = array($cell);
366
                $table->rowclasses[] = $rowclasses;
367
            }
368
 
369
            // Don't display information items.
370
            if (!$attemptobj->is_real_question($slot)) {
371
                continue;
372
            }
373
 
374
            $flag = '';
375
 
376
            // Real question, show it.
377
            if ($attemptobj->is_question_flagged($slot)) {
378
                // Quiz has custom JS manipulating these image tags - so we can't use the pix_icon method here.
379
                $flag = '<svg class="ml-2"
380
                    width="20"
381
                    height="20"
382
                    viewBox="0 0 24 24"
383
                    fill="none"
384
                    xmlns="http://www.w3.org/2000/svg"
385
                >
386
                <path d="M4.75 5.75V19.25"
387
                    stroke="currentColor"
388
                    stroke-width="2"
389
                    stroke-linecap="round"
390
                    stroke-linejoin="round">
391
                </path>
392
                <path d="M4.75 15.25V5.75C4.75 5.75 6 4.75 9 4.75C12 4.75 13.5 6.25 16 6.25C18.5
393
                6.25 19.25 5.75 19.25 5.75L15.75 10.5L19.25 15.25C19.25 15.25 18.5 16.25 16
394
                16.25C13.5 16.25 11.5 14.25 9 14.25C6.5 14.25 4.75 15.25 4.75 15.25Z"
395
                    stroke="currentColor"
396
                    stroke-width="2"
397
                    stroke-linecap="round"
398
                    stroke-linejoin="round"></path>
399
                </svg>';
400
            }
401
            if ($attemptobj->can_navigate_to($slot)) {
402
                $row = array(
403
                    html_writer::link(
404
                        $attemptobj->attempt_url($slot),
405
                        $attemptobj->get_question_number($slot) . $flag
406
                    ),
407
                    $attemptobj->get_question_status($slot, $displayoptions->correctness)
408
                );
409
            } else {
410
                $row = array(
411
                    $attemptobj->get_question_number($slot) . $flag,
412
                    $attemptobj->get_question_status($slot, $displayoptions->correctness)
413
                );
414
            }
415
            if ($markscolumn) {
416
                $row[] = $attemptobj->get_question_mark($slot);
417
            }
418
            $table->data[] = $row;
419
            $table->rowclasses[] = 'quizsummary' . $slot . ' ' . $attemptobj->get_question_state_class(
420
                $slot,
421
                $displayoptions->correctness
422
            );
423
        }
424
 
425
        // Print the summary table.
426
        $output = html_writer::table($table);
427
 
428
        return $output;
429
    }
430
}
431
 
432
require_once($CFG->dirroot . "/question/engine/renderer.php");
710 ariadna 433
class theme_universe_core_question_renderer extends core_question_renderer
434
{
1 efrain 435
    /**
436
     * Generate the information bit of the question display that contains the
437
     * metadata like the question number, current state, and mark.
438
     * @param question_attempt $qa the question attempt to display.
439
     * @param qbehaviour_renderer $behaviouroutput the renderer to output the behaviour
440
     *      specific parts.
441
     * @param qtype_renderer $qtoutput the renderer to output the question type
442
     *      specific parts.
443
     * @param question_display_options $options controls what should and should not be displayed.
444
     * @param string|null $number The question number to display. 'i' is a special
445
     *      value that gets displayed as Information. Null means no number is displayed.
446
     * @return HTML fragment.
447
     */
448
    protected function info(
449
        question_attempt $qa,
450
        qbehaviour_renderer $behaviouroutput,
451
        qtype_renderer $qtoutput,
452
        question_display_options $options,
453
        $number
454
    ) {
455
        $output = '';
456
        $output .= '<div class="d-flex align-items-center flex-wrap mb-sm-2 mb-md-0">' .
457
            $this->number($number) .
458
            '<div class="d-inline-flex align-items-center flex-wrap">' .
459
            $this->status($qa, $behaviouroutput, $options) .
460
            $this->mark_summary($qa, $behaviouroutput, $options) .
461
            '</div></div>';
462
        $output .= '<div>' .
463
            $this->question_flag($qa, $options->flags) .
464
            $this->edit_question_link($qa, $options) .
465
            '</div>';
466
        return $output;
467
    }
468
 
469
    /**
470
     * Generate the display of the question number.
471
     * @param string|null $number The question number to display. 'i' is a special
472
     *      value that gets displayed as Information. Null means no number is displayed.
473
     * @return HTML fragment.
474
     */
710 ariadna 475
    protected function number($number)
476
    {
1 efrain 477
        if (trim($number) === '') {
478
            return '';
479
        }
480
        $numbertext = '';
481
        if (trim($number) === 'i') {
482
            $numbertext = get_string('information', 'question');
483
        } else {
484
            $numbertext = get_string(
485
                'questionx',
486
                'question',
487
                html_writer::tag('span', $number, array('class' => 'rui-qno'))
488
            );
489
        }
490
        return html_writer::tag('h4', $numbertext, array('class' => 'h3 w-100 mb-2'));
491
    }
492
 
493
 
494
    /**
495
     * Generate the display of the status line that gives the current state of
496
     * the question.
497
     * @param question_attempt $qa the question attempt to display.
498
     * @param qbehaviour_renderer $behaviouroutput the renderer to output the behaviour
499
     *      specific parts.
500
     * @param question_display_options $options controls what should and should not be displayed.
501
     * @return HTML fragment.
502
     */
503
    protected function status(
504
        question_attempt $qa,
505
        qbehaviour_renderer $behaviouroutput,
506
        question_display_options $options
507
    ) {
508
        return html_writer::tag(
509
            'div',
510
            $qa->get_state_string($options->correctness),
715 ariadna 511
            array('class' => 'state mr-2')
1 efrain 512
        );
513
    }
514
 
515
    /**
516
     * Render the question flag, assuming $flagsoption allows it.
517
     *
518
     * @param question_attempt $qa the question attempt to display.
519
     * @param int $flagsoption the option that says whether flags should be displayed.
520
     */
710 ariadna 521
    protected function question_flag(question_attempt $qa, $flagsoption)
522
    {
1 efrain 523
        global $CFG;
524
 
525
        $divattributes = array('class' => 'questionflag mx-1 d-none');
526
 
527
        switch ($flagsoption) {
528
            case question_display_options::VISIBLE:
529
                $flagcontent = $this->get_flag_html($qa->is_flagged());
530
                break;
531
 
532
            case question_display_options::EDITABLE:
533
                $id = $qa->get_flag_field_name();
534
                $checkboxattributes = array(
535
                    'type' => 'checkbox',
536
                    'id' => $id . 'checkbox',
537
                    'name' => $id,
538
                    'value' => 1,
539
                );
540
                if ($qa->is_flagged()) {
541
                    $checkboxattributes['checked'] = 'checked';
542
                }
543
                $postdata = question_flags::get_postdata($qa);
544
 
545
                $flagcontent = html_writer::empty_tag(
546
                    'input',
547
                    array('type' => 'hidden', 'name' => $id, 'value' => 0)
548
                ) .
549
                    html_writer::empty_tag('input', $checkboxattributes) .
550
                    html_writer::empty_tag(
551
                        'input',
552
                        array('type' => 'hidden', 'value' => $postdata, 'class' => 'questionflagpostdata')
553
                    ) .
554
                    html_writer::tag(
555
                        'label',
556
                        $this->get_flag_html($qa->is_flagged(), $id . 'img'),
557
                        array('id' => $id . 'label', 'for' => $id . 'checkbox')
558
                    ) . "\n";
559
 
560
                $divattributes = array(
561
                    'class' => 'questionflag mb-sm-2 mb-md-0 mx-md-2 editable d-inline-flex',
562
                    'aria-atomic' => 'true',
563
                    'aria-relevant' => 'text',
564
                    'aria-live' => 'assertive',
565
                );
566
 
567
                break;
568
 
569
            default:
570
                $flagcontent = '';
571
        }
572
 
573
        return html_writer::nonempty_tag('div', $flagcontent, $divattributes);
574
    }
575
 
576
 
710 ariadna 577
    protected function edit_question_link(question_attempt $qa, question_display_options $options)
578
    {
1 efrain 579
        global $CFG;
580
 
581
        if (empty($options->editquestionparams)) {
582
            return '';
583
        }
584
 
585
        $params = $options->editquestionparams;
586
        if ($params['returnurl'] instanceof moodle_url) {
587
            $params['returnurl'] = $params['returnurl']->out_as_local_url(false);
588
        }
589
        $params['id'] = $qa->get_question_id();
590
        $editurl = new moodle_url('/question/bank/editquestion/question.php', $params);
591
 
592
        $icon = '<svg width="19" height="19" fill="none" viewBox="0 0 24 24">
593
        <path stroke="currentColor"
594
            stroke-linecap="round"
595
            stroke-linejoin="round"
596
            stroke-width="2"
597
            d="M4.75 19.25L9 18.25L18.2929 8.95711C18.6834 8.56658 18.6834
598
            7.93342 18.2929 7.54289L16.4571 5.70711C16.0666 5.31658 15.4334
599
            5.31658 15.0429 5.70711L5.75 15L4.75 19.25Z">
600
        </path>
601
        <path stroke="currentColor"
602
            stroke-linecap="round"
603
            stroke-linejoin="round"
604
            stroke-width="2"
605
            d="M19.25 19.25H13.75">
606
        </path>
607
        </svg>';
608
 
710 ariadna 609
        return html_writer::link(
610
            $editurl,
611
            $icon,
612
            array('class' => 'btn btn-icon btn-secondary editquestion line-height-1 ml-sm-2')
613
        );
1 efrain 614
    }
615
}