Proyectos de Subversion Moodle

Rev

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