<?php
// This file is part of mod_offlinequiz for Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Creates DB-entries and PDF forms for offlinequizzes
 *
 *
 * @package       mod_offlinequiz
 * @subpackage    offlinequiz
 * @author        Juergen Zimmer <zimmerj7@univie.ac.at>
 * @copyright     2015 Academic Moodle Cooperation {@link http://www.academic-moodle-cooperation.org}
 * @since         Moodle 2.2+
 * @license       http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
use mod_offlinequiz\constants\offlinequiz_page;
require_once("../../config.php");
require_once('locallib.php');
require_once('pdflib.php');
require_once($CFG->libdir . '/questionlib.php');

$id = optional_param('id', 0, PARAM_INT);               // Course Module ID.
$q = optional_param('q', 0, PARAM_INT);                 // Or offlinequiz ID.
$forcenew = optional_param('forcenew', false, PARAM_BOOL); // Reshuffle questions.
$forcepdfnew = optional_param('forcepdfnew', false, PARAM_BOOL); // Recreate PDFs.
$mode = optional_param('mode', 'preview', PARAM_ALPHA);        // Mode.
$downloadall = optional_param('downloadall', false, PARAM_BOOL);

$letterstr = 'ABCDEFGHIJKL';

[$offlinequiz, $course, $cm] = get_course_objects($id, $q);

$offlinequiz->optionflags = 0;

require_login($course->id, false, $cm);
if (!$context = context_module::instance($cm->id)) {
    throw new \moodle_exception("The context for the course module with ID $cm->id is missing");
}
$offlinequiz->cmid = $cm->id;

$coursecontext = context_course::instance($course->id);

// We redirect students to info.
if (!has_capability('mod/offlinequiz:createofflinequiz', $context)) {
    redirect('view.php?q=' . $offlinequiz->id);
}

// If not in all group questions have been set up yet redirect to edit.php.

$strpreview = get_string('createquiz', 'offlinequiz');
$strofflinequizzes = get_string("modulenameplural", "offlinequiz");
$completion = new completion_info($course);
$completion->set_module_viewed($cm);
$PAGE->set_url(new moodle_url('/mod/offlinequiz/createquiz.php', ['id' => $cm->id]));
$PAGE->set_title($strpreview);
$PAGE->activityheader->disable();
$PAGE->set_heading($course->fullname);
$PAGE->set_pagelayout('report');
$PAGE->set_cacheable(true);
$PAGE->force_settings_menu(true);

if ($node = $PAGE->settingsnav->find('mod_offlinequiz_createquiz', core\navigation\navigation_node::TYPE_SETTING)) {
    $node->make_active();
}

if (
    !$groups = $DB->get_records(
        'offlinequiz_groups',
        ['offlinequizid' => $offlinequiz->id],
        'groupnumber',
        '*',
        0,
        $offlinequiz->numgroups
    )
) {
    throw new \moodle_exception(
        'There are no offlinequiz groups',
        'mod_offlinequiz',
        new moodle_url('/mod/offlinequiz/edit.php', ['q' => $offlinequiz->id, 'sesskey' => sesskey()])
    );
}

// Redmine 2131: Handle download all before any HTML output is produced.
if ($downloadall && $offlinequiz->docscreated) {
    $fs = get_file_storage();

    $date = usergetdate(time());
    $timestamp = sprintf(
        '%04d%02d%02d_%02d%02d%02d',
        $date['year'],
        $date['mon'],
        $date['mday'],
        $date['hours'],
        $date['minutes'],
        $date['seconds']
    );


    $shortname = $DB->get_field('course', 'shortname', ['id' => $offlinequiz->course]);
    $zipfilename = clean_filename($shortname . '_' . $offlinequiz->name . '_' . $timestamp . '.zip');
    $zipfilename = str_replace(' ', '_', $zipfilename);
    $tempzip = tempnam($CFG->tempdir . '/', 'offlinequizzip');
    $filelist = [];

    $questionpath = clean_filename(get_string('questionforms', 'offlinequiz'));
    $answerpath = clean_filename(get_string('answerforms', 'offlinequiz'));
    $correctionpath = clean_filename(get_string('correctionforms', 'offlinequiz'));

    // Simply packing all files in the 'pdfs' filearea does not work.
    // We have to read the file names from the offlinequiz_groups table.
    foreach ($groups as $group) {
        if ($questionfile = $fs->get_file($context->id, 'mod_offlinequiz', 'pdfs', 0, '/', $group->questionfilename)) {
            $filelist[$questionpath . '/' . $questionfile->get_filename()] = $questionfile;
        }

        if ($answerfile = $fs->get_file($context->id, 'mod_offlinequiz', 'pdfs', 0, '/', $group->answerfilename)) {
            $filelist[$answerpath . '/' . $answerfile->get_filename()] = $answerfile;
        }

        if ($correctionfile = $fs->get_file($context->id, 'mod_offlinequiz', 'pdfs', 0, '/', $group->correctionfilename)) {
            $filelist[$correctionpath . '/' . $correctionfile->get_filename()] = $correctionfile;
        }
    }

    $zipper = new zip_packer();

    if ($zipper->archive_to_pathname($filelist, $tempzip)) {
        send_temp_file($tempzip, $zipfilename);
    }
}
$hasscannedpages = offlinequiz_has_scanned_pages($offlinequiz->id);
// Delete the PDF forms if forcepdfnew and if there are no scanned pages yet.
if ($forcepdfnew) {
    if ($hasscannedpages) {
        throw new \moodle_exception(
            'Some answer forms have already been analysed',
            'mod_offlinequiz',
            new moodle_url(
                '/mod/offlinequiz/createquiz.php',
                ['q' => $offlinequiz->id, 'mode' => 'createpdfs', 'sesskey' => sesskey()]
            )
        );
    } else {
        // Redmine 2750: Always delete templates as well.
        offlinequiz_delete_template_usages($offlinequiz);
        $offlinequiz = offlinequiz_delete_pdf_forms($offlinequiz);

        $doctype = 'PDF';
        if ($offlinequiz->fileformat == OFFLINEQUIZ_DOCX_FORMAT) {
            $doctype = 'DOCX';
        } else if ($offlinequiz->fileformat == OFFLINEQUIZ_LATEX_FORMAT) {
            $doctype = 'LATEX';
        }
        $params = [
            'context' => $context,
            'other' => [
                'offlinequizid' => $offlinequiz->id,
                'reportname' => $mode,
                'doctype' => $doctype,
            ],
        ];
        $event = \mod_offlinequiz\event\docs_deleted::create($params);
        $event->trigger();
        redirect(new moodle_url('/mod/offlinequiz/createquiz.php', ['q' => $offlinequiz->id, 'mode' => 'preview']));
        die();
    }
}

// Print the page header.
echo $OUTPUT->header();

if ($offlinequiz->grade == 0) {
    echo '<div class="linkbox"><strong>';
    echo $OUTPUT->notification(get_string('gradeiszero', 'offlinequiz'), 'notifyproblem');
    echo '</strong></div>';
}

// Preview.
if ($mode == 'preview') {
    offlinequiz_print_tabs($offlinequiz, 'tabpreview', $cm);
    // Print shuffle again buttons.
    if (!$offlinequiz->docscreated && !$hasscannedpages) {
        echo $OUTPUT->box_start('generalbox controlbuttonbox');

        unset($buttonoptions);
        $buttonoptions = [];
        $buttonoptions['q'] = $offlinequiz->id;
        $buttonoptions['forcenew'] = true;
        $buttonurl = new moodle_url('/mod/offlinequiz/createquiz.php', $buttonoptions);

        echo '<div class="controlbuttons linkbox">';
        if ($offlinequiz->shufflequestions && $offlinequiz->shuffleanswers) {
            echo $OUTPUT->single_button($buttonurl, get_string('shufflequestionsanswers', 'offlinequiz') . ' / ' .
                    get_string('reloadquestionlist', 'offlinequiz'), 'post');
        } else if ($offlinequiz->shufflequestions) {
            echo $OUTPUT->single_button($buttonurl, get_string('shufflequestions', 'offlinequiz') . ' / ' .
                    get_string('reloadquestionlist', 'offlinequiz'), 'post');
        } else if ($offlinequiz->shuffleanswers) {
            echo $OUTPUT->single_button($buttonurl, get_string('shuffleanswers', 'offlinequiz') . ' / ' .
                    get_string('reloadquestionlist', 'offlinequiz'), 'post');
        } else {
            echo $OUTPUT->single_button($buttonurl, get_string('reloadquestionlist', 'offlinequiz'));
        }
        $buttonoptions['mode'] = 'createpdfs';
        echo $OUTPUT->single_button(
            new moodle_url('/mod/offlinequiz/createquiz.php', $buttonoptions),
            get_string('createpdfforms', 'offlinequiz'),
            'get',
            ['type' => 'primary']
        );

        echo '</div>';

        echo $OUTPUT->box_end();
    } else {
        $createformsurl = new moodle_url('/mod/offlinequiz/createquiz.php', ['mode' => 'createpdfs', 'q' => $offlinequiz->id]);
        $text = get_string('formsexist', 'offlinequiz', $createformsurl->out(false));
        echo "<p>$text";
        echo $OUTPUT->single_button(
            $createformsurl,
            get_string('createpdfs', 'offlinequiz'),
            'get'
        );
        echo "</p>";
    }
    // Shuffle again if no scanned pages.
    if ($forcenew) {
        if ($offlinequiz->docscreated || $hasscannedpages) {
            echo $OUTPUT->notification(get_string('formsexist', 'offlinequiz'), 'notifyproblem');
        } else {
            $offlinequiz = offlinequiz_delete_template_usages($offlinequiz);
            $groups = $DB->get_records(
                'offlinequiz_groups',
                ['offlinequizid' => $offlinequiz->id],
                'groupnumber',
                '*',
                0,
                $offlinequiz->numgroups
            );
        }
    }

    echo $OUTPUT->heading(get_string('formspreview', 'offlinequiz'));
    $done = 0;
    // Process group data.
    foreach ($groups as $group) {
        $groupletter = $letterstr[$group->groupnumber - 1];

        // Print the group heading.
        echo $OUTPUT->heading(get_string('previewforgroup', 'offlinequiz', $groupletter));

        echo $OUTPUT->box_start('generalbox groupcontainer');

        // Load all the questions needed for this offline quiz group.
        $sql = "SELECT q.*, c.contextid, ogq.page, ogq.slot, ogq.maxmark
              FROM {offlinequiz_group_questions} ogq
              JOIN {question} q ON q.id = ogq.questionid
              JOIN {question_versions} qv ON q.id = qv.questionid
              JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
              JOIN {question_categories} c ON qbe.questioncategoryid = c.id
              WHERE ogq.offlinequizid = :offlinequizid
                AND ogq.offlinegroupid = :offlinegroupid
              ORDER BY ogq.slot ASC ";
        $params = ['offlinequizid' => $offlinequiz->id, 'offlinegroupid' => $group->id];

        $questions = $DB->get_records_sql($sql, $params);

        // Load the questions.
        if (!$questions) {
            $url = new moodle_url(
                $CFG->wwwroot . '/mod/offlinequiz/edit.php',
                ['cmid' => $cm->id, 'groupnumber' => $group->groupnumber, 'noquestions' => 1]
            );
            echo html_writer::link(
                $url,
                get_string('noquestionsfound', 'offlinequiz', $groupletter),
                ['class' => 'linkbox']
            );
            echo $OUTPUT->box_end();
            continue;
        }
        // Load the question type specific information.
        if (!get_question_options($questions)) {
            throw new \moodle_exception('Could not load question options');
        }

        // Get or create a question usage for this offline group.
        if (!$templateusage = offlinequiz_get_group_template_usage($offlinequiz, $group, $context)) {
            echo $OUTPUT->notification(get_string('missingquestions', 'offlinequiz'), 'notifyproblem');
            echo $OUTPUT->box_end();
            echo $OUTPUT->footer();
            continue;
        }
        if (!$slots = $templateusage->get_slots()) {
            echo $OUTPUT->box_start('notify');
            echo $OUTPUT->notification(get_string('nomcquestions', 'offlinequiz', $groupletter));
            echo $OUTPUT->box_end();
        }

        // We need a mapping from question IDs to slots, assuming that each question occurs only once..
        $questionslots = [];
        foreach ($slots as $qid => $slot) {
            $questionslots[$templateusage->get_question($slot)->id] = $slot;
        }
        $questionnumber = 1;
        $currentpage = 1;
        if ($offlinequiz->shufflequestions) {
            foreach ($slots as $slot) {
                $slotquestion = $templateusage->get_question($slot);
                $question = $questions[$slotquestion->id];
                $attempt = $templateusage->get_question_attempt($slot);
                $order = $slotquestion->get_order($attempt);  // Order.
                offlinequiz_print_question_preview($question, $order, $questionnumber, $context, $PAGE);
                // Note: we don't have description questions in quba slots.
                $questionnumber++;
            }
        } else {
            foreach ($questions as $question) {
                if ($question->page > $currentpage) {
                    echo '<center>//---------------------- ' . get_string('newpage', 'offlinequiz') .
                            ' ----------------//</center>';
                    $currentpage++;
                }
                $order = [];
                if ($question->qtype == 'multichoice' || $question->qtype == 'multichoiceset') {
                    $slot = $questionslots[$question->id];
                    $slotquestion = $templateusage->get_question($slot);
                    $attempt = $templateusage->get_question_attempt($slot);
                    $order = $slotquestion->get_order($attempt);
                }
                // Use our own function to print the preview.
                offlinequiz_print_question_preview($question, $order, $questionnumber, $context, $PAGE);
                if ($question->qtype != 'description') {
                    $questionnumber++;
                }
            }
        }
        echo $OUTPUT->box_end();
    }// End foreach.

    // O==============================================================.
    // O TAB for creating, downloading and deleting PDF forms.
    // O==============================================================.
} else if ($mode == 'createpdfs') {
    offlinequiz_print_tabs($offlinequiz, 'tabpreview', $cm);
    // Print the heading.
    echo $OUTPUT->heading(get_string('downloadpdfs', 'offlinequiz'));

    $emptygroups = offlinequiz_get_empty_groups($offlinequiz);
    if (!empty($emptygroups)) {
        echo $OUTPUT->box_start('linkbox');
        foreach ($emptygroups as $groupnumber) {
            $groupletter = $letterstr[$groupnumber - 1];
            echo $OUTPUT->notification(get_string('noquestionsfound', 'offlinequiz', $groupletter));
        }
        echo $OUTPUT->notification(get_string('nopdfscreated', 'offlinequiz'), 'notifyproblem');
        echo $OUTPUT->box_end();

        echo $OUTPUT->footer();
        return true;
    }

    echo $OUTPUT->box_start('controlbuttons linkbox');

    $url = new moodle_url('/mod/offlinequiz/createquiz.php', ['q' => $offlinequiz->id]);
    echo $OUTPUT->single_button($url, get_string('backtopreview', 'offlinequiz'), 'get');
    echo \mod_offlinequiz\output\action_api::insert_all_actions(
        'offlinequiz',
        offlinequiz_page::CREATEQUIZ_CREATEPDFS,
        $cm,
        $offlinequiz
    );

    // Print buttons for delete/recreate iff there are no scanned pages yet.
    if (!$hasscannedpages) {
        unset($buttonoptions);
        $buttonoptions['q'] = $offlinequiz->id;
        $buttonoptions['mode'] = 'createpdfs';
        $buttonurl = new moodle_url('/mod/offlinequiz/createquiz.php', $buttonoptions);
        if ($forcepdfnew) {
            echo '<div class="singlebutton">';
            echo $OUTPUT->single_button($buttonurl, get_string('createpdfforms', 'offlinequiz'), 'get');
            echo '</div>';
        } else {
            $actionurl = new moodle_url('/mod/offlinequiz/createquiz.php', ['q' => $offlinequiz->id, 'mode' => 'createpdfs']);
            echo $OUTPUT->render_from_template(
                'mod_offlinequiz/delete_documents_button',
                ['actionurl' => $actionurl->out()]
            );
            "";
        }
    }
    echo $OUTPUT->box_end();

    $fs = get_file_storage();

    // Options for the popup_action.
    $options = [];
    $options['height'] = 1200; // Optional.
    $options['width'] = 1170; // Optional.

    // O============================================================.
    // O show/create the question forms for the offline groups.
    // O============================================================.
    if (!$forcepdfnew) {
        // Redmine 2131: Add download all link.
        $downloadallurl = new moodle_url(
            $CFG->wwwroot . '/mod/offlinequiz/createquiz.php',
            ['q' => $offlinequiz->id,
            'mode' => 'createpdfs',
            'downloadall' => 1]
        );
        echo html_writer::start_div('downloadalllink');
        echo html_writer::link($downloadallurl->out(false), get_string('downloadallzip', 'offlinequiz'));
        echo html_writer::end_div();

        echo $OUTPUT->box_start('generalbox linkbox docsbox');

        foreach ($groups as $group) {
            $groupletter = $letterstr[$group->groupnumber - 1];

            if (!$offlinequiz->docscreated) {
                if (!$templateusage = offlinequiz_get_group_template_usage($offlinequiz, $group, $context)) {
                    throw new \moodle_exception(
                        "Missing data for group " . $groupletter,
                        "createquiz.php?q=$offlinequiz->id&amp;mode=preview&amp;sesskey=" . sesskey()
                    );
                }
                $DB->set_field('offlinequiz', 'id_digits', get_config('offlinequiz', 'ID_digits'), ['id' => $offlinequiz->id]);

                if ($offlinequiz->fileformat == OFFLINEQUIZ_DOCX_FORMAT) {
                    require_once('docxlib.php');
                    $questionfile = offlinequiz_create_docx_question($templateusage, $offlinequiz, $group, $course->id, $context);
                } else if ($offlinequiz->fileformat == OFFLINEQUIZ_LATEX_FORMAT) {
                    require_once('latexlib.php');
                    $questionfile = offlinequiz_create_latex_question($templateusage, $offlinequiz, $group, $course->id, $context);
                } else {
                    $questionfile = offlinequiz_create_pdf_question($templateusage, $offlinequiz, $group, $course->id, $context);
                }
                if ($questionfile) {
                    $group->questionfilename = $questionfile->get_filename();
                    $DB->update_record('offlinequiz_groups', $group);
                }
            } else {
                $filename = $group->questionfilename;
                $questionfile = $fs->get_file($context->id, 'mod_offlinequiz', 'pdfs', 0, '/', $filename);
            }

            if ($questionfile) {
                $filestring = get_string('formforgroup', 'offlinequiz', $groupletter);
                if ($offlinequiz->fileformat == OFFLINEQUIZ_DOCX_FORMAT) {
                    $filestring = get_string('formforgroupdocx', 'offlinequiz', $groupletter);
                } else if ($offlinequiz->fileformat == OFFLINEQUIZ_LATEX_FORMAT) {
                    $filestring = get_string('formforgrouplatex', 'offlinequiz', $groupletter);
                }
                $url = "$CFG->wwwroot/pluginfile.php/" . $questionfile->get_contextid() . '/' . $questionfile->get_component() .
                            '/' . $questionfile->get_filearea() . '/' . $questionfile->get_itemid() . '/' .
                            $questionfile->get_filename() . '?forcedownload=1';
                echo $OUTPUT->action_link($url, $filestring);
                echo '<br />&nbsp;<br />';
                @flush();
                @ob_flush();
            } else {
                echo $OUTPUT->notification(get_string('createpdferror', 'offlinequiz', $groupletter));
            }
        }
        echo $OUTPUT->box_end();

        // O============================================================.
        // O Show/create the answer forms for all offline groups.
        // O============================================================.
        echo $OUTPUT->box_start('generalbox linkbox docsbox');

        echo $OUTPUT->notification(get_string('marginwarning', 'offlinequiz'));
        echo '<br/>';

        foreach ($groups as $group) {
            $groupletter = $letterstr[$group->groupnumber - 1];

            if (!$templateusage = offlinequiz_get_group_template_usage($offlinequiz, $group, $context)) {
                throw new \moodle_exception(
                    "Missing data for group " . $groupletter,
                    "createquiz.php?q=$offlinequiz->id&amp;mode=preview&amp;sesskey=" . sesskey()
                );
            }

            if (!$offlinequiz->docscreated) {
                $answerpdffile = offlinequiz_create_pdf_answer(
                    offlinequiz_get_maxanswers($offlinequiz, [$group]),
                    $templateusage,
                    $offlinequiz,
                    $group,
                    $course->id,
                    $context
                );
                if ($answerpdffile) {
                    $group->answerfilename = $answerpdffile->get_filename();
                    $DB->update_record('offlinequiz_groups', $group);
                }
            } else {
                $filename = $group->answerfilename;
                $answerpdffile = $fs->get_file($context->id, 'mod_offlinequiz', 'pdfs', 0, '/', $filename);
            }

            if ($answerpdffile) {
                $url = "$CFG->wwwroot/pluginfile.php/" . $answerpdffile->get_contextid() . '/' .
                        $answerpdffile->get_component() . '/' . $answerpdffile->get_filearea() . '/' .
                        $answerpdffile->get_itemid() . '/' . $answerpdffile->get_filename() . '?forcedownload=1';
                echo $OUTPUT->action_link($url, get_string('answerformforgroup', 'offlinequiz', $groupletter));
                echo '<br />&nbsp;<br />';
                @flush();
                @ob_flush();
            } else {
                echo $OUTPUT->notification(get_string('createpdferror', 'offlinequiz', $groupletter));
            }
        }


        echo $OUTPUT->box_end();

        // O============================================================.
        // O Show/create the correction forms for all offline groups.
        // O============================================================.
        echo $OUTPUT->box_start('generalbox linkbox docsbox');

        foreach ($groups as $group) {
            $groupletter = $letterstr[$group->groupnumber - 1];

            if (!$templateusage = offlinequiz_get_group_template_usage($offlinequiz, $group, $context)) {
                throw new \moodle_exception(
                    "Missing data for group " . $groupletter,
                    "createquiz.php?q=$offlinequiz->id&amp;mode=preview&amp;sesskey=" . sesskey()
                );
            }

            if (!$offlinequiz->docscreated) {
                $correctionpdffile = offlinequiz_create_pdf_question(
                    $templateusage,
                    $offlinequiz,
                    $group,
                    $course->id,
                    $context,
                    true
                );
                if ($correctionpdffile) {
                    $group->correctionfilename = $correctionpdffile->get_filename();
                    $DB->update_record('offlinequiz_groups', $group);
                }
            } else {
                $filename = $group->correctionfilename;
                $correctionpdffile = $fs->get_file($context->id, 'mod_offlinequiz', 'pdfs', 0, '/', $filename);
            }

            if ($correctionpdffile) {
                $url = "$CFG->wwwroot/pluginfile.php/" . $correctionpdffile->get_contextid() . '/' .
                        $correctionpdffile->get_component() . '/' . $correctionpdffile->get_filearea() . '/' .
                        $correctionpdffile->get_itemid() . '/' . $correctionpdffile->get_filename() . '?forcedownload=1';
                echo $OUTPUT->action_link($url, get_string('formforcorrection', 'offlinequiz', $groupletter));

                echo '<br />&nbsp;<br />';
                @flush();
                @ob_flush();
            } else {
                echo $OUTPUT->notification(get_string('createpdferror', 'offlinequiz', $groupletter));
            }
        }


        echo $OUTPUT->box_end();

        // Only send event if we have actually created new documents.
        if ($offlinequiz->docscreated == 0) {
            $doctype = 'PDF';
            if ($offlinequiz->fileformat == OFFLINEQUIZ_DOCX_FORMAT) {
                $doctype = 'DOCX';
            } else if ($offlinequiz->fileformat == OFFLINEQUIZ_LATEX_FORMAT) {
                $doctype = 'LATEX';
            }
            $params = [
                'context' => $context,
                'other' => [
                        'offlinequizid' => $offlinequiz->id,
                        'reportname' => $mode,
                        'doctype' => $doctype,

                ],
            ];
            $event = \mod_offlinequiz\event\docs_created::create($params);
            $event->trigger();
        }

        // Register that we have created the documents.
        $offlinequiz->docscreated = 1;
        $DB->set_field('offlinequiz', 'docscreated', 1, ['id' => $offlinequiz->id]);
    }
}

// Finish the page.
echo $OUTPUT->footer();
