/** * (non-PHPdoc) * * @see offlinequiz_default_report::display() */ public function display($offlinequiz, $cm, $course) { global $CFG, $OUTPUT, $SESSION, $DB; // Define some strings. $strtimeformat = get_string('strftimedatetime'); $letterstr = ' ABCDEFGHIJKLMNOPQRSTUVWXYZ'; offlinequiz_load_useridentification(); $offlinequizconfig = get_config('offlinequiz'); // Deal with actions. $action = optional_param('action', '', PARAM_ACTION); // Only print headers if not asked to download data or delete data. if (!($download = optional_param('download', null, PARAM_TEXT)) && !$action == 'delete') { $this->print_header_and_tabs($cm, $course, $offlinequiz, 'overview'); echo $OUTPUT->box_start('linkbox'); echo $OUTPUT->heading(format_string($offlinequiz->name)); echo $OUTPUT->heading(get_string('results', 'offlinequiz')); require_once $CFG->libdir . '/grouplib.php'; echo groups_print_activity_menu($cm, $CFG->wwwroot . '/mod/offlinequiz/report.php?id=' . $cm->id . '&mode=overview', true); echo $OUTPUT->box_end(); echo '<br/>'; } $context = context_module::instance($cm->id); $systemcontext = context_system::instance(); // Set table options. $noresults = optional_param('noresults', 0, PARAM_INT); $pagesize = optional_param('pagesize', 10, PARAM_INT); $groupid = optional_param('group', 0, PARAM_INT); if ($pagesize < 1) { $pagesize = 10; } $answerletters = 'abcdefghijklmnopqrstuvwxyz'; if ($action == 'delete' && confirm_sesskey()) { $selectedresultids = array(); $params = (array) data_submitted(); foreach ($params as $key => $value) { if (preg_match('!^s([0-9]+)$!', $key, $matches)) { $selectedresultids[] = $matches[1]; } } if ($selectedresultids) { foreach ($selectedresultids as $resultid) { if ($resultid && ($todelete = $DB->get_record('offlinequiz_results', array('id' => $resultid)))) { offlinequiz_delete_result($resultid, $context); // Log this event. $params = array('objectid' => $resultid, 'relateduserid' => $todelete->userid, 'context' => context_module::instance($cm->id), 'other' => array('mode' => 'overview')); $event = \mod_offlinequiz\event\attempt_deleted::create($params); $event->trigger(); // Change the status of all related pages with error 'resultexists' to 'suspended'. $user = $DB->get_record('user', array('id' => $todelete->userid)); $group = $DB->get_record('offlinequiz_groups', array('id' => $todelete->offlinegroupid)); $sql = "SELECT id\n FROM {offlinequiz_scanned_pages}\n WHERE offlinequizid = :offlinequizid\n AND userkey = :userkey\n AND groupnumber = :groupnumber\n AND status = 'error'\n AND (error = 'resultexists' OR error = 'differentresultexists')"; $params = array('offlinequizid' => $offlinequiz->id, 'userkey' => $user->{$offlinequizconfig->ID_field}, 'groupnumber' => $group->number); $otherpages = $DB->get_records_sql($sql, $params); foreach ($otherpages as $page) { $DB->set_field('offlinequiz_scanned_pages', 'status', 'suspended', array('id' => $page->id)); $DB->set_field('offlinequiz_scanned_pages', 'error', '', array('id' => $page->id)); } } } offlinequiz_grade_item_update($offlinequiz, 'reset'); offlinequiz_update_grades($offlinequiz); redirect(new moodle_url('/mod/offlinequiz/report.php', array('mode' => 'overview', 'id' => $cm->id, 'noresults' => $noresults, 'pagesize' => $pagesize))); } } // Now check if asked download of data. if ($download) { $filename = clean_filename("{$course->shortname} " . format_string($offlinequiz->name, true)); $sort = ''; } // Fetch the group data. $groups = $DB->get_records('offlinequiz_groups', array('offlinequizid' => $offlinequiz->id), 'number', '*', 0, $offlinequiz->numgroups); // Define table columns. $tablecolumns = array('checkbox', 'picture', 'fullname', $offlinequizconfig->ID_field, 'timestart', 'offlinegroupid', 'sumgrades'); $tableheaders = array('<input type="checkbox" name="toggle" onClick="if (this.checked) {select_all_in(\'DIV\',null,\'tablecontainer\');} else {deselect_all_in(\'DIV\',null,\'tablecontainer\');}"/>', '', get_string('fullname'), get_string($offlinequizconfig->ID_field), get_string('importedon', 'offlinequiz'), get_string('group'), get_string('grade', 'offlinequiz')); $checked = array(); // Get participants list. $withparticipants = false; if ($lists = $DB->get_records('offlinequiz_p_lists', array('offlinequizid' => $offlinequiz->id))) { $withparticipants = true; $tablecolumns[] = 'checked'; $tableheaders[] = get_string('present', 'offlinequiz'); foreach ($lists as $list) { $participants = $DB->get_records('offlinequiz_participants', array('listid' => $list->id)); foreach ($participants as $participant) { $checked[$participant->userid] = $participant->checked; } } } if (!$download) { // Set up the table. $params = array('offlinequiz' => $offlinequiz, 'noresults' => $noresults, 'pagesize' => $pagesize); $table = new offlinequiz_results_table('mod-offlinequiz-report-overview-report', $params); $table->define_columns($tablecolumns); $table->define_headers($tableheaders); $table->define_baseurl($CFG->wwwroot . '/mod/offlinequiz/report.php?mode=overview&id=' . $cm->id . '&noresults=' . $noresults . '&pagesize=' . $pagesize); $table->sortable(true); $table->no_sorting('checkbox'); if ($withparticipants) { $table->no_sorting('checked'); } $table->column_suppress('picture'); $table->column_suppress('fullname'); $table->column_class('picture', 'picture'); $table->column_class($offlinequizconfig->ID_field, 'userkey'); $table->column_class('timestart', 'timestart'); $table->column_class('offlinegroupid', 'offlinegroupid'); $table->column_class('sumgrades', 'sumgrades'); $table->set_attribute('cellpadding', '2'); $table->set_attribute('id', 'attempts'); $table->set_attribute('class', 'generaltable generalbox'); // Start working -- this is necessary as soon as the niceties are over. $table->setup(); } else { if ($download == 'ODS') { require_once "{$CFG->libdir}/odslib.class.php"; $filename .= ".ods"; // Creating a workbook. $workbook = new MoodleODSWorkbook("-"); // Sending HTTP headers. $workbook->send($filename); // Creating the first worksheet. $sheettitle = get_string('reportoverview', 'offlinequiz'); $myxls = $workbook->add_worksheet($sheettitle); // Format types. $format = $workbook->add_format(); $format->set_bold(0); $formatbc = $workbook->add_format(); $formatbc->set_bold(1); $formatbc->set_align('center'); $formatb = $workbook->add_format(); $formatb->set_bold(1); $formaty = $workbook->add_format(); $formaty->set_bg_color('yellow'); $formatc = $workbook->add_format(); $formatc->set_align('center'); $formatr = $workbook->add_format(); $formatr->set_bold(1); $formatr->set_color('red'); $formatr->set_align('center'); $formatg = $workbook->add_format(); $formatg->set_bold(1); $formatg->set_color('green'); $formatg->set_align('center'); // Here starts workshhet headers. $headers = array(get_string($offlinequizconfig->ID_field), get_string('firstname'), get_string('lastname'), get_string('importedon', 'offlinequiz'), get_string('group'), get_string('grade', 'offlinequiz')); if (!empty($withparticipants)) { $headers[] = get_string('present', 'offlinequiz'); } $colnum = 0; foreach ($headers as $item) { $myxls->write(0, $colnum, $item, $formatbc); $colnum++; } $rownum = 1; } else { if ($download == 'Excel') { require_once "{$CFG->libdir}/excellib.class.php"; $filename .= ".xls"; // Creating a workbook. $workbook = new MoodleExcelWorkbook("-"); // Sending HTTP headers. $workbook->send($filename); // Creating the first worksheet. $sheettitle = get_string('results', 'offlinequiz'); $myxls = $workbook->add_worksheet($sheettitle); // Format types. $format = $workbook->add_format(); $format->set_bold(0); $formatbc = $workbook->add_format(); $formatbc->set_bold(1); $formatbc->set_align('center'); $formatb = $workbook->add_format(); $formatb->set_bold(1); $formaty = $workbook->add_format(); $formaty->set_bg_color('yellow'); $formatc = $workbook->add_format(); $formatc->set_align('center'); $formatr = $workbook->add_format(); $formatr->set_bold(1); $formatr->set_color('red'); $formatr->set_align('center'); $formatg = $workbook->add_format(); $formatg->set_bold(1); $formatg->set_color('green'); $formatg->set_align('center'); // Here starts worksheet headers. $headers = array(get_string($offlinequizconfig->ID_field), get_string('firstname'), get_string('lastname'), get_string('importedon', 'offlinequiz'), get_string('group'), get_string('grade', 'offlinequiz')); if (!empty($withparticipants)) { $headers[] = get_string('present', 'offlinequiz'); } $colnum = 0; foreach ($headers as $item) { $myxls->write(0, $colnum, $item, $formatbc); $colnum++; } $rownum = 1; } else { if ($download == 'CSV') { $filename .= ".csv"; header("Content-Encoding: UTF-8"); header("Content-Type: text/csv; charset=utf-8"); header("Content-Disposition: attachment; filename=\"{$filename}\""); header("Expires: 0"); header("Cache-Control: must-revalidate,post-check=0,pre-check=0"); header("Pragma: public"); echo ""; // UTF-8 BOM. $headers = get_string($offlinequizconfig->ID_field) . ", " . get_string('fullname') . ", " . get_string('importedon', 'offlinequiz') . ", " . get_string('group') . ", " . get_string('grade', 'offlinequiz'); if (!empty($withparticipants)) { $headers .= ", " . get_string('present', 'offlinequiz'); } echo $headers . " \n"; } else { if ($download == 'CSVplus1' || $download == 'CSVpluspoints') { $filename .= ".csv"; header("Content-Encoding: UTF-8"); header("Content-Type: text/csv; charset=utf-8"); header("Content-Disposition: attachment; filename=\"{$filename}\""); header("Expires: 0"); header("Cache-Control: must-revalidate,post-check=0,pre-check=0"); header("Pragma: public"); echo ""; // UTF-8 BOM. // Print the table headers. echo get_string('firstname') . ',' . get_string('lastname') . ',' . get_string($offlinequizconfig->ID_field) . ',' . get_string('group'); $maxquestions = offlinequiz_get_maxquestions($offlinequiz, $groups); for ($i = 0; $i < $maxquestions; $i++) { echo ', ' . get_string('question') . ' ' . ($i + 1); } echo "\n"; // Print the correct answer bit-strings. foreach ($groups as $group) { if ($group->templateusageid) { $quba = question_engine::load_questions_usage_by_activity($group->templateusageid); $slots = $quba->get_slots(); echo ', ,' . get_string('correct', 'offlinequiz'); echo ',' . $group->number; foreach ($slots as $slot) { $slotquestion = $quba->get_question($slot); $qtype = $slotquestion->get_type_name(); if ($qtype == 'multichoice' || $qtype == 'multichoiceset') { $attempt = $quba->get_question_attempt($slot); $order = $slotquestion->get_order($attempt); // Order of the answers. $tempstr = ","; $letters = array(); $counter = 0; foreach ($order as $key => $answerid) { $fraction = $DB->get_field('question_answers', 'fraction', array('id' => $answerid)); if ($fraction > 0) { $letters[] = $answerletters[$counter]; } $counter++; } if (empty($letters)) { $tempstr .= '99'; } else { $tempstr .= implode('/', $letters); } echo $tempstr; } } echo "\n"; } } } } } } } $coursecontext = context_course::instance($course->id); $contextids = $coursecontext->get_parent_context_ids(true); // Construct the SQL // First get roleids for students from leagcy. if (!($roles = get_roles_with_capability('mod/offlinequiz:attempt', CAP_ALLOW, $systemcontext))) { error("No roles with capability 'moodle/offlinequiz:attempt' defined in system context"); } $roleids = array(); foreach ($roles as $role) { $roleids[] = $role->id; } $rolelist = implode(',', $roleids); $select = "SELECT " . $DB->sql_concat('u.id', "'#'", "COALESCE(qa.usageid, 0)") . " AS uniqueid,\n qa.id AS resultid, u.id, qa.usageid, qa.offlinegroupid, qa.status,\n u.id AS userid, u.firstname, u.lastname,\n u.alternatename, u.middlename, u.firstnamephonetic, u.lastnamephonetic,\n u.picture, u." . $offlinequizconfig->ID_field . ",\n qa.sumgrades, qa.timefinish, qa.timestart, qa.timefinish - qa.timestart AS duration "; $result = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'ctx'); list($contexttest, $cparams) = $result; list($roletest, $rparams) = $DB->get_in_or_equal($roleids, SQL_PARAMS_NAMED, 'role'); $from = "FROM {user} u\n JOIN {role_assignments} ra ON ra.userid = u.id\n LEFT JOIN {offlinequiz_results} qa ON u.id = qa.userid AND qa.offlinequizid = :offlinequizid\n "; $where = " WHERE ra.contextid {$contexttest} AND ra.roleid {$roletest} "; $params = array('offlinequizid' => $offlinequiz->id); $params = array_merge($params, $cparams, $rparams); if ($groupid) { $from .= " JOIN {groups_members} gm ON gm.userid = u.id "; $where .= " AND gm.groupid = :groupid "; $params['groupid'] = $groupid; } if (empty($noresults)) { $where = $where . " AND qa.userid IS NOT NULL\n AND qa.status = 'complete'"; // Show ONLY students with results. } else { if ($noresults == 1) { // The value noresults = 1 means only no results, so make the left join ask for only records // where the right is null (no results). $where .= ' AND qa.userid IS NULL'; // Show ONLY students without results. } else { if ($noresults == 3) { // We want all results, also the partial ones. $from = "FROM {user} u\n JOIN {offlinequiz_results} qa ON u.id = qa.userid "; $where = " WHERE qa.offlinequizid = :offlinequizid"; } } } // The value noresults = 2 means we want all students, with or without results. $countsql = 'SELECT COUNT(DISTINCT(u.id)) ' . $from . $where; if (!$download) { // Count the records NOW, before funky question grade sorting messes up $from. $totalinitials = $DB->count_records_sql($countsql, $params); // Add extra limits due to initials bar. list($ttest, $tparams) = $table->get_sql_where(); if (!empty($ttest)) { $where .= ' AND ' . $ttest; $countsql .= ' AND ' . $ttest; $params = array_merge($params, $tparams); } $total = $DB->count_records_sql($countsql, $params); // Add extra limits due to sorting by question grade. $sort = $table->get_sql_sort(); // Fix some wired sorting. if (empty($sort)) { $sort = ' ORDER BY u.lastname'; } else { $sort = ' ORDER BY ' . $sort; } $table->pagesize($pagesize, $total); } // Fetch the results. if (!$download) { $results = $DB->get_records_sql($select . $from . $where . $sort, $params, $table->get_page_start(), $table->get_page_size()); } else { $results = $DB->get_records_sql($select . $from . $where . $sort, $params); } // Build table rows. if (!$download) { $table->initialbars(true); } if (!empty($results) || !empty($noresults)) { foreach ($results as $result) { $user = $DB->get_record('user', array('id' => $result->userid)); $picture = $OUTPUT->user_picture($user, array('courseid' => $course->id)); if (!empty($result->resultid)) { $checkbox = '<input type="checkbox" name="s' . $result->resultid . '" value="' . $result->resultid . '" />'; } else { $checkbox = ''; } if (!empty($result) && (empty($result->resultid) || $result->timefinish == 0)) { $resultdate = '-'; } else { $resultdate = userdate($result->timefinish, $strtimeformat); } if (!empty($result) && $result->offlinegroupid) { $groupletter = $letterstr[$groups[$result->offlinegroupid]->number]; } else { $groupletter = '-'; } $userlink = '<a href="' . $CFG->wwwroot . '/user/view.php?id=' . $result->userid . '&course=' . $course->id . '">' . fullname($result) . '</a>'; if (!$download) { $row = array($checkbox, $picture, $userlink, $result->{$offlinequizconfig->ID_field}, $resultdate, $groupletter); } else { $row = array($result->{$offlinequizconfig->ID_field}, $result->firstname, $result->lastname, $resultdate, $groupletter); } if (!empty($result) && $result->offlinegroupid) { $outputgrade = format_float($result->sumgrades / $groups[$result->offlinegroupid]->sumgrades * $offlinequiz->grade, $offlinequiz->decimalpoints); } else { $outputgrade = '-'; } if (!$download) { if ($result->status == 'partial') { $row[] = get_string('partial', 'offlinequiz'); } else { if ($result->sumgrades === null) { $row[] = '-'; } else { $row[] = '<a href="review.php?q=' . $offlinequiz->id . '&resultid=' . $result->resultid . '">' . $outputgrade . '</a>'; } } if ($withparticipants) { $row[] = !empty($checked[$result->userid]) ? "<img src=\"{$CFG->wwwroot}/mod/offlinequiz/pix/tick.gif\" alt=\"" . get_string('ischecked', 'offlinequiz') . "\">" : "<img src=\"{$CFG->wwwroot}/mod/offlinequiz/pix/cross.gif\" alt=\"" . get_string('isnotchecked', 'offlinequiz') . "\">"; } } else { if ($download != 'CSVplus1' || $download == 'CSVpluspoints') { $row[] = $result->sumgrades === null ? '-' : $outputgrade; if ($withparticipants) { if (array_key_exists($result->userid, $checked)) { $row[] = $checked[$result->userid] ? get_string('ok') : '-'; } else { $row[] = '-'; } } } } if (!$download) { $table->add_data($row); } else { if ($download == 'Excel' or $download == 'ODS') { $colnum = 0; foreach ($row as $item) { $myxls->write($rownum, $colnum, $item, $format); $colnum++; } $rownum++; } else { if ($download == 'CSV') { $text = implode(',', $row); echo $text . "\n"; } else { if ($download == 'CSVplus1' || $download == 'CSVpluspoints') { $text = $row[1] . ',' . $row[2] . ',' . $row[0] . ',' . $groups[$result->offlinegroupid]->number; if ($pages = $DB->get_records('offlinequiz_scanned_pages', array('resultid' => $result->resultid), 'pagenumber ASC')) { foreach ($pages as $page) { if ($page->status == 'ok' || $page->status == 'submitted') { $choices = $DB->get_records('offlinequiz_choices', array('scannedpageid' => $page->id), 'slotnumber, choicenumber'); $counter = 0; $oldslot = -1; $letters = array(); foreach ($choices as $choice) { if ($oldslot == -1) { $oldslot = $choice->slotnumber; } else { if ($oldslot != $choice->slotnumber) { if (empty($letters)) { $text .= ',99'; } else { $text .= ',' . implode('/', $letters); } $counter = 0; $oldslot = $choice->slotnumber; $letters = array(); } } if ($choice->value == 1) { $letters[] = $answerletters[$counter]; } $counter++; } if (empty($letters)) { $text .= ',99'; } else { $text .= ',' . implode('/', $letters); } } } } echo $text . "\n"; if ($download == 'CSVpluspoints') { $text = $row[1] . ',' . $row[2] . ',' . $row[0] . ',' . $groups[$result->offlinegroupid]->number; $quba = question_engine::load_questions_usage_by_activity($result->usageid); $slots = $quba->get_slots(); foreach ($slots as $slot) { $slotquestion = $quba->get_question($slot); $attempt = $quba->get_question_attempt($slot); $text .= ',' . format_float($attempt->get_mark(), $offlinequiz->decimalpoints, false); } echo $text . "\n"; } } } } } } // End foreach ($results... } else { if (!$download) { $table->print_initials_bar(); } } if (!$download) { // Print table. $table->finish_html(); if (!empty($results)) { echo '<form id="downloadoptions" action="report.php" method="get">'; echo ' <input type="hidden" name="id" value="' . $cm->id . '" />'; echo ' <input type="hidden" name="q" value="' . $offlinequiz->id . '" />'; echo ' <input type="hidden" name="mode" value="overview" />'; echo ' <input type="hidden" name="noheader" value="yes" />'; echo ' <table class="boxaligncenter"><tr><td>'; $options = array('Excel' => get_string('excelformat', 'offlinequiz'), 'ODS' => get_string('odsformat', 'offlinequiz'), 'CSV' => get_string('csvformat', 'offlinequiz'), 'CSVplus1' => get_string('csvplus1format', 'offlinequiz'), 'CSVpluspoints' => get_string('csvpluspointsformat', 'offlinequiz')); print_string('downloadresultsas', 'offlinequiz'); echo "</td><td>"; echo html_writer::select($options, 'download', '', false); echo ' <input type="submit" value="' . get_string('download') . '" />'; echo ' <script type="text/javascript">' . "\n<!--\n" . 'document.getElementById("noscriptmenuaction").style.display = "none";' . "\n-->\n" . '</script>'; echo " </td>\n"; echo "<td>"; echo "</td>\n"; echo '</tr></table></form>'; } } else { if ($download == 'Excel' || $download == 'ODS') { $workbook->close(); exit; } else { if ($download == 'CSV' || $download == 'CSVplus1' || $download == 'CSVpluspoints') { exit; } } } // Print display options. echo '<div class="controls">'; echo '<form id="options" action="report.php" method="get">'; echo '<div class=centerbox>'; echo '<p>' . get_string('displayoptions', 'offlinequiz') . ': </p>'; echo '<input type="hidden" name="id" value="' . $cm->id . '" />'; echo '<input type="hidden" name="q" value="' . $offlinequiz->id . '" />'; echo '<input type="hidden" name="mode" value="overview" />'; echo '<input type="hidden" name="detailedmarks" value="0" />'; echo '<table id="overview-options" class="boxaligncenter">'; echo '<tr align="left">'; echo '<td><label for="pagesize">' . get_string('pagesizeparts', 'offlinequiz') . '</label></td>'; echo '<td><input type="text" id="pagesize" name="pagesize" size="3" value="' . $pagesize . '" /></td>'; echo '</tr>'; echo '<tr align="left">'; echo '<td colspan="2">'; $options = array(); $options[] = get_string('attemptsonly', 'offlinequiz'); $options[] = get_string('noattemptsonly', 'offlinequiz'); $options[] = get_string('allstudents', 'offlinequiz'); $options[] = get_string('allresults', 'offlinequiz'); echo html_writer::select($options, 'noresults', $noresults, ''); echo '</td></tr>'; echo '<tr><td colspan="2" align="center">'; echo '<input type="submit" value="' . get_string('go') . '" />'; echo '</td></tr></table>'; echo '</div>'; echo '</form>'; echo '</div>'; echo "\n"; return true; }
print_error("The course module for the offlinequiz with id {$q} is missing"); } } $offlinequiz->optionflags = 0; require_login($course->id, false, $cm); if (!($context = context_module::instance($cm->id))) { print_error("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. offlinequiz_load_useridentification(); $strpreview = get_string('createquiz', 'offlinequiz'); $strofflinequizzes = get_string("modulenameplural", "offlinequiz"); $PAGE->set_url('/mod/offlinequiz/createquiz.php?id=' . $cm->id); $PAGE->set_title($strpreview); $PAGE->set_heading($course->fullname); $PAGE->set_pagelayout('report'); // Or 'admin'. $PAGE->set_cacheable(true); if ($node = $PAGE->settingsnav->find('mod_offlinequiz_createquiz', navigation_node::TYPE_SETTING)) { $node->make_active(); } if (!($groups = $DB->get_records('offlinequiz_groups', array('offlinequizid' => $offlinequiz->id), 'number', '*', 0, $offlinequiz->numgroups))) { print_error('There are no offlinequiz groups', "edit.php?q={$offlinequiz->id}{$amp};sesskey=" . sesskey()); } // Redmine 2131: Handle download all before any HTML output is produced.
public function display($offlinequiz, $cm, $course) { global $CFG, $OUTPUT, $DB; $confirm = optional_param('confirm', 0, PARAM_INT); raise_memory_limit(MEMORY_EXTRA); // Print header. $this->print_header_and_tabs($cm, $course, $offlinequiz, 'regrade'); offlinequiz_load_useridentification(); $offlinequizconfig = get_config('offlinequiz'); $letterstr = 'ABCDEFGHIJKL'; // Print heading. echo $OUTPUT->box_start('linkbox'); echo $OUTPUT->heading(format_string($offlinequiz->name)); echo $OUTPUT->heading(get_string('regradingquiz', 'offlinequiz')); echo $OUTPUT->box_end('linkbox'); // Fetch all results. $ressql = "SELECT res.*, u.{$offlinequizconfig->ID_field}\n FROM {offlinequiz_results} res\n JOIN {user} u on u.id = res.userid\n WHERE res.offlinequizid = :offlinequizid\n AND res.status = 'complete'"; $resparams = array('offlinequizid' => $offlinequiz->id); if (!($results = $DB->get_records_sql($ressql, $resparams))) { $url = new moodle_url('/mod/offlinequiz/report.php', array('id' => $cm->id)); $url->param('mode', 'overview'); echo $OUTPUT->heading(get_string('noresults', 'offlinequiz')); echo $OUTPUT->box_start('linkbox'); echo $OUTPUT->continue_button($url); echo $OUTPUT->box_end('linkbox'); return true; } // If we don't have a confirmation we only display the confirm and cancel buttons. if (!$confirm) { echo $OUTPUT->box_start('linkbox'); echo $OUTPUT->notification(get_string('regradedisplayexplanation', 'offlinequiz'), 'notifyproblem'); echo '<br/>'; $url = new moodle_url('/mod/offlinequiz/report.php', array('id' => $cm->id)); $url->param('mode', 'regrade'); $url->param('confirm', 1); echo $OUTPUT->single_button($url, get_string('reallyregrade', 'offlinequiz_regrade')); echo '<br/>'; $url = new moodle_url('/mod/offlinequiz/report.php', array('id' => $cm->id)); $url->param('mode', 'overview'); echo $OUTPUT->single_button($url, get_string('cancel')); echo $OUTPUT->box_end('linkbox'); return true; } // Fetch all groups. if ($groups = $DB->get_records('offlinequiz_groups', array('offlinequizid' => $offlinequiz->id), 'number', '*', 0, $offlinequiz->numgroups)) { foreach ($groups as $group) { $sumgrade = offlinequiz_update_sumgrades($offlinequiz, $group->id); $groupletter = $letterstr[$group->number - 1]; $a = new StdClass(); $a->letter = $groupletter; $a->grade = round($sumgrade, $offlinequiz->decimalpoints); echo $OUTPUT->notification(get_string('updatedsumgrades', 'offlinequiz', $a), 'notifysuccess'); } } // Options for the popup_action. $options = array(); $options['height'] = 1024; // Optional. $options['width'] = 860; // Optional. $options['resizable'] = false; $saveresult = false; // Loop through all results and regrade while printing progress info. foreach ($results as $result) { set_time_limit(120); $sql = "SELECT ogq.questionid, ogq.maxmark \n FROM {offlinequiz_group_questions} ogq\n WHERE ogq.offlinequizid = :offlinequizid\n AND ogq.offlinegroupid = :offlinegroupid"; $params = array('offlinequizid' => $offlinequiz->id, 'offlinegroupid' => $result->offlinegroupid); if (!($questions = $DB->get_records_sql($sql, $params))) { print_error("Failed to get questions for regrading!"); } $user = $DB->get_record('user', array('id' => $result->userid)); echo '<strong>' . get_string('regradingresult', 'offlinequiz', $user->{$offlinequizconfig->ID_field}) . '</strong> '; $changed = $this->regrade_result($result, $questions); if ($changed) { $quba = question_engine::load_questions_usage_by_activity($result->usageid); $DB->set_field('offlinequiz_results', 'sumgrades', $quba->get_total_mark(), array('id' => $result->id)); $url = new moodle_url($CFG->wwwroot . '/mod/offlinequiz/review.php', array('resultid' => $result->id)); $title = get_string('changed', 'offlinequiz'); echo $OUTPUT->action_link($url, $title, new popup_action('click', $url, 'review' . $result->id, $options)); } else { echo get_string('done', 'offlinequiz'); } echo '<br />'; // The following makes sure that the output is sent immediately. @flush(); @ob_flush(); } // Log this action. $params = array('objectid' => $cm->id, 'context' => context_module::instance($cm->id), 'other' => array('numberofresults' => count($results), 'offlinequizid' => $offlinequiz->id)); $event = \mod_offlinequiz\event\results_regraded::create($params); $event->trigger(); $url = new moodle_url($CFG->wwwroot . '/mod/offlinequiz/report.php', array('q' => $offlinequiz->id, 'mode' => 'overview')); offlinequiz_update_grades($offlinequiz); echo $OUTPUT->box_start('linkbox'); echo $OUTPUT->single_button($url, get_string('continue'), 'get'); echo $OUTPUT->box_end('linkbox'); return true; }
/** * Serves a list of participants as a file. * * @param unknown_type $offlinequiz * @param unknown_type $fileformat * @param unknown_type $coursecontext * @param unknown_type $systemcontext */ function offlinequiz_download_partlist($offlinequiz, $fileformat, &$coursecontext, &$systemcontext) { global $CFG, $DB, $COURSE; offlinequiz_load_useridentification(); $offlinequizconfig = get_config('offlinequiz'); $filename = clean_filename(get_string('participants', 'offlinequiz') . $offlinequiz->id); // First get roleids for students from leagcy. if (!($roles = get_roles_with_capability('mod/offlinequiz:attempt', CAP_ALLOW, $systemcontext))) { print_error("No roles with capability 'mod/offlinequiz:attempt' defined in system context"); } $roleids = array(); foreach ($roles as $role) { $roleids[] = $role->id; } list($csql, $cparams) = $DB->get_in_or_equal($coursecontext->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'ctx'); list($rsql, $rparams) = $DB->get_in_or_equal($roleids, SQL_PARAMS_NAMED, 'role'); $params = array_merge($cparams, $rparams); $sql = "SELECT p.id, p.userid, p.listid, u." . $offlinequizconfig->ID_field . ", u.firstname, u.lastname,\n u.alternatename, u.middlename, u.firstnamephonetic, u.lastnamephonetic,\n u.picture, p.checked\n FROM {offlinequiz_participants} p,\n {offlinequiz_p_lists} pl,\n {user} u,\n {role_assignments} ra\n WHERE p.listid = pl.id\n AND p.userid = u.id\n AND ra.userid=u.id\n AND pl.offlinequizid = :offlinequizid\n AND ra.contextid {$csql}\n AND ra.roleid {$rsql}"; $params['offlinequizid'] = $offlinequiz->id; // Define table headers. $tableheaders = array(get_string('fullname'), get_string($offlinequizconfig->ID_field), get_string('participantslist', 'offlinequiz'), get_string('attemptexists', 'offlinequiz'), get_string('present', 'offlinequiz')); if ($fileformat == 'ODS') { require_once "{$CFG->libdir}/odslib.class.php"; $filename .= ".ods"; // Creating a workbook. $workbook = new MoodleODSWorkbook("-"); // Sending HTTP headers. $workbook->send($filename); // Creating the first worksheet. $sheettitle = get_string('participants', 'offlinequiz'); $myxls = $workbook->add_worksheet($sheettitle); // Format types. $format = $workbook->add_format(); $format->set_bold(0); $formatbc = $workbook->add_format(); $formatbc->set_bold(1); $formatbc->set_align('center'); $formatb = $workbook->add_format(); $formatb->set_bold(1); $formaty = $workbook->add_format(); $formaty->set_bg_color('yellow'); $formatc = $workbook->add_format(); $formatc->set_align('center'); $formatr = $workbook->add_format(); $formatr->set_bold(1); $formatr->set_color('red'); $formatr->set_align('center'); $formatg = $workbook->add_format(); $formatg->set_bold(1); $formatg->set_color('green'); $formatg->set_align('center'); // Print worksheet headers. $colnum = 0; foreach ($tableheaders as $item) { $myxls->write(0, $colnum, $item, $formatbc); $colnum++; } $rownum = 1; } else { if ($fileformat == 'Excel') { require_once "{$CFG->libdir}/excellib.class.php"; $filename .= ".xls"; // Creating a workbook. $workbook = new MoodleExcelWorkbook("-"); // Sending HTTP headers. $workbook->send($filename); // Creating the first worksheet. $sheettitle = get_string('participants', 'offlinequiz'); $myxls = $workbook->add_worksheet($sheettitle); // Format types. $format = $workbook->add_format(); $format->set_bold(0); $formatbc = $workbook->add_format(); $formatbc->set_bold(1); $formatbc->set_align('center'); $formatb = new StdClass(); $formatb = $workbook->add_format(); $formatb->set_bold(1); $formaty = $workbook->add_format(); $formaty->set_bg_color('yellow'); $formatc = $workbook->add_format(); $formatc->set_align('center'); $formatr = $workbook->add_format(); $formatr->set_bold(1); $formatr->set_color('red'); $formatr->set_align('center'); $formatg = $workbook->add_format(); $formatg->set_bold(1); $formatg->set_color('green'); $formatg->set_align('center'); // Print worksheet headers. $colnum = 0; foreach ($tableheaders as $item) { $myxls->write(0, $colnum, $item, $formatbc); $colnum++; } $rownum = 1; } else { if ($fileformat == 'CSV') { $filename .= ".txt"; header("Content-Type: application/download\n"); header("Content-Disposition: attachment; filename=\"{$filename}\""); header("Expires: 0"); header("Cache-Control: must-revalidate,post-check=0,pre-check=0"); header("Pragma: public"); $headers = implode(", ", $tableheaders); echo $headers . " \n"; } } } $lists = $DB->get_records('offlinequiz_p_lists', array('offlinequizid' => $offlinequiz->id)); $participants = $DB->get_records_sql($sql, $params); if ($participants) { foreach ($participants as $participant) { $userid = $participant->userid; $attempt = false; $sql = "SELECT COUNT(*)\n FROM {offlinequiz_results}\n WHERE userid = :userid\n AND offlinequizid = :offlinequizid\n AND status = 'complete'"; if ($DB->count_records_sql($sql, array('userid' => $userid, 'offlinequizid' => $offlinequiz->id)) > 0) { $attempt = true; } $row = array(fullname($participant), $participant->{$offlinequizconfig->ID_field}, $lists[$participant->listid]->name, $attempt ? get_string('yes') : get_string('no'), $participant->checked ? get_string('yes') : get_string('no')); if ($fileformat == 'Excel' or $fileformat == 'ODS') { $colnum = 0; foreach ($row as $item) { $myxls->write($rownum, $colnum, $item, $format); $colnum++; } $rownum++; } else { if ($fileformat == 'CSV') { $text = implode(", ", $row); echo $text . "\n"; } } } } if ($fileformat == 'Excel' or $fileformat == 'ODS') { $workbook->close(); } exit; }
private function print_error_report($offlinequiz) { global $CFG, $DB, $OUTPUT; offlinequiz_load_useridentification(); $offlinequizconfig = get_config('offlinequiz'); $nologs = optional_param('nologs', 0, PARAM_INT); $pagesize = optional_param('pagesize', 10, PARAM_INT); $letterstr = 'ABCDEFGHIJKL'; require_once 'errorpages_table.php'; $tableparams = array('q' => $offlinequiz->id, 'mode' => 'rimport', 'action' => 'delete', 'strreallydel' => addslashes(get_string('deletepagecheck', 'offlinequiz'))); $table = new offlinequiz_selectall_table('mod_offlinequiz_import_report', 'report.php', $tableparams); $tablecolumns = array('checkbox', 'counter', 'userkey', 'groupnumber', 'pagenumber', 'time', 'error', 'info', 'link'); $tableheaders = array('', '#', get_string($offlinequizconfig->ID_field, 'offlinequiz_rimport'), get_string('group'), get_string('page'), get_string('importedon', 'offlinequiz_rimport'), get_string('error'), get_string('info'), ''); $table->initialbars(true); $table->define_columns($tablecolumns); $table->define_headers($tableheaders); $table->define_baseurl($CFG->wwwroot . '/mod/offlinequiz/report.php?mode=rimport&q=' . $offlinequiz->id . '&nologs=' . $nologs . '&pagesize=' . $pagesize); $table->sortable(true, 'time'); // Sorted by lastname by default. $table->initialbars(true); $table->column_class('checkbox', 'checkbox'); $table->column_class('counter', 'counter'); $table->column_class('username', 'username'); $table->column_class('group', 'group'); $table->column_class('page', 'page'); $table->column_class('time', 'time'); $table->column_class('error', 'error'); $table->column_class('link', 'link'); $table->set_attribute('cellspacing', '0'); $table->set_attribute('cellpadding', '4'); $table->set_attribute('id', 'errorpages'); $table->set_attribute('class', 'errorpages'); $table->set_attribute('align', 'center'); $table->set_attribute('border', '1'); $table->no_sorting('checkbox'); $table->no_sorting('counter'); $table->no_sorting('info'); $table->no_sorting('link'); // Start working -- this is necessary as soon as the niceties are over. $table->setup(); // Construct the SQL. $sql = "SELECT *\n FROM {offlinequiz_scanned_pages}\n WHERE offlinequizid = :offlinequizid\n AND (status = 'error'\n OR status = 'suspended'\n OR error = 'missingpages')"; $params = array('offlinequizid' => $offlinequiz->id); // Add extra limits due to sorting by question grade. if ($sort = $table->get_sql_sort()) { if (strpos($sort, 'checkbox') === false && strpos($sort, 'counter') === false && strpos($sort, 'info') === false && strpos($sort, 'link') === false) { $sql .= ' ORDER BY ' . $sort; } } $errorpages = $DB->get_records_sql($sql, $params); $strtimeformat = get_string('strftimedatetime'); // Options for the popup_action. $options = array(); $options['height'] = 1200; // Optional. $options['width'] = 1170; // Optional. $options['resizable'] = false; $counter = 1; foreach ($errorpages as $page) { if ($page->error == 'filenotfound') { $actionlink = ''; } else { if ($page->error == 'missingpages') { $url = new moodle_url($CFG->wwwroot . '/mod/offlinequiz/image.php?pageid=' . $page->id . '&resultid=' . $page->resultid); $title = get_string('showpage', 'offlinequiz_rimport'); } else { $url = new moodle_url($CFG->wwwroot . '/mod/offlinequiz/correct.php?pageid=' . $page->id); $title = get_string('correcterror', 'offlinequiz_rimport'); } $actionlink = $OUTPUT->action_link($url, $title, new popup_action('click', $url, 'correct' . $page->id, $options)); } $groupstr = '?'; $groupnumber = $page->groupnumber; if ($groupnumber > 0 and $groupnumber <= $offlinequiz->numgroups) { $groupstr = $letterstr[$page->groupnumber - 1]; } $errorstr = ''; if (!empty($page->error)) { $errorstr = get_string('error' . $page->error, 'offlinequiz_rimport'); } if ($page->status == 'suspended') { $errorstr = get_string('waitingforanalysis', 'offlinequiz_rimport'); } $row = array('<input type="checkbox" name="p' . $page->id . '" value="' . $page->id . '" />', $counter . ' ', $page->userkey, $groupstr, empty($page->pagenumber) ? '?' : $page->pagenumber, userdate($page->time, $strtimeformat), $errorstr, $page->info, $actionlink); $table->add_data($row); $counter++; } if (!$table->print_nothing_to_display()) { // Print the table. $table->print_html(); } }
function offlinequiz_evaluation_cron($jobid = 0, $verbose = false) { global $CFG, $DB; raise_memory_limit(MEMORY_EXTRA); // Only count the jobs with status processing that have been started in the last 24 hours. $expiretime = time() - 86400; $runningsql = "SELECT COUNT(*)\n FROM {offlinequiz_queue}\n WHERE status = 'processing'\n AND timestart > :expiretime"; $runningjobs = $DB->count_records_sql($runningsql, array('expiretime' => $expiretime)); if ($runningjobs >= OFFLINEQUIZ_MAX_CRON_JOBS) { echo "Too many jobs running! Exiting!"; return; } // TODO do this properly. Just for testing. $sql = "SELECT * FROM {offlinequiz_queue} WHERE status = 'new'"; $params = array(); if ($jobid) { $sql .= ' AND id = :jobid '; $params['jobid'] = $jobid; } $sql .= " ORDER BY id ASC"; // If there are no new jobs, we simply exit. if (!($jobs = $DB->get_records_sql($sql, $params, 0, OFFLINEQUIZ_TOP_QUEUE_JOBS))) { if ($verbose) { echo get_string('nothingtodo', 'offlinequiz'); } return; } $numberofjobs = count($jobs); if ($verbose) { $pbar = new progress_bar('offlinequizcronbar', 500, true); $pbar->create(); $pbar->update(0, $numberofjobs, "Processing job - {0}/{$numberofjobs}."); } $numberdone = 0; foreach ($jobs as $job) { // Check whether the status is still 'new' (might have been changed by other cronjob). $transaction = $DB->start_delegated_transaction(); $status = $DB->get_field('offlinequiz_queue', 'status', array('id' => $job->id)); if ($status == 'new') { $DB->set_field('offlinequiz_queue', 'status', 'processing', array('id' => $job->id)); $job->timestart = time(); $DB->set_field('offlinequiz_queue', 'timestart', $job->timestart, array('id' => $job->id)); $alreadydone = false; } else { $alreadydone = true; } $transaction->allow_commit(); // If the job is still new, process it! if (!$alreadydone) { // Set up the context for this job. if (!($offlinequiz = $DB->get_record('offlinequiz', array('id' => $job->offlinequizid)))) { $DB->set_field('offlinequiz_queue', 'status', 'error', array('id' => $job->id)); $DB->set_field('offlinequiz_queue', 'info', 'offlinequiz not found', array('id' => $job->id)); continue; } if (!($course = $DB->get_record('course', array('id' => $offlinequiz->course)))) { $DB->set_field('offlinequiz_queue', 'status', 'error', array('id' => $job->id)); $DB->set_field('offlinequiz_queue', 'info', 'course not found', array('id' => $job->id)); continue; } if (!($cm = get_coursemodule_from_instance("offlinequiz", $offlinequiz->id, $course->id))) { $DB->set_field('offlinequiz_queue', 'status', 'error', array('id' => $job->id)); $DB->set_field('offlinequiz_queue', 'info', 'course module found', array('id' => $job->id)); continue; } if (!($context = context_module::instance($cm->id))) { $DB->set_field('offlinequiz_queue', 'status', 'error', array('id' => $job->id)); $DB->set_field('offlinequiz_queue', 'info', 'context not found', array('id' => $job->id)); continue; } if (!($groups = $DB->get_records('offlinequiz_groups', array('offlinequizid' => $offlinequiz->id), 'number', '*', 0, $offlinequiz->numgroups))) { $DB->set_field('offlinequiz_queue', 'status', 'error', array('id' => $job->id)); $DB->set_field('offlinequiz_queue', 'info', 'no offlinequiz groups found', array('id' => $job->id)); continue; } $coursecontext = context_course::instance($course->id); offlinequiz_load_useridentification(); // TODO. $jobdata = $DB->get_records_sql("\n SELECT *\n FROM {offlinequiz_queue_data}\n WHERE queueid = :queueid\n AND status = 'new'", array('queueid' => $job->id)); list($maxquestions, $maxanswers, $formtype, $questionsperpage) = offlinequiz_get_question_numbers($offlinequiz, $groups); $dirname = ''; $doubleentry = 0; foreach ($jobdata as $data) { $starttime = time(); $DB->set_field('offlinequiz_queue_data', 'status', 'processing', array('id' => $data->id)); // We remember the directory name to be able to remove it later. if (empty($dirname)) { $pathparts = pathinfo($data->filename); $dirname = $pathparts['dirname']; } set_time_limit(120); try { // Create a new scanner for every page. $scanner = new offlinequiz_page_scanner($offlinequiz, $context->id, $maxquestions, $maxanswers); // Try to load the image file. echo 'job ' . $job->id . ': evaluating ' . $data->filename . "\n"; $scannedpage = $scanner->load_image($data->filename); if ($scannedpage->status == 'ok') { echo 'job ' . $job->id . ': image loaded ' . $scannedpage->filename . "\n"; } else { if ($scannedpage->error == 'filenotfound') { echo 'job ' . $job->id . ': image file not found: ' . $scannedpage->filename . "\n"; } } // Unset the origfilename because we don't need it in the DB. unset($scannedpage->origfilename); $scannedpage->offlinequizid = $offlinequiz->id; // If we could load the image file, the status is 'ok', so we can check the page for errors. if ($scannedpage->status == 'ok') { // We autorotate so check_scanned_page will return a potentially new scanner and the scannedpage. list($scanner, $scannedpage) = offlinequiz_check_scanned_page($offlinequiz, $scanner, $scannedpage, $job->importuserid, $coursecontext, true); } else { if (property_exists($scannedpage, 'id') && !empty($scannedpage->id)) { $DB->update_record('offlinequiz_scanned_pages', $scannedpage); } else { $scannedpage->id = $DB->insert_record('offlinequiz_scanned_pages', $scannedpage); } } echo 'job ' . $job->id . ': scannedpage id ' . $scannedpage->id . "\n"; // If the status is still 'ok', we can process the answers. This potentially submits the page and // checks whether the result for a student is complete. if ($scannedpage->status == 'ok') { // We can process the answers and submit them if possible. $scannedpage = offlinequiz_process_scanned_page($offlinequiz, $scanner, $scannedpage, $job->importuserid, $questionsperpage, $coursecontext, true); echo 'job ' . $job->id . ': processed answers for ' . $scannedpage->id . "\n"; } else { if ($scannedpage->status == 'error' && $scannedpage->error == 'resultexists') { // Already process the answers but don't submit them. $scannedpage = offlinequiz_process_scanned_page($offlinequiz, $scanner, $scannedpage, $job->importuserid, $questionsperpage, $coursecontext, false); // Compare the old and the new result wrt. the choices. $scannedpage = offlinequiz_check_different_result($scannedpage); } } // If there is something to correct then store the hotspots for retrieval in correct.php. if ($scannedpage->status != 'ok' && $scannedpage->error != 'couldnotgrab' && $scannedpage->error != 'notadjusted' && $scannedpage->error != 'grouperror') { $scanner->store_hotspots($scannedpage->id); } if ($scannedpage->status == 'ok' || $scannedpage->status == 'submitted' || $scannedpage->status == 'suspended' || $scannedpage->error == 'missingpages') { // Mark the file as processed. $DB->set_field('offlinequiz_queue_data', 'status', 'processed', array('id' => $data->id)); } else { $DB->set_field('offlinequiz_queue_data', 'status', 'error', array('id' => $data->id)); $DB->set_field('offlinequiz_queue_data', 'error', $scannedpage->error, array('id' => $data->id)); } if ($scannedpage->error == 'doublepage') { $doubleentry++; } } catch (Exception $e) { echo 'job ' . $job->id . ': ' . $e->getMessage() . "\n"; $DB->set_field('offlinequiz_queue_data', 'status', 'error', array('id' => $data->id)); $DB->set_field('offlinequiz_queue_data', 'error', 'couldnotgrab', array('id' => $data->id)); $DB->set_field('offlinequiz_queue_data', 'info', $e->getMessage(), array('id' => $data->id)); $scannedpage->status = 'error'; $scannedpage->error = 'couldnotgrab'; if ($scannedpage->id) { $DB->update_record('offlinequiz_scanned_pages', $scannedpage); } else { $DB->insert_record('offlinequiz_scanned_pages', $scannedpage); } } } // End foreach jobdata. offlinequiz_update_grades($offlinequiz); $job->timefinish = time(); $DB->set_field('offlinequiz_queue', 'timefinish', $job->timefinish, array('id' => $job->id)); $job->status = 'finished'; $DB->set_field('offlinequiz_queue', 'status', 'finished', array('id' => $job->id)); echo date('Y-m-d-H:i') . ": Import queue with id {$job->id} imported.\n\n"; if ($user = $DB->get_record('user', array('id' => $job->importuserid))) { $mailtext = get_string('importisfinished', 'offlinequiz', format_text($offlinequiz->name, FORMAT_PLAIN)); // How many pages have been imported successfully. $countsql = "SELECT COUNT(id)\n FROM {offlinequiz_queue_data}\n WHERE queueid = :queueid\n AND status = 'processed'"; $params = array('queueid' => $job->id); $mailtext .= "\n\n" . get_string('importnumberpages', 'offlinequiz', $DB->count_records_sql($countsql, $params)); // How many pages have an error. $countsql = "SELECT COUNT(id)\n FROM {offlinequiz_queue_data}\n WHERE queueid = :queueid\n AND status = 'error'"; $mailtext .= "\n" . get_string('importnumberverify', 'offlinequiz', $DB->count_records_sql($countsql, $params)); $mailtext .= "\n" . get_string('importnumberexisting', 'offlinequiz', $doubleentry); $linkoverview = "{$CFG->wwwroot}/mod/offlinequiz/report.php?q={$job->offlinequizid}&mode=overview"; $mailtext .= "\n\n" . get_string('importlinkresults', 'offlinequiz', $linkoverview); $linkupload = "{$CFG->wwwroot}/mod/offlinequiz/report.php?q={$job->offlinequizid}&mode=rimport"; $mailtext .= "\n" . get_string('importlinkverify', 'offlinequiz', $linkupload); $mailtext .= "\n\n" . get_string('importtimestart', 'offlinequiz', userdate($job->timestart)); $mailtext .= "\n" . get_string('importtimefinish', 'offlinequiz', userdate($job->timefinish)); email_to_user($user, $CFG->noreplyaddress, get_string('importmailsubject', 'offlinequiz'), $mailtext); } } // End !alreadydone. $numberdone++; if ($verbose) { ob_flush(); $pbar->update($numberdone, $numberofjobs, "Processing job - {$numberdone}/{$numberofjobs}."); } } // End foreach. }