Ejemplo n.º 1
0
/**
 * Prints a preview for a question in an offlinequiz to Stdout.
 *
 * @param object $question
 * @param array $choiceorder
 * @param int $number
 * @param object $context
 */
function offlinequiz_print_question_preview($question, $choiceorder, $number, $context, $page)
{
    global $CFG, $DB;
    require_once $CFG->dirroot . '/filter/mathjaxloader/filter.php';
    $letterstr = 'abcdefghijklmnopqrstuvwxyz';
    echo '<div id="q' . $question->id . '" class="preview">
            <div class="question">
              <span class="number">';
    if ($question->qtype != 'description') {
        echo $number . ')&nbsp;&nbsp;';
    }
    echo '    </span>';
    $text = question_rewrite_question_preview_urls($question->questiontext, $question->id, $question->contextid, 'question', 'questiontext', $question->id, $context->id, 'offlinequiz');
    // Remove leading paragraph tags because the cause a line break after the question number.
    $text = preg_replace('!^<p>!i', '', $text);
    // Filter only for tex formulas.
    $texfilter = null;
    $mathjaxfilter = null;
    $filters = filter_get_active_in_context($context);
    if (array_key_exists('mathjaxloader', $filters)) {
        $mathjaxfilter = new filter_mathjaxloader($context, array());
        $mathjaxfilter->setup($page, $context);
    }
    if (array_key_exists('tex', $filters)) {
        $texfilter = new filter_tex($context, array());
    }
    if ($mathjaxfilter) {
        $text = $mathjaxfilter->filter($text);
        if ($question->qtype != 'description') {
            foreach ($choiceorder as $key => $answer) {
                $question->options->answers[$answer]->answer = $mathjaxfilter->filter($question->options->answers[$answer]->answer);
            }
        }
    } else {
        if ($texfilter) {
            $text = $texfilter->filter($text);
            if ($question->qtype != 'description') {
                foreach ($choiceorder as $key => $answer) {
                    $question->options->answers[$answer]->answer = $texfilter->filter($question->options->answers[$answer]->answer);
                }
            }
        }
    }
    echo $text;
    echo '  </div>';
    if ($question->qtype != 'description') {
        echo '  <div class="grade">';
        echo '(' . get_string('marks', 'quiz') . ': ' . ($question->maxmark + 0) . ')';
        echo '  </div>';
        foreach ($choiceorder as $key => $answer) {
            $answertext = $question->options->answers[$answer]->answer;
            // Remove all HTML comments (typically from MS Office).
            $answertext = preg_replace("/<!--.*?--\\s*>/ms", "", $answertext);
            // Remove all paragraph tags because they mess up the layout.
            $answertext = preg_replace("/<p[^>]*>/ms", "", $answertext);
            $answertext = preg_replace("/<\\/p[^>]*>/ms", "", $answertext);
            echo "<div class=\"answer\">{$letterstr[$key]})&nbsp;&nbsp;";
            echo $answertext;
            echo "</div>";
        }
    }
    echo "</div>";
}
Ejemplo n.º 2
0
/**
 * Generates the PDF question/correction form for an offlinequiz group.
 *
 * @param question_usage_by_activity $templateusage the template question  usage for this offline group
 * @param object $offlinequiz The offlinequiz object
 * @param object $group the offline group object
 * @param int $courseid the ID of the Moodle course
 * @param object $context the context of the offline quiz.
 * @param boolean correction if true the correction form is generated.
 * @return stored_file instance, the generated PDF file.
 */
function offlinequiz_create_pdf_question(question_usage_by_activity $templateusage, $offlinequiz, $group, $courseid, $context, $correction = false)
{
    global $CFG, $DB, $OUTPUT;
    $letterstr = 'abcdefghijklmnopqrstuvwxyz';
    $groupletter = strtoupper($letterstr[$group->number - 1]);
    $coursecontext = context_course::instance($courseid);
    $pdf = new offlinequiz_question_pdf('P', 'mm', 'A4');
    $trans = new offlinequiz_html_translator();
    $title = offlinequiz_str_html_pdf($offlinequiz->name);
    if (!empty($offlinequiz->time)) {
        $title .= ": " . offlinequiz_str_html_pdf(userdate($offlinequiz->time));
    }
    $title .= ",  " . offlinequiz_str_html_pdf(get_string('group') . " {$groupletter}");
    $pdf->set_title($title);
    $pdf->SetMargins(15, 28, 15);
    $pdf->SetAutoPageBreak(false, 25);
    $pdf->AddPage();
    // Print title page.
    $pdf->SetFont('FreeSans', 'B', 14);
    $pdf->Ln(4);
    if (!$correction) {
        $pdf->Cell(0, 4, offlinequiz_str_html_pdf(get_string('questionsheet', 'offlinequiz')), 0, 0, 'C');
        $pdf->Rect(34, 46, 137, 53, 'D');
        $pdf->SetFont('FreeSans', '', 10);
        // Line breaks to position name string etc. properly.
        $pdf->Ln(20);
        $pdf->Cell(58, 10, offlinequiz_str_html_pdf(get_string('name')) . ":", 0, 0, 'R');
        $pdf->Rect(76, 60, 80, 0.3, 'F');
        $pdf->Ln(10);
        $pdf->Cell(58, 10, offlinequiz_str_html_pdf(get_string('idnumber', 'offlinequiz')) . ":", 0, 0, 'R');
        $pdf->Rect(76, 70, 80, 0.3, 'F');
        $pdf->Ln(10);
        $pdf->Cell(58, 10, offlinequiz_str_html_pdf(get_string('studycode', 'offlinequiz')) . ":", 0, 0, 'R');
        $pdf->Rect(76, 80, 80, 0.3, 'F');
        $pdf->Ln(10);
        $pdf->Cell(58, 10, offlinequiz_str_html_pdf(get_string('signature', 'offlinequiz')) . ":", 0, 0, 'R');
        $pdf->Rect(76, 90, 80, 0.3, 'F');
        $pdf->Ln(33);
        $pdf->SetFont('FreeSans', '', $offlinequiz->fontsize);
        $pdf->SetFontSize($offlinequiz->fontsize);
        // The PDF intro text can be arbitrarily long so we have to catch page overflows.
        if (!empty($offlinequiz->pdfintro)) {
            $oldx = $pdf->GetX();
            $oldy = $pdf->GetY();
            $pdf->checkpoint();
            $pdf->writeHTMLCell(165, round($offlinequiz->fontsize / 2), $pdf->GetX(), $pdf->GetY(), $offlinequiz->pdfintro);
            $pdf->Ln();
            if ($pdf->is_overflowing()) {
                $pdf->backtrack();
                $pdf->SetX($oldx);
                $pdf->SetY($oldy);
                $paragraphs = preg_split('/<p>/', $offlinequiz->pdfintro);
                foreach ($paragraphs as $paragraph) {
                    if (!empty($paragraph)) {
                        $sentences = preg_split('/<br\\s*\\/>/', $paragraph);
                        foreach ($sentences as $sentence) {
                            $pdf->checkpoint();
                            $pdf->writeHTMLCell(165, round($offlinequiz->fontsize / 2), $pdf->GetX(), $pdf->GetY(), $sentence . '<br/>');
                            $pdf->Ln();
                            if ($pdf->is_overflowing()) {
                                $pdf->backtrack();
                                $pdf->AddPage();
                                $pdf->Ln(14);
                                $pdf->writeHTMLCell(165, round($offlinequiz->fontsize / 2), $pdf->GetX(), $pdf->GetY(), $sentence);
                                $pdf->Ln();
                            }
                        }
                    }
                }
            }
        }
        $pdf->AddPage();
        $pdf->Ln(2);
    }
    $pdf->SetMargins(15, 15, 15);
    // Load all the questions needed for this offline quiz group.
    $sql = "SELECT q.*, c.contextid, ogq.page, ogq.slot, ogq.maxmark \n              FROM {offlinequiz_group_questions} ogq,\n                   {question} q,\n                   {question_categories} c\n             WHERE ogq.offlinequizid = :offlinequizid\n               AND ogq.offlinegroupid = :offlinegroupid\n               AND q.id = ogq.questionid\n               AND q.category = c.id\n          ORDER BY ogq.slot ASC ";
    $params = array('offlinequizid' => $offlinequiz->id, 'offlinegroupid' => $group->id);
    // Load the questions.
    $questions = $DB->get_records_sql($sql, $params);
    if (!$questions) {
        echo $OUTPUT->box_start();
        echo $OUTPUT->error_text(get_string('noquestionsfound', 'offlinequiz', $groupletter));
        echo $OUTPUT->box_end();
        return;
    }
    // Load the question type specific information.
    if (!get_question_options($questions)) {
        print_error('Could not load question options');
    }
    // Restore the question sessions to their most recent states.
    // Creating new sessions where required.
    $number = 1;
    // We need a mapping from question IDs to slots, assuming that each question occurs only once.
    $slots = $templateusage->get_slots();
    $texfilter = new filter_tex($context, array());
    // If shufflequestions has been activated we go through the questions in the order determined by
    // the template question usage.
    if ($offlinequiz->shufflequestions) {
        foreach ($slots as $slot) {
            $slotquestion = $templateusage->get_question($slot);
            $currentquestionid = $slotquestion->id;
            // Add page break if necessary because of overflow.
            if ($pdf->GetY() > 230) {
                $pdf->AddPage();
                $pdf->Ln(14);
            }
            set_time_limit(120);
            $question = $questions[$currentquestionid];
            /*****************************************************/
            /*  Either we print the question HTML */
            /*****************************************************/
            $pdf->checkpoint();
            $questiontext = $question->questiontext;
            // Filter only for tex formulas.
            if (!empty($texfilter)) {
                $questiontext = $texfilter->filter($questiontext);
            }
            // Remove all HTML comments (typically from MS Office).
            $questiontext = preg_replace("/<!--.*?--\\s*>/ms", "", $questiontext);
            // Remove <font> tags.
            $questiontext = preg_replace("/<font[^>]*>[^<]*<\\/font>/ms", "", $questiontext);
            // Remove <script> tags that are created by mathjax preview.
            $questiontext = preg_replace("/<script[^>]*>[^<]*<\\/script>/ms", "", $questiontext);
            // Remove all class info from paragraphs because TCPDF won't use CSS.
            $questiontext = preg_replace('/<p[^>]+class="[^"]*"[^>]*>/i', "<p>", $questiontext);
            $questiontext = $trans->fix_image_paths($questiontext, $question->contextid, 'questiontext', $question->id, 1, 300);
            $html = '';
            $html .= $questiontext . '<br/><br/>';
            if ($question->qtype == 'multichoice' || $question->qtype == 'multichoiceset') {
                // Save the usage slot in the group questions table.
                //                 $DB->set_field('offlinequiz_group_questions', 'usageslot', $slot,
                //                         array('offlinequizid' => $offlinequiz->id,
                //                                 'offlinegroupid' => $group->id, 'questionid' => $question->id));
                // There is only a slot for multichoice questions.
                $attempt = $templateusage->get_question_attempt($slot);
                $order = $slotquestion->get_order($attempt);
                // Order of the answers.
                foreach ($order as $key => $answer) {
                    $answertext = $question->options->answers[$answer]->answer;
                    // Filter only for tex formulas.
                    if (!empty($texfilter)) {
                        $answertext = $texfilter->filter($answertext);
                    }
                    // Remove all HTML comments (typically from MS Office).
                    $answertext = preg_replace("/<!--.*?--\\s*>/ms", "", $answertext);
                    // Remove all paragraph tags because they mess up the layout.
                    $answertext = preg_replace("/<p[^>]*>/ms", "", $answertext);
                    // Remove <script> tags that are created by mathjax preview.
                    $answertext = preg_replace("/<script[^>]*>[^<]*<\\/script>/ms", "", $answertext);
                    $answertext = preg_replace("/<\\/p[^>]*>/ms", "", $answertext);
                    $answertext = $trans->fix_image_paths($answertext, $question->contextid, 'answer', $answer, 1, 300);
                    if ($correction) {
                        if ($question->options->answers[$answer]->fraction > 0) {
                            $html .= '<b>';
                        }
                        $answertext .= " (" . round($question->options->answers[$answer]->fraction * 100) . "%)";
                    }
                    $html .= number_in_style($key, $question->options->answernumbering) . ') &nbsp; ';
                    $html .= $answertext;
                    if ($correction) {
                        if ($question->options->answers[$answer]->fraction > 0) {
                            $html .= '</b>';
                        }
                    }
                    $html .= "<br/>\n";
                }
                if ($offlinequiz->showgrades) {
                    $pointstr = get_string('points', 'grades');
                    if ($question->maxmark == 1) {
                        $pointstr = get_string('point', 'offlinequiz');
                    }
                    $html .= '<br/>(' . ($question->maxmark + 0) . ' ' . $pointstr . ')<br/>';
                }
            }
            // Finally print the question number and the HTML string.
            if ($question->qtype == 'multichoice' || $question->qtype == 'multichoiceset') {
                $pdf->SetFont('FreeSans', 'B', $offlinequiz->fontsize);
                $pdf->Cell(4, round($offlinequiz->fontsize / 2), "{$number})  ", 0, 0, 'R');
                $pdf->SetFont('FreeSans', '', $offlinequiz->fontsize);
            }
            $pdf->writeHTMLCell(165, round($offlinequiz->fontsize / 2), $pdf->GetX(), $pdf->GetY() + 0.3, $html);
            $pdf->Ln();
            if ($pdf->is_overflowing()) {
                $pdf->backtrack();
                $pdf->AddPage();
                $pdf->Ln(14);
                // Print the question number and the HTML string again on the new page.
                if ($question->qtype == 'multichoice' || $question->qtype == 'multichoiceset') {
                    $pdf->SetFont('FreeSans', 'B', $offlinequiz->fontsize);
                    $pdf->Cell(4, round($offlinequiz->fontsize / 2), "{$number})  ", 0, 0, 'R');
                    $pdf->SetFont('FreeSans', '', $offlinequiz->fontsize);
                }
                $pdf->writeHTMLCell(165, round($offlinequiz->fontsize / 2), $pdf->GetX(), $pdf->GetY() + 0.3, $html);
                $pdf->Ln();
            }
            $number += $questions[$currentquestionid]->length;
        }
    } else {
        // No shufflequestions, so go through the questions as they have been added to the offlinequiz group.
        // We also have to show description questions that are not in the template.
        // First, compute mapping  questionid -> slotnumber.
        $questionslots = array();
        foreach ($slots as $slot) {
            $questionslots[$templateusage->get_question($slot)->id] = $slot;
        }
        $currentpage = 1;
        foreach ($questions as $question) {
            $currentquestionid = $question->id;
            // Add page break if set explicitely by teacher.
            if ($question->page > $currentpage) {
                $pdf->AddPage();
                $pdf->Ln(14);
                $currentpage++;
            }
            // Add page break if necessary because of overflow.
            if ($pdf->GetY() > 230) {
                $pdf->AddPage();
                $pdf->Ln(14);
            }
            set_time_limit(120);
            /**
             * **************************************************
             * either we print the question HTML 
             * **************************************************
             */
            $pdf->checkpoint();
            $questiontext = $question->questiontext;
            // Filter only for tex formulas.
            if (!empty($texfilter)) {
                $questiontext = $texfilter->filter($questiontext);
            }
            // Remove all HTML comments (typically from MS Office).
            $questiontext = preg_replace("/<!--.*?--\\s*>/ms", "", $questiontext);
            // Remove <font> tags.
            $questiontext = preg_replace("/<font[^>]*>[^<]*<\\/font>/ms", "", $questiontext);
            // Remove <script> tags that are created by mathjax preview.
            $questiontext = preg_replace("/<script[^>]*>[^<]*<\\/script>/ms", "", $questiontext);
            // Remove all class info from paragraphs because TCPDF won't use CSS.
            $questiontext = preg_replace('/<p[^>]+class="[^"]*"[^>]*>/i', "<p>", $questiontext);
            $questiontext = $trans->fix_image_paths($questiontext, $question->contextid, 'questiontext', $question->id, 1, 300);
            $html = '';
            $html .= $questiontext . '<br/><br/>';
            if ($question->qtype == 'multichoice' || $question->qtype == 'multichoiceset') {
                $slot = $questionslots[$currentquestionid];
                // Save the usage slot in the group questions table.
                // $DB->set_field('offlinequiz_group_questions', 'usageslot', $slot,
                // array('offlinequizid' => $offlinequiz->id,
                // 'offlinegroupid' => $group->id, 'questionid' => $question->id));
                // There is only a slot for multichoice questions.
                $slotquestion = $templateusage->get_question($slot);
                $attempt = $templateusage->get_question_attempt($slot);
                $order = $slotquestion->get_order($attempt);
                // Order of the answers.
                foreach ($order as $key => $answer) {
                    $answertext = $question->options->answers[$answer]->answer;
                    // Filter only for tex formulas.
                    if (!empty($texfilter)) {
                        $answertext = $texfilter->filter($answertext);
                    }
                    // Remove all HTML comments (typically from MS Office).
                    $answertext = preg_replace("/<!--.*?--\\s*>/ms", "", $answertext);
                    // Remove all paragraph tags because they mess up the layout.
                    $answertext = preg_replace("/<p[^>]*>/ms", "", $answertext);
                    // Remove <script> tags that are created by mathjax preview.
                    $answertext = preg_replace("/<script[^>]*>[^<]*<\\/script>/ms", "", $answertext);
                    $answertext = preg_replace("/<\\/p[^>]*>/ms", "", $answertext);
                    $answertext = $trans->fix_image_paths($answertext, $question->contextid, 'answer', $answer, 1, 300);
                    // Was $pdf->GetK()).
                    if ($correction) {
                        if ($question->options->answers[$answer]->fraction > 0) {
                            $html .= '<b>';
                        }
                        $answertext .= " (" . round($question->options->answers[$answer]->fraction * 100) . "%)";
                    }
                    $html .= number_in_style($key, $question->options->answernumbering) . ') &nbsp; ';
                    $html .= $answertext;
                    if ($correction) {
                        if ($question->options->answers[$answer]->fraction > 0) {
                            $html .= '</b>';
                        }
                    }
                    $html .= "<br/>\n";
                }
                if ($offlinequiz->showgrades) {
                    $pointstr = get_string('points', 'grades');
                    if ($question->maxmark == 1) {
                        $pointstr = get_string('point', 'offlinequiz');
                    }
                    $html .= '<br/>(' . ($question->maxmark + 0) . ' ' . $pointstr . ')<br/>';
                }
            }
            // Finally print the question number and the HTML string.
            if ($question->qtype == 'multichoice' || $question->qtype == 'multichoiceset') {
                $pdf->SetFont('FreeSans', 'B', $offlinequiz->fontsize);
                $pdf->Cell(4, round($offlinequiz->fontsize / 2), "{$number})  ", 0, 0, 'R');
                $pdf->SetFont('FreeSans', '', $offlinequiz->fontsize);
            }
            $pdf->writeHTMLCell(165, round($offlinequiz->fontsize / 2), $pdf->GetX(), $pdf->GetY() + 0.3, $html);
            $pdf->Ln();
            if ($pdf->is_overflowing()) {
                $pdf->backtrack();
                $pdf->AddPage();
                $pdf->Ln(14);
                // Print the question number and the HTML string again on the new page.
                if ($question->qtype == 'multichoice' || $question->qtype == 'multichoiceset') {
                    $pdf->SetFont('FreeSans', 'B', $offlinequiz->fontsize);
                    $pdf->Cell(4, round($offlinequiz->fontsize / 2), "{$number})  ", 0, 0, 'R');
                    $pdf->SetFont('FreeSans', '', $offlinequiz->fontsize);
                }
                $pdf->writeHTMLCell(165, round($offlinequiz->fontsize / 2), $pdf->GetX(), $pdf->GetY() + 0.3, $html);
                $pdf->Ln();
            }
            $number += $questions[$currentquestionid]->length;
        }
    }
    $fs = get_file_storage();
    $fileprefix = 'form';
    if ($correction) {
        $fileprefix = 'correction';
    }
    // Prepare file record object.
    $timestamp = date('Ymd_His', time());
    $fileinfo = array('contextid' => $context->id, 'component' => 'mod_offlinequiz', 'filearea' => 'pdfs', 'filepath' => '/', 'itemid' => 0, 'filename' => $fileprefix . '-' . strtolower($groupletter) . '_' . $timestamp . '.pdf');
    if ($oldfile = $fs->get_file($fileinfo['contextid'], $fileinfo['component'], $fileinfo['filearea'], $fileinfo['itemid'], $fileinfo['filepath'], $fileinfo['filename'])) {
        $oldfile->delete();
    }
    $pdfstring = $pdf->Output('', 'S');
    $file = $fs->create_file_from_string($fileinfo, $pdfstring);
    $trans->remove_temp_files();
    return $file;
}
Ejemplo n.º 3
0
/**
 * Generates the DOCX question/correction form for an offlinequiz group.
 *
 * @param question_usage_by_activity $templateusage the template question  usage for this offline group
 * @param object $offlinequiz The offlinequiz object
 * @param object $group the offline group object
 * @param int $courseid the ID of the Moodle course
 * @param object $context the context of the offline quiz.
 * @param boolean correction if true the correction form is generated.
 * @return stored_file instance, the generated DOCX file.
 */
function offlinequiz_create_docx_question(question_usage_by_activity $templateusage, $offlinequiz, $group, $courseid, $context, $correction = false)
{
    global $CFG, $DB, $OUTPUT;
    $letterstr = 'abcdefghijklmnopqrstuvwxyz';
    $groupletter = strtoupper($letterstr[$group->number - 1]);
    $coursecontext = context_course::instance($courseid);
    PHPWord_Media::resetMedia();
    $docx = new PHPWord();
    $trans = new offlinequiz_html_translator();
    // Define cell style arrays.
    $cellstyle = array('valign' => 'center');
    // Add text styles.
    // Normal style.
    $docx->addFontStyle('nStyle', array('size' => $offlinequiz->fontsize));
    // Italic style.
    $docx->addFontStyle('iStyle', array('italic' => true, 'size' => $offlinequiz->fontsize));
    // Bold style.
    $docx->addFontStyle('bStyle', array('bold' => true, 'size' => $offlinequiz->fontsize));
    $docx->addFontStyle('brStyle', array('bold' => true, 'align' => 'right', 'size' => $offlinequiz->fontsize));
    // Underline style.
    $docx->addFontStyle('uStyle', array('underline' => PHPWord_Style_Font::UNDERLINE_SINGLE, 'size' => $offlinequiz->fontsize));
    $docx->addFontStyle('ibStyle', array('italic' => true, 'bold' => true, 'size' => $offlinequiz->fontsize));
    $docx->addFontStyle('iuStyle', array('italic' => true, 'underline' => PHPWord_Style_Font::UNDERLINE_SINGLE, 'size' => $offlinequiz->fontsize));
    $docx->addFontStyle('buStyle', array('bold' => true, 'underline' => PHPWord_Style_Font::UNDERLINE_SINGLE, 'size' => $offlinequiz->fontsize));
    $docx->addFontStyle('ibuStyle', array('italic' => true, 'bold' => true, 'underline' => PHPWord_Style_Font::UNDERLINE_SINGLE, 'size' => $offlinequiz->fontsize));
    // Header style.
    $docx->addFontStyle('hStyle', array('bold' => true, 'size' => $offlinequiz->fontsize + 4));
    // Center style.
    $docx->addParagraphStyle('cStyle', array('align' => 'center', 'spaceAfter' => 100));
    $docx->addParagraphStyle('cStyle', array('align' => 'center', 'spaceAfter' => 100));
    $docx->addParagraphStyle('questionTab', array('tabs' => array(new PHPWord_Style_Tab("left", 360))));
    // Define table style arrays.
    $tablestyle = array('borderSize' => 0, 'borderColor' => 'FFFFFF', 'cellMargin' => 20, 'align' => 'center');
    $firstrowstyle = array('borderBottomSize' => 0, 'borderBottomColor' => 'FFFFFF', 'bgColor' => 'FFFFFF');
    $docx->addTableStyle('tableStyle', $tablestyle, $firstrowstyle);
    $boldfont = new PHPWord_Style_Font();
    $boldfont->setBold(true);
    $boldfont->setSize($offlinequiz->fontsize);
    $normalfont = new PHPWord_Style_Font();
    $normalfont->setSize($offlinequiz->fontsize);
    // Define custom list item style for question answers.
    $level1 = new PHPWord_Style_Paragraph();
    $level1->setTabs(new PHPWord_Style_Tabs(array(new PHPWord_Style_Tab('clear', 720), new PHPWord_Style_Tab('num', 360))));
    $level1->setIndentions(new PHPWord_Style_Indentation(array('left' => 360, 'hanging' => 360)));
    $level2 = new PHPWord_Style_Paragraph();
    $level2->setTabs(new PHPWord_Style_Tabs(array(new PHPWord_Style_Tab('left', 720), new PHPWord_Style_Tab('num', 720))));
    $level2->setIndentions(new PHPWord_Style_Indentation(array('left' => 720, 'hanging' => 360)));
    // Create the section that will be used for all outputs.
    $section = $docx->createSection();
    $title = offlinequiz_str_html_docx($offlinequiz->name);
    if (!empty($offlinequiz->time)) {
        if (strlen($title) > 35) {
            $title = substr($title, 0, 33) . ' ...';
        }
        $title .= ": " . offlinequiz_str_html_docx(userdate($offlinequiz->time));
    } else {
        if (strlen($title) > 40) {
            $title = substr($title, 0, 37) . ' ...';
        }
    }
    $title .= ",  " . offlinequiz_str_html_docx(get_string('group') . " {$groupletter}");
    // Add a header.
    $header = $section->createHeader();
    $header->addText($title, array('size' => 10), 'cStyle');
    $header->addImage($CFG->dirroot . '/mod/offlinequiz/pix/line.png', array('width' => 600, 'height' => 5, 'align' => 'center'));
    // Add a footer.
    $footer = $section->createFooter();
    $footer->addImage($CFG->dirroot . '/mod/offlinequiz/pix/line.png', array('width' => 600, 'height' => 5, 'align' => 'center'));
    $footer->addPreserveText($title . '  |  ' . get_string('page') . ' ' . '{PAGE} / {NUMPAGES}', null, array('align' => 'left'));
    // Print title page.
    if (!$correction) {
        $section->addText(offlinequiz_str_html_docx(get_string('questionsheet', 'offlinequiz') . ' - ' . get_string('group') . " {$groupletter}"), 'hStyle', 'cStyle');
        $section->addTextBreak(2);
        $table = $section->addTable('tableStyle');
        $table->addRow();
        $cell = $table->addCell(200, $cellstyle)->addText(offlinequiz_str_html_docx(get_string('name')) . ':  ', 'brStyle');
        $table->addRow();
        $cell = $table->addCell(200, $cellstyle)->addText(offlinequiz_str_html_docx(get_string('idnumber', 'offlinequiz')) . ':  ', 'brStyle');
        $table->addRow();
        $cell = $table->addCell(200, $cellstyle)->addText(offlinequiz_str_html_docx(get_string('studycode', 'offlinequiz')) . ':  ', 'brStyle');
        $table->addRow();
        $cell = $table->addCell(200, $cellstyle)->addText(offlinequiz_str_html_docx(get_string('signature', 'offlinequiz')) . ':  ', 'brStyle');
        $section->addTextBreak(2);
        // The DOCX intro text can be arbitrarily long so we have to catch page overflows.
        if (!empty($offlinequiz->pdfintro)) {
            $blocks = offlinequiz_convert_image_docx($offlinequiz->pdfintro);
            offlinequiz_print_blocks_docx($section, $blocks);
        }
        $section->addPageBreak();
    }
    // Load all the questions needed for this offline quiz group.
    $sql = "SELECT q.*, c.contextid, ogq.page, ogq.slot, ogq.maxmark \n              FROM {offlinequiz_group_questions} ogq,\n                   {question} q,\n                   {question_categories} c\n             WHERE ogq.offlinequizid = :offlinequizid\n               AND ogq.offlinegroupid = :offlinegroupid\n               AND q.id = ogq.questionid\n               AND q.category = c.id\n          ORDER BY ogq.slot ASC ";
    $params = array('offlinequizid' => $offlinequiz->id, 'offlinegroupid' => $group->id);
    // Load the questions.
    $questions = $DB->get_records_sql($sql, $params);
    if (!$questions) {
        echo $OUTPUT->box_start();
        echo $OUTPUT->error_text(get_string('noquestionsfound', 'offlinequiz', $groupletter));
        echo $OUTPUT->box_end();
        return;
    }
    // Load the question type specific information.
    if (!get_question_options($questions)) {
        print_error('Could not load question options');
    }
    // Restore the question sessions to their most recent states.
    // Creating new sessions where required.
    $number = 1;
    // We need a mapping from question IDs to slots, assuming that each question occurs only once.
    $slots = $templateusage->get_slots();
    $texfilter = new filter_tex($context, array());
    // Create the docx question numbering. This is only created once since we number all questions from 1...n.
    $questionnumbering = new PHPWord_Numbering_AbstractNumbering("Question-level", array(new PHPWord_Numbering_Level("1", PHPWord_Numbering_Level::NUMFMT_DECIMAL, "%1)", "left", $level1, $boldfont), new PHPWord_Numbering_Level("1", PHPWord_Numbering_Level::NUMFMT_LOWER_LETTER, "%2)", "left", $level2, $normalfont)));
    $docx->addNumbering($questionnumbering);
    // If shufflequestions has been activated we go through the questions in the order determined by
    // the template question usage.
    if ($offlinequiz->shufflequestions) {
        foreach ($slots as $slot) {
            $slotquestion = $templateusage->get_question($slot);
            $myquestion = $slotquestion->id;
            set_time_limit(120);
            $question = $questions[$myquestion];
            // Either we print the question HTML.
            $questiontext = $question->questiontext;
            // Filter only for tex formulas.
            if (!empty($texfilter)) {
                $questiontext = $texfilter->filter($questiontext);
            }
            // Remove all HTML comments (typically from MS Office).
            $questiontext = preg_replace("/<!--.*?--\\s*>/ms", "", $questiontext);
            // Remove <font> tags.
            $questiontext = preg_replace("/<font[^>]*>[^<]*<\\/font>/ms", "", $questiontext);
            // Remove <script> tags that are created by mathjax preview.
            $questiontext = preg_replace("/<script[^>]*>[^<]*<\\/script>/ms", "", $questiontext);
            // Remove all class info from paragraphs because TCDOCX won't use CSS.
            $questiontext = preg_replace('/<p[^>]+class="[^"]*"[^>]*>/i', "<p>", $questiontext);
            $questiontext = $trans->fix_image_paths($questiontext, $question->contextid, 'questiontext', $question->id, 0.6, 300, 'docx');
            $blocks = offlinequiz_convert_image_docx($questiontext);
            offlinequiz_print_blocks_docx($section, $blocks, $questionnumbering, 0);
            $answernumbering = new PHPWord_Numbering_AbstractNumbering("Adv Multi-level", array(new PHPWord_Numbering_Level("1", PHPWord_Numbering_Level::NUMFMT_DECIMAL, "%1.", "left", $level1, $boldfont), new PHPWord_Numbering_Level("1", PHPWord_Numbering_Level::NUMFMT_LOWER_LETTER, "%2)", "left", $level2, $normalfont)));
            $docx->addNumbering($answernumbering);
            if ($question->qtype == 'multichoice' || $question->qtype == 'multichoiceset') {
                // Save the usage slot in the group questions table.
                //                 $DB->set_field('offlinequiz_group_questions', 'usageslot', $slot,
                //                         array('offlinequizid' => $offlinequiz->id,
                //                                 'offlinegroupid' => $group->id, 'questionid' => $question->id));
                // There is only a slot for multichoice questions.
                $attempt = $templateusage->get_question_attempt($slot);
                $order = $slotquestion->get_order($attempt);
                // Order of the answers.
                foreach ($order as $key => $answer) {
                    $answertext = $question->options->answers[$answer]->answer;
                    // Filter only for tex formulas.
                    if (!empty($texfilter)) {
                        $answertext = $texfilter->filter($answertext);
                    }
                    // Remove all HTML comments (typically from MS Office).
                    $answertext = preg_replace("/<!--.*?--\\s*>/ms", "", $answertext);
                    // Remove all paragraph tags because they mess up the layout.
                    $answertext = preg_replace("/<p[^>]*>/ms", "", $answertext);
                    // Remove <script> tags that are created by mathjax preview.
                    $answertext = preg_replace("/<script[^>]*>[^<]*<\\/script>/ms", "", $answertext);
                    $answertext = preg_replace("/<\\/p[^>]*>/ms", "", $answertext);
                    $answertext = $trans->fix_image_paths($answertext, $question->contextid, 'answer', $answer, 0.6, 200, 'docx');
                    $blocks = offlinequiz_convert_image_docx($answertext);
                    offlinequiz_print_blocks_docx($section, $blocks, $answernumbering, 1);
                }
                if ($offlinequiz->showgrades) {
                    $pointstr = get_string('points', 'grades');
                    if ($question->maxgrade == 1) {
                        $pointstr = get_string('point', 'offlinequiz');
                    }
                    // Indent the question grade like the answers.
                    $textrun = $section->createTextRun($level2);
                    $textrun->addText('(' . ($question->maxgrade + 0) . ' ' . $pointstr . ')', 'bStyle');
                }
            }
            $section->addTextBreak();
            $number++;
        }
    } else {
        // Not shufflequestions.
        // We have to compute the mapping  questionid -> slotnumber.
        $questionslots = array();
        foreach ($slots as $slot) {
            $questionslots[$templateusage->get_question($slot)->id] = $slot;
        }
        // No shufflequestions, so go through the questions as they have been added to the offlinequiz group
        // We also add custom page breaks.
        $currentpage = 1;
        foreach ($questions as $question) {
            // Add page break if set explicitely by teacher.
            if ($question->page > $currentpage) {
                $section->addPageBreak();
                $currentpage++;
            }
            set_time_limit(120);
            // Print the question.
            $questiontext = $question->questiontext;
            // Filter only for tex formulas.
            if (!empty($texfilter)) {
                $questiontext = $texfilter->filter($questiontext);
            }
            // Remove all HTML comments (typically from MS Office).
            $questiontext = preg_replace("/<!--.*?--\\s*>/ms", "", $questiontext);
            // Remove <font> tags.
            $questiontext = preg_replace("/<font[^>]*>[^<]*<\\/font>/ms", "", $questiontext);
            // Remove <script> tags that are created by mathjax preview.
            $questiontext = preg_replace("/<script[^>]*>[^<]*<\\/script>/ms", "", $questiontext);
            // Remove all class info from paragraphs because TCDOCX won't use CSS.
            $questiontext = preg_replace('/<p[^>]+class="[^"]*"[^>]*>/i', "<p>", $questiontext);
            $questiontext = $trans->fix_image_paths($questiontext, $question->contextid, 'questiontext', $question->id, 0.6, 300, 'docx');
            $blocks = offlinequiz_convert_image_docx($questiontext);
            // Description questions are printed without a number because they are not on the answer form.
            if ($question->qtype == 'description') {
                offlinequiz_print_blocks_docx($section, $blocks);
            } else {
                offlinequiz_print_blocks_docx($section, $blocks, $questionnumbering, 0);
            }
            $answernumbering = new PHPWord_Numbering_AbstractNumbering("Adv Multi-level", array(new PHPWord_Numbering_Level("1", PHPWord_Numbering_Level::NUMFMT_DECIMAL, "%1.", "left", $level1), new PHPWord_Numbering_Level("1", PHPWord_Numbering_Level::NUMFMT_LOWER_LETTER, "%2)", "left", $level2)));
            $docx->addNumbering($answernumbering);
            if ($question->qtype == 'multichoice' || $question->qtype == 'multichoiceset') {
                $slot = $questionslots[$question->id];
                // Save the usage slot in the group questions table.
                //                 $DB->set_field('offlinequiz_group_questions', 'usageslot', $slot,
                //                         array('offlinequizid' => $offlinequiz->id,
                //                                 'offlinegroupid' => $group->id, 'questionid' => $question->id));
                // Now retrieve the order of the answers.
                $slotquestion = $templateusage->get_question($slot);
                $attempt = $templateusage->get_question_attempt($slot);
                $order = $slotquestion->get_order($attempt);
                // Order of the answers.
                foreach ($order as $key => $answer) {
                    $answertext = $question->options->answers[$answer]->answer;
                    // Filter only for tex formulas.
                    if (!empty($texfilter)) {
                        $answertext = $texfilter->filter($answertext);
                    }
                    // Remove all HTML comments (typically from MS Office).
                    $answertext = preg_replace("/<!--.*?--\\s*>/ms", "", $answertext);
                    // Remove all paragraph tags because they mess up the layout.
                    $answertext = preg_replace("/<p[^>]*>/ms", "", $answertext);
                    // Remove <script> tags that are created by mathjax preview.
                    $answertext = preg_replace("/<script[^>]*>[^<]*<\\/script>/ms", "", $answertext);
                    $answertext = preg_replace("/<\\/p[^>]*>/ms", "", $answertext);
                    $answertext = $trans->fix_image_paths($answertext, $question->contextid, 'answer', $answer, 0.6, 200, 'docx');
                    $blocks = offlinequiz_convert_image_docx($answertext);
                    offlinequiz_print_blocks_docx($section, $blocks, $answernumbering, 1);
                }
                if ($offlinequiz->showgrades) {
                    $pointstr = get_string('points', 'grades');
                    if ($question->maxgrade == 1) {
                        $pointstr = get_string('point', 'offlinequiz');
                    }
                    // Indent the question grade like the answers.
                    $textrun = $section->createTextRun($level2);
                    $textrun->addText('(' . ($question->maxgrade + 0) . ' ' . $pointstr . ')', 'bStyle');
                }
                $section->addTextBreak();
                $number++;
                // End if multichoice.
            }
        }
        // End forall questions.
    }
    // End else no shufflequestions.
    $fs = get_file_storage();
    $fileprefix = 'form';
    if ($correction) {
        $fileprefix = 'correction';
    }
    srand(microtime() * 1000000);
    $unique = str_replace('.', '', microtime(true) . rand(0, 100000));
    $tempfilename = $CFG->dataroot . '/temp/offlinequiz/' . $unique . '.docx';
    check_dir_exists($CFG->dataroot . '/temp/offlinequiz', true, true);
    if (file_exists($tempfilename)) {
        unlink($tempfilename);
    }
    // Save file.
    $objwriter = PHPWord_IOFactory::createWriter($docx, 'Word2007');
    $objwriter->save($tempfilename);
    // Prepare file record object.
    $timestamp = date('Ymd_His', time());
    $fileinfo = array('contextid' => $context->id, 'component' => 'mod_offlinequiz', 'filearea' => 'pdfs', 'filepath' => '/', 'itemid' => 0, 'filename' => $fileprefix . '-' . strtolower($groupletter) . '_' . $timestamp . '.docx');
    // Delete existing old files, should actually not happen.
    if ($oldfile = $fs->get_file($fileinfo['contextid'], $fileinfo['component'], $fileinfo['filearea'], $fileinfo['itemid'], $fileinfo['filepath'], $fileinfo['filename'])) {
        $oldfile->delete();
    }
    // Create a Moodle file from the temporary file.
    $file = $fs->create_file_from_pathname($fileinfo, $tempfilename);
    // Remove all temporary files.
    unlink($tempfilename);
    $trans->remove_temp_files();
    return $file;
}