Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/mod/quiz/lib.php
Go to the documentation of this file.
00001 <?php
00002 // This file is part of Moodle - http://moodle.org/
00003 //
00004 // Moodle is free software: you can redistribute it and/or modify
00005 // it under the terms of the GNU General Public License as published by
00006 // the Free Software Foundation, either version 3 of the License, or
00007 // (at your option) any later version.
00008 //
00009 // Moodle is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License
00015 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00016 
00030 defined('MOODLE_INTERNAL') || die();
00031 
00032 require_once($CFG->libdir . '/eventslib.php');
00033 require_once($CFG->dirroot . '/calendar/lib.php');
00034 
00035 
00039 define('QUIZ_MAX_ATTEMPT_OPTION', 10);
00040 define('QUIZ_MAX_QPP_OPTION', 50);
00041 define('QUIZ_MAX_DECIMAL_OPTION', 5);
00042 define('QUIZ_MAX_Q_DECIMAL_OPTION', 7);
00049 define('QUIZ_GRADEHIGHEST', '1');
00050 define('QUIZ_GRADEAVERAGE', '2');
00051 define('QUIZ_ATTEMPTFIRST', '3');
00052 define('QUIZ_ATTEMPTLAST',  '4');
00059 define('QUIZ_MAX_EVENT_LENGTH', 5*24*60*60); // 5 days
00060 
00071 function quiz_add_instance($quiz) {
00072     global $DB;
00073     $cmid = $quiz->coursemodule;
00074 
00075     // Process the options from the form.
00076     $quiz->created = time();
00077     $quiz->questions = '';
00078     $result = quiz_process_options($quiz);
00079     if ($result && is_string($result)) {
00080         return $result;
00081     }
00082 
00083     // Try to store it in the database.
00084     $quiz->id = $DB->insert_record('quiz', $quiz);
00085 
00086     // Do the processing required after an add or an update.
00087     quiz_after_add_or_update($quiz);
00088 
00089     return $quiz->id;
00090 }
00091 
00100 function quiz_update_instance($quiz, $mform) {
00101     global $CFG, $DB;
00102 
00103     // Process the options from the form.
00104     $result = quiz_process_options($quiz);
00105     if ($result && is_string($result)) {
00106         return $result;
00107     }
00108 
00109     $oldquiz = $DB->get_record('quiz', array('id' => $quiz->instance));
00110 
00111     // Repaginate, if asked to.
00112     if (!$quiz->shufflequestions && !empty($quiz->repaginatenow)) {
00113         require_once($CFG->dirroot . '/mod/quiz/locallib.php');
00114         $quiz->questions = quiz_repaginate(quiz_clean_layout($oldquiz->questions, true),
00115                 $quiz->questionsperpage);
00116     }
00117     unset($quiz->repaginatenow);
00118 
00119     // Update the database.
00120     $quiz->id = $quiz->instance;
00121     $DB->update_record('quiz', $quiz);
00122 
00123     // Do the processing required after an add or an update.
00124     quiz_after_add_or_update($quiz);
00125 
00126     if ($oldquiz->grademethod != $quiz->grademethod) {
00127         require_once($CFG->dirroot . '/mod/quiz/locallib.php');
00128         $quiz->sumgrades = $oldquiz->sumgrades;
00129         $quiz->grade = $oldquiz->grade;
00130         quiz_update_all_final_grades($quiz);
00131         quiz_update_grades($quiz);
00132     }
00133 
00134     // Delete any previous preview attempts
00135     quiz_delete_previews($quiz);
00136 
00137     return true;
00138 }
00139 
00148 function quiz_delete_instance($id) {
00149     global $DB;
00150 
00151     $quiz = $DB->get_record('quiz', array('id' => $id), '*', MUST_EXIST);
00152 
00153     quiz_delete_all_attempts($quiz);
00154     quiz_delete_all_overrides($quiz);
00155 
00156     $DB->delete_records('quiz_question_instances', array('quiz' => $quiz->id));
00157     $DB->delete_records('quiz_feedback', array('quizid' => $quiz->id));
00158 
00159     $events = $DB->get_records('event', array('modulename' => 'quiz', 'instance' => $quiz->id));
00160     foreach ($events as $event) {
00161         $event = calendar_event::load($event);
00162         $event->delete();
00163     }
00164 
00165     quiz_grade_item_delete($quiz);
00166     $DB->delete_records('quiz', array('id' => $quiz->id));
00167 
00168     return true;
00169 }
00170 
00178 function quiz_delete_override($quiz, $overrideid) {
00179     global $DB;
00180 
00181     $override = $DB->get_record('quiz_overrides', array('id' => $overrideid), '*', MUST_EXIST);
00182 
00183     // Delete the events
00184     $events = $DB->get_records('event', array('modulename' => 'quiz',
00185             'instance' => $quiz->id, 'groupid' => (int)$override->groupid,
00186             'userid' => (int)$override->userid));
00187     foreach ($events as $event) {
00188         $eventold = calendar_event::load($event);
00189         $eventold->delete();
00190     }
00191 
00192     $DB->delete_records('quiz_overrides', array('id' => $overrideid));
00193     return true;
00194 }
00195 
00201 function quiz_delete_all_overrides($quiz) {
00202     global $DB;
00203 
00204     $overrides = $DB->get_records('quiz_overrides', array('quiz' => $quiz->id), 'id');
00205     foreach ($overrides as $override) {
00206         quiz_delete_override($quiz, $override->id);
00207     }
00208 }
00209 
00225 function quiz_update_effective_access($quiz, $userid) {
00226     global $DB;
00227 
00228     // check for user override
00229     $override = $DB->get_record('quiz_overrides', array('quiz' => $quiz->id, 'userid' => $userid));
00230 
00231     if (!$override) {
00232         $override = new stdClass();
00233         $override->timeopen = null;
00234         $override->timeclose = null;
00235         $override->timelimit = null;
00236         $override->attempts = null;
00237         $override->password = null;
00238     }
00239 
00240     // check for group overrides
00241     $groupings = groups_get_user_groups($quiz->course, $userid);
00242 
00243     if (!empty($groupings[0])) {
00244         // Select all overrides that apply to the User's groups
00245         list($extra, $params) = $DB->get_in_or_equal(array_values($groupings[0]));
00246         $sql = "SELECT * FROM {quiz_overrides}
00247                 WHERE groupid $extra AND quiz = ?";
00248         $params[] = $quiz->id;
00249         $records = $DB->get_records_sql($sql, $params);
00250 
00251         // Combine the overrides
00252         $opens = array();
00253         $closes = array();
00254         $limits = array();
00255         $attempts = array();
00256         $passwords = array();
00257 
00258         foreach ($records as $gpoverride) {
00259             if (isset($gpoverride->timeopen)) {
00260                 $opens[] = $gpoverride->timeopen;
00261             }
00262             if (isset($gpoverride->timeclose)) {
00263                 $closes[] = $gpoverride->timeclose;
00264             }
00265             if (isset($gpoverride->timelimit)) {
00266                 $limits[] = $gpoverride->timelimit;
00267             }
00268             if (isset($gpoverride->attempts)) {
00269                 $attempts[] = $gpoverride->attempts;
00270             }
00271             if (isset($gpoverride->password)) {
00272                 $passwords[] = $gpoverride->password;
00273             }
00274         }
00275         // If there is a user override for a setting, ignore the group override
00276         if (is_null($override->timeopen) && count($opens)) {
00277             $override->timeopen = min($opens);
00278         }
00279         if (is_null($override->timeclose) && count($closes)) {
00280             $override->timeclose = max($closes);
00281         }
00282         if (is_null($override->timelimit) && count($limits)) {
00283             $override->timelimit = max($limits);
00284         }
00285         if (is_null($override->attempts) && count($attempts)) {
00286             $override->attempts = max($attempts);
00287         }
00288         if (is_null($override->password) && count($passwords)) {
00289             $override->password = array_shift($passwords);
00290             if (count($passwords)) {
00291                 $override->extrapasswords = $passwords;
00292             }
00293         }
00294 
00295     }
00296 
00297     // merge with quiz defaults
00298     $keys = array('timeopen', 'timeclose', 'timelimit', 'attempts', 'password', 'extrapasswords');
00299     foreach ($keys as $key) {
00300         if (isset($override->{$key})) {
00301             $quiz->{$key} = $override->{$key};
00302         }
00303     }
00304 
00305     return $quiz;
00306 }
00307 
00313 function quiz_delete_all_attempts($quiz) {
00314     global $CFG, $DB;
00315     require_once($CFG->dirroot . '/mod/quiz/locallib.php');
00316     question_engine::delete_questions_usage_by_activities(new qubaids_for_quiz($quiz->id));
00317     $DB->delete_records('quiz_attempts', array('quiz' => $quiz->id));
00318     $DB->delete_records('quiz_grades', array('quiz' => $quiz->id));
00319 }
00320 
00329 function quiz_get_best_grade($quiz, $userid) {
00330     global $DB;
00331     $grade = $DB->get_field('quiz_grades', 'grade',
00332             array('quiz' => $quiz->id, 'userid' => $userid));
00333 
00334     // Need to detect errors/no result, without catching 0 grades.
00335     if ($grade === false) {
00336         return null;
00337     }
00338 
00339     return $grade + 0; // Convert to number.
00340 }
00341 
00350 function quiz_has_grades($quiz) {
00351     return $quiz->grade >= 0.000005 && $quiz->sumgrades >= 0.000005;
00352 }
00353 
00367 function quiz_user_outline($course, $user, $mod, $quiz) {
00368     global $DB, $CFG;
00369     require_once("$CFG->libdir/gradelib.php");
00370     $grades = grade_get_grades($course->id, 'mod', 'quiz', $quiz->id, $user->id);
00371 
00372     if (empty($grades->items[0]->grades)) {
00373         return null;
00374     } else {
00375         $grade = reset($grades->items[0]->grades);
00376     }
00377 
00378     $result = new stdClass();
00379     $result->info = get_string('grade') . ': ' . $grade->str_long_grade;
00380 
00381     //datesubmitted == time created. dategraded == time modified or time overridden
00382     //if grade was last modified by the user themselves use date graded. Otherwise use
00383     // date submitted
00384     // TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704
00385     if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) {
00386         $result->time = $grade->dategraded;
00387     } else {
00388         $result->time = $grade->datesubmitted;
00389     }
00390 
00391     return $result;
00392 }
00393 
00405 function quiz_user_complete($course, $user, $mod, $quiz) {
00406     global $DB, $CFG, $OUTPUT;
00407     require_once("$CFG->libdir/gradelib.php");
00408 
00409     $grades = grade_get_grades($course->id, 'mod', 'quiz', $quiz->id, $user->id);
00410     if (!empty($grades->items[0]->grades)) {
00411         $grade = reset($grades->items[0]->grades);
00412         echo $OUTPUT->container(get_string('grade').': '.$grade->str_long_grade);
00413         if ($grade->str_feedback) {
00414             echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback);
00415         }
00416     }
00417 
00418     if ($attempts = $DB->get_records('quiz_attempts',
00419             array('userid' => $user->id, 'quiz' => $quiz->id), 'attempt')) {
00420         foreach ($attempts as $attempt) {
00421             echo get_string('attempt', 'quiz').' '.$attempt->attempt.': ';
00422             if ($attempt->timefinish == 0) {
00423                 print_string('unfinished');
00424             } else {
00425                 echo quiz_format_grade($quiz, $attempt->sumgrades) . '/' .
00426                         quiz_format_grade($quiz, $quiz->sumgrades);
00427             }
00428             echo ' - '.userdate($attempt->timemodified).'<br />';
00429         }
00430     } else {
00431         print_string('noattempts', 'quiz');
00432     }
00433 
00434     return true;
00435 }
00436 
00442 function quiz_cron() {
00443     global $DB, $CFG;
00444 
00445     // First handle standard plugins.
00446     cron_execute_plugin_type('quiz', 'quiz reports');
00447 
00448     // The deal with any plugins that do it the legacy way.
00449     mtrace("Starting legacy quiz reports");
00450     $timenow = time();
00451     if ($reports = $DB->get_records_select('quiz_reports', "cron > 0 AND ((? - lastcron) > cron)", array($timenow))) {
00452         foreach ($reports as $report) {
00453             $cronfile = "$CFG->dirroot/mod/quiz/report/$report->name/cron.php";
00454             if (file_exists($cronfile)) {
00455                 include_once($cronfile);
00456                 $cron_function = 'quiz_report_'.$report->name."_cron";
00457                 if (function_exists($cron_function)) {
00458                     mtrace("Processing quiz report cron function $cron_function ...", '');
00459                     $pre_dbqueries = null;
00460                     $pre_dbqueries = $DB->perf_get_queries();
00461                     $pre_time      = microtime(1);
00462                     if ($cron_function()) {
00463                         $DB->set_field('quiz_reports', "lastcron", $timenow, array("id"=>$report->id));
00464                     }
00465                     if (isset($pre_dbqueries)) {
00466                         mtrace("... used " . ($DB->perf_get_queries() - $pre_dbqueries) . " dbqueries");
00467                         mtrace("... used " . (microtime(1) - $pre_time) . " seconds");
00468                     }
00469                     @set_time_limit(0);
00470                     mtrace("done.");
00471                 }
00472             }
00473         }
00474     }
00475     mtrace("Finished legacy quiz reports");
00476 }
00477 
00486 function quiz_get_user_attempts($quizid, $userid, $status = 'finished', $includepreviews = false) {
00487     global $DB;
00488     $status_condition = array(
00489         'all' => '',
00490         'finished' => ' AND timefinish > 0',
00491         'unfinished' => ' AND timefinish = 0'
00492     );
00493     $previewclause = '';
00494     if (!$includepreviews) {
00495         $previewclause = ' AND preview = 0';
00496     }
00497     return $DB->get_records_select('quiz_attempts',
00498             'quiz = ? AND userid = ?' . $previewclause . $status_condition[$status],
00499             array($quizid, $userid), 'attempt ASC');
00500 }
00501 
00510 function quiz_get_user_grades($quiz, $userid = 0) {
00511     global $CFG, $DB;
00512 
00513     $params = array($quiz->id);
00514     $usertest = '';
00515     if ($userid) {
00516         $params[] = $userid;
00517         $usertest = 'AND u.id = ?';
00518     }
00519     return $DB->get_records_sql("
00520             SELECT
00521                 u.id,
00522                 u.id AS userid,
00523                 qg.grade AS rawgrade,
00524                 qg.timemodified AS dategraded,
00525                 MAX(qa.timefinish) AS datesubmitted
00526 
00527             FROM {user} u
00528             JOIN {quiz_grades} qg ON u.id = qg.userid
00529             JOIN {quiz_attempts} qa ON qa.quiz = qg.quiz AND qa.userid = u.id
00530 
00531             WHERE qg.quiz = ?
00532             $usertest
00533             GROUP BY u.id, qg.grade, qg.timemodified", $params);
00534 }
00535 
00543 function quiz_format_grade($quiz, $grade) {
00544     if (is_null($grade)) {
00545         return get_string('notyetgraded', 'quiz');
00546     }
00547     return format_float($grade, $quiz->decimalpoints);
00548 }
00549 
00557 function quiz_format_question_grade($quiz, $grade) {
00558     if (empty($quiz->questiondecimalpoints)) {
00559         $quiz->questiondecimalpoints = -1;
00560     }
00561     if ($quiz->questiondecimalpoints == -1) {
00562         return format_float($grade, $quiz->decimalpoints);
00563     } else {
00564         return format_float($grade, $quiz->questiondecimalpoints);
00565     }
00566 }
00567 
00574 function quiz_update_grades($quiz, $userid = 0, $nullifnone = true) {
00575     global $CFG, $DB;
00576     require_once($CFG->libdir.'/gradelib.php');
00577 
00578     if ($quiz->grade == 0) {
00579         quiz_grade_item_update($quiz);
00580 
00581     } else if ($grades = quiz_get_user_grades($quiz, $userid)) {
00582         quiz_grade_item_update($quiz, $grades);
00583 
00584     } else if ($userid && $nullifnone) {
00585         $grade = new stdClass();
00586         $grade->userid = $userid;
00587         $grade->rawgrade = null;
00588         quiz_grade_item_update($quiz, $grade);
00589 
00590     } else {
00591         quiz_grade_item_update($quiz);
00592     }
00593 }
00594 
00598 function quiz_upgrade_grades() {
00599     global $DB;
00600 
00601     $sql = "SELECT COUNT('x')
00602               FROM {quiz} a, {course_modules} cm, {modules} m
00603              WHERE m.name='quiz' AND m.id=cm.module AND cm.instance=a.id";
00604     $count = $DB->count_records_sql($sql);
00605 
00606     $sql = "SELECT a.*, cm.idnumber AS cmidnumber, a.course AS courseid
00607               FROM {quiz} a, {course_modules} cm, {modules} m
00608              WHERE m.name='quiz' AND m.id=cm.module AND cm.instance=a.id";
00609     $rs = $DB->get_recordset_sql($sql);
00610     if ($rs->valid()) {
00611         $pbar = new progress_bar('quizupgradegrades', 500, true);
00612         $i=0;
00613         foreach ($rs as $quiz) {
00614             $i++;
00615             upgrade_set_timeout(60*5); // set up timeout, may also abort execution
00616             quiz_update_grades($quiz, 0, false);
00617             $pbar->update($i, $count, "Updating Quiz grades ($i/$count).");
00618         }
00619     }
00620     $rs->close();
00621 }
00622 
00630 function quiz_grade_item_update($quiz, $grades = null) {
00631     global $CFG, $OUTPUT;
00632     require_once($CFG->dirroot . '/mod/quiz/locallib.php');
00633     require_once($CFG->libdir.'/gradelib.php');
00634 
00635     if (array_key_exists('cmidnumber', $quiz)) { // may not be always present
00636         $params = array('itemname' => $quiz->name, 'idnumber' => $quiz->cmidnumber);
00637     } else {
00638         $params = array('itemname' => $quiz->name);
00639     }
00640 
00641     if ($quiz->grade > 0) {
00642         $params['gradetype'] = GRADE_TYPE_VALUE;
00643         $params['grademax']  = $quiz->grade;
00644         $params['grademin']  = 0;
00645 
00646     } else {
00647         $params['gradetype'] = GRADE_TYPE_NONE;
00648     }
00649 
00650     // description by TJ:
00651     // 1. If the quiz is set to not show grades while the quiz is still open,
00652     //    and is set to show grades after the quiz is closed, then create the
00653     //    grade_item with a show-after date that is the quiz close date.
00654     // 2. If the quiz is set to not show grades at either of those times,
00655     //    create the grade_item as hidden.
00656     // 3. If the quiz is set to show grades, create the grade_item visible.
00657     $openreviewoptions = mod_quiz_display_options::make_from_quiz($quiz,
00658             mod_quiz_display_options::LATER_WHILE_OPEN);
00659     $closedreviewoptions = mod_quiz_display_options::make_from_quiz($quiz,
00660             mod_quiz_display_options::AFTER_CLOSE);
00661     if ($openreviewoptions->marks < question_display_options::MARK_AND_MAX &&
00662             $closedreviewoptions->marks < question_display_options::MARK_AND_MAX) {
00663         $params['hidden'] = 1;
00664 
00665     } else if ($openreviewoptions->marks < question_display_options::MARK_AND_MAX &&
00666             $closedreviewoptions->marks >= question_display_options::MARK_AND_MAX) {
00667         if ($quiz->timeclose) {
00668             $params['hidden'] = $quiz->timeclose;
00669         } else {
00670             $params['hidden'] = 1;
00671         }
00672 
00673     } else {
00674         // a) both open and closed enabled
00675         // b) open enabled, closed disabled - we can not "hide after",
00676         //    grades are kept visible even after closing
00677         $params['hidden'] = 0;
00678     }
00679 
00680     if ($grades  === 'reset') {
00681         $params['reset'] = true;
00682         $grades = null;
00683     }
00684 
00685     $gradebook_grades = grade_get_grades($quiz->course, 'mod', 'quiz', $quiz->id);
00686     if (!empty($gradebook_grades->items)) {
00687         $grade_item = $gradebook_grades->items[0];
00688         if ($grade_item->locked) {
00689             $confirm_regrade = optional_param('confirm_regrade', 0, PARAM_INT);
00690             if (!$confirm_regrade) {
00691                 $message = get_string('gradeitemislocked', 'grades');
00692                 $back_link = $CFG->wwwroot . '/mod/quiz/report.php?q=' . $quiz->id .
00693                         '&amp;mode=overview';
00694                 $regrade_link = qualified_me() . '&amp;confirm_regrade=1';
00695                 echo $OUTPUT->box_start('generalbox', 'notice');
00696                 echo '<p>'. $message .'</p>';
00697                 echo $OUTPUT->container_start('buttons');
00698                 echo $OUTPUT->single_button($regrade_link, get_string('regradeanyway', 'grades'));
00699                 echo $OUTPUT->single_button($back_link,  get_string('cancel'));
00700                 echo $OUTPUT->container_end();
00701                 echo $OUTPUT->box_end();
00702 
00703                 return GRADE_UPDATE_ITEM_LOCKED;
00704             }
00705         }
00706     }
00707 
00708     return grade_update('mod/quiz', $quiz->course, 'mod', 'quiz', $quiz->id, 0, $grades, $params);
00709 }
00710 
00717 function quiz_grade_item_delete($quiz) {
00718     global $CFG;
00719     require_once($CFG->libdir . '/gradelib.php');
00720 
00721     return grade_update('mod/quiz', $quiz->course, 'mod', 'quiz', $quiz->id, 0,
00722             null, array('deleted' => 1));
00723 }
00724 
00733 function quiz_get_participants($quizid) {
00734     global $CFG, $DB;
00735 
00736     return $DB->get_records_sql('
00737             SELECT DISTINCT userid, userid
00738             JOIN {quiz_attempts} qa
00739             WHERE a.quiz = ?', array($quizid));
00740 }
00741 
00752 function quiz_refresh_events($courseid = 0) {
00753     global $DB;
00754 
00755     if ($courseid == 0) {
00756         if (!$quizzes = $DB->get_records('quiz')) {
00757             return true;
00758         }
00759     } else {
00760         if (!$quizzes = $DB->get_records('quiz', array('course' => $courseid))) {
00761             return true;
00762         }
00763     }
00764 
00765     foreach ($quizzes as $quiz) {
00766         quiz_update_events($quiz);
00767     }
00768 
00769     return true;
00770 }
00771 
00775 function quiz_get_recent_mod_activity(&$activities, &$index, $timestart,
00776         $courseid, $cmid, $userid = 0, $groupid = 0) {
00777     global $CFG, $COURSE, $USER, $DB;
00778     require_once('locallib.php');
00779 
00780     if ($COURSE->id == $courseid) {
00781         $course = $COURSE;
00782     } else {
00783         $course = $DB->get_record('course', array('id' => $courseid));
00784     }
00785 
00786     $modinfo =& get_fast_modinfo($course);
00787 
00788     $cm = $modinfo->cms[$cmid];
00789     $quiz = $DB->get_record('quiz', array('id' => $cm->instance));
00790 
00791     if ($userid) {
00792         $userselect = "AND u.id = :userid";
00793         $params['userid'] = $userid;
00794     } else {
00795         $userselect = '';
00796     }
00797 
00798     if ($groupid) {
00799         $groupselect = 'AND gm.groupid = :groupid';
00800         $groupjoin   = 'JOIN {groups_members} gm ON  gm.userid=u.id';
00801         $params['groupid'] = $groupid;
00802     } else {
00803         $groupselect = '';
00804         $groupjoin   = '';
00805     }
00806 
00807     $params['timestart'] = $timestart;
00808     $params['quizid'] = $quiz->id;
00809 
00810     if (!$attempts = $DB->get_records_sql("
00811               SELECT qa.*,
00812                      u.firstname, u.lastname, u.email, u.picture, u.imagealt
00813                 FROM {quiz_attempts} qa
00814                      JOIN {user} u ON u.id = qa.userid
00815                      $groupjoin
00816                WHERE qa.timefinish > :timestart
00817                  AND qa.quiz = :quizid
00818                  AND qa.preview = 0
00819                      $userselect
00820                      $groupselect
00821             ORDER BY qa.timefinish ASC", $params)) {
00822         return;
00823     }
00824 
00825     $context         = get_context_instance(CONTEXT_MODULE, $cm->id);
00826     $accessallgroups = has_capability('moodle/site:accessallgroups', $context);
00827     $viewfullnames   = has_capability('moodle/site:viewfullnames', $context);
00828     $grader          = has_capability('mod/quiz:viewreports', $context);
00829     $groupmode       = groups_get_activity_groupmode($cm, $course);
00830 
00831     if (is_null($modinfo->groups)) {
00832         // load all my groups and cache it in modinfo
00833         $modinfo->groups = groups_get_user_groups($course->id);
00834     }
00835 
00836     $usersgroups = null;
00837     $aname = format_string($cm->name, true);
00838     foreach ($attempts as $attempt) {
00839         if ($attempt->userid != $USER->id) {
00840             if (!$grader) {
00841                 // Grade permission required
00842                 continue;
00843             }
00844 
00845             if ($groupmode == SEPARATEGROUPS and !$accessallgroups) {
00846                 if (is_null($usersgroups)) {
00847                     $usersgroups = groups_get_all_groups($course->id,
00848                             $attempt->userid, $cm->groupingid);
00849                     if (is_array($usersgroups)) {
00850                         $usersgroups = array_keys($usersgroups);
00851                     } else {
00852                         $usersgroups = array();
00853                     }
00854                 }
00855                 if (!array_intersect($usersgroups, $modinfo->groups[$cm->id])) {
00856                     continue;
00857                 }
00858             }
00859         }
00860 
00861         $options = quiz_get_review_options($quiz, $attempt, $context);
00862 
00863         $tmpactivity = new stdClass();
00864 
00865         $tmpactivity->type       = 'quiz';
00866         $tmpactivity->cmid       = $cm->id;
00867         $tmpactivity->name       = $aname;
00868         $tmpactivity->sectionnum = $cm->sectionnum;
00869         $tmpactivity->timestamp  = $attempt->timefinish;
00870 
00871         $tmpactivity->content->attemptid = $attempt->id;
00872         $tmpactivity->content->attempt   = $attempt->attempt;
00873         if (quiz_has_grades($quiz) && $options->marks >= question_display_options::MARK_AND_MAX) {
00874             $tmpactivity->content->sumgrades = quiz_format_grade($quiz, $attempt->sumgrades);
00875             $tmpactivity->content->maxgrade  = quiz_format_grade($quiz, $quiz->sumgrades);
00876         } else {
00877             $tmpactivity->content->sumgrades = null;
00878             $tmpactivity->content->maxgrade  = null;
00879         }
00880 
00881         $tmpactivity->user->id        = $attempt->userid;
00882         $tmpactivity->user->firstname = $attempt->firstname;
00883         $tmpactivity->user->lastname  = $attempt->lastname;
00884         $tmpactivity->user->fullname  = fullname($attempt, $viewfullnames);
00885         $tmpactivity->user->picture   = $attempt->picture;
00886         $tmpactivity->user->imagealt  = $attempt->imagealt;
00887         $tmpactivity->user->email     = $attempt->email;
00888 
00889         $activities[$index++] = $tmpactivity;
00890     }
00891 }
00892 
00893 function quiz_print_recent_mod_activity($activity, $courseid, $detail, $modnames) {
00894     global $CFG, $OUTPUT;
00895 
00896     echo '<table border="0" cellpadding="3" cellspacing="0" class="forum-recent">';
00897 
00898     echo '<tr><td class="userpicture" valign="top">';
00899     echo $OUTPUT->user_picture($activity->user, array('courseid' => $courseid));
00900     echo '</td><td>';
00901 
00902     if ($detail) {
00903         $modname = $modnames[$activity->type];
00904         echo '<div class="title">';
00905         echo '<img src="' . $OUTPUT->pix_url('icon', $activity->type) . '" ' .
00906                 'class="icon" alt="' . $modname . '" />';
00907         echo '<a href="' . $CFG->wwwroot . '/mod/quiz/view.php?id=' .
00908                 $activity->cmid . '">' . $activity->name . '</a>';
00909         echo '</div>';
00910     }
00911 
00912     echo '<div class="grade">';
00913     echo  get_string('attempt', 'quiz', $activity->content->attempt);
00914     if (isset($activity->content->maxgrade)) {
00915         $grades = $activity->content->sumgrades . ' / ' . $activity->content->maxgrade;
00916         echo ': (<a href="' . $CFG->wwwroot . '/mod/quiz/review.php?attempt=' .
00917                 $activity->content->attemptid . '">' . $grades . '</a>)';
00918     }
00919     echo '</div>';
00920 
00921     echo '<div class="user">';
00922     echo '<a href="' . $CFG->wwwroot . '/user/view.php?id=' . $activity->user->id .
00923             '&amp;course=' . $courseid . '">' . $activity->user->fullname .
00924             '</a> - ' . userdate($activity->timestamp);
00925     echo '</div>';
00926 
00927     echo '</td></tr></table>';
00928 
00929     return;
00930 }
00931 
00938 function quiz_process_options($quiz) {
00939     global $CFG;
00940     require_once($CFG->dirroot . '/mod/quiz/locallib.php');
00941     require_once($CFG->libdir . '/questionlib.php');
00942 
00943     $quiz->timemodified = time();
00944 
00945     // Quiz name.
00946     if (!empty($quiz->name)) {
00947         $quiz->name = trim($quiz->name);
00948     }
00949 
00950     // Password field - different in form to stop browsers that remember passwords
00951     // getting confused.
00952     $quiz->password = $quiz->quizpassword;
00953     unset($quiz->quizpassword);
00954 
00955     // Quiz feedback
00956     if (isset($quiz->feedbacktext)) {
00957         // Clean up the boundary text.
00958         for ($i = 0; $i < count($quiz->feedbacktext); $i += 1) {
00959             if (empty($quiz->feedbacktext[$i]['text'])) {
00960                 $quiz->feedbacktext[$i]['text'] = '';
00961             } else {
00962                 $quiz->feedbacktext[$i]['text'] = trim($quiz->feedbacktext[$i]['text']);
00963             }
00964         }
00965 
00966         // Check the boundary value is a number or a percentage, and in range.
00967         $i = 0;
00968         while (!empty($quiz->feedbackboundaries[$i])) {
00969             $boundary = trim($quiz->feedbackboundaries[$i]);
00970             if (!is_numeric($boundary)) {
00971                 if (strlen($boundary) > 0 && $boundary[strlen($boundary) - 1] == '%') {
00972                     $boundary = trim(substr($boundary, 0, -1));
00973                     if (is_numeric($boundary)) {
00974                         $boundary = $boundary * $quiz->grade / 100.0;
00975                     } else {
00976                         return get_string('feedbackerrorboundaryformat', 'quiz', $i + 1);
00977                     }
00978                 }
00979             }
00980             if ($boundary <= 0 || $boundary >= $quiz->grade) {
00981                 return get_string('feedbackerrorboundaryoutofrange', 'quiz', $i + 1);
00982             }
00983             if ($i > 0 && $boundary >= $quiz->feedbackboundaries[$i - 1]) {
00984                 return get_string('feedbackerrororder', 'quiz', $i + 1);
00985             }
00986             $quiz->feedbackboundaries[$i] = $boundary;
00987             $i += 1;
00988         }
00989         $numboundaries = $i;
00990 
00991         // Check there is nothing in the remaining unused fields.
00992         if (!empty($quiz->feedbackboundaries)) {
00993             for ($i = $numboundaries; $i < count($quiz->feedbackboundaries); $i += 1) {
00994                 if (!empty($quiz->feedbackboundaries[$i]) &&
00995                         trim($quiz->feedbackboundaries[$i]) != '') {
00996                     return get_string('feedbackerrorjunkinboundary', 'quiz', $i + 1);
00997                 }
00998             }
00999         }
01000         for ($i = $numboundaries + 1; $i < count($quiz->feedbacktext); $i += 1) {
01001             if (!empty($quiz->feedbacktext[$i]['text']) &&
01002                     trim($quiz->feedbacktext[$i]['text']) != '') {
01003                 return get_string('feedbackerrorjunkinfeedback', 'quiz', $i + 1);
01004             }
01005         }
01006         // Needs to be bigger than $quiz->grade because of '<' test in quiz_feedback_for_grade().
01007         $quiz->feedbackboundaries[-1] = $quiz->grade + 1;
01008         $quiz->feedbackboundaries[$numboundaries] = 0;
01009         $quiz->feedbackboundarycount = $numboundaries;
01010     }
01011 
01012     // Combing the individual settings into the review columns.
01013     $quiz->reviewattempt = quiz_review_option_form_to_db($quiz, 'attempt');
01014     $quiz->reviewcorrectness = quiz_review_option_form_to_db($quiz, 'correctness');
01015     $quiz->reviewmarks = quiz_review_option_form_to_db($quiz, 'marks');
01016     $quiz->reviewspecificfeedback = quiz_review_option_form_to_db($quiz, 'specificfeedback');
01017     $quiz->reviewgeneralfeedback = quiz_review_option_form_to_db($quiz, 'generalfeedback');
01018     $quiz->reviewrightanswer = quiz_review_option_form_to_db($quiz, 'rightanswer');
01019     $quiz->reviewoverallfeedback = quiz_review_option_form_to_db($quiz, 'overallfeedback');
01020     $quiz->reviewattempt |= mod_quiz_display_options::DURING;
01021     $quiz->reviewoverallfeedback &= ~mod_quiz_display_options::DURING;
01022 }
01023 
01029 function quiz_review_option_form_to_db($fromform, $field) {
01030     static $times = array(
01031         'during' => mod_quiz_display_options::DURING,
01032         'immediately' => mod_quiz_display_options::IMMEDIATELY_AFTER,
01033         'open' => mod_quiz_display_options::LATER_WHILE_OPEN,
01034         'closed' => mod_quiz_display_options::AFTER_CLOSE,
01035     );
01036 
01037     $review = 0;
01038     foreach ($times as $whenname => $when) {
01039         $fieldname = $field . $whenname;
01040         if (isset($fromform->$fieldname)) {
01041             $review |= $when;
01042             unset($fromform->$fieldname);
01043         }
01044     }
01045 
01046     return $review;
01047 }
01048 
01055 function quiz_after_add_or_update($quiz) {
01056     global $DB;
01057     $cmid = $quiz->coursemodule;
01058 
01059     // we need to use context now, so we need to make sure all needed info is already in db
01060     $DB->set_field('course_modules', 'instance', $quiz->id, array('id'=>$cmid));
01061     $context = get_context_instance(CONTEXT_MODULE, $cmid);
01062 
01063     // Save the feedback
01064     $DB->delete_records('quiz_feedback', array('quizid' => $quiz->id));
01065 
01066     for ($i = 0; $i <= $quiz->feedbackboundarycount; $i++) {
01067         $feedback = new stdClass();
01068         $feedback->quizid = $quiz->id;
01069         $feedback->feedbacktext = $quiz->feedbacktext[$i]['text'];
01070         $feedback->feedbacktextformat = $quiz->feedbacktext[$i]['format'];
01071         $feedback->mingrade = $quiz->feedbackboundaries[$i];
01072         $feedback->maxgrade = $quiz->feedbackboundaries[$i - 1];
01073         $feedback->id = $DB->insert_record('quiz_feedback', $feedback);
01074         $feedbacktext = file_save_draft_area_files((int)$quiz->feedbacktext[$i]['itemid'],
01075                 $context->id, 'mod_quiz', 'feedback', $feedback->id,
01076                 array('subdirs' => false, 'maxfiles' => -1, 'maxbytes' => 0),
01077                 $quiz->feedbacktext[$i]['text']);
01078         $DB->set_field('quiz_feedback', 'feedbacktext', $feedbacktext,
01079                 array('id' => $feedback->id));
01080     }
01081 
01082     // Store any settings belonging to the access rules.
01083     quiz_access_manager::save_settings($quiz);
01084 
01085     // Update the events relating to this quiz.
01086     quiz_update_events($quiz);
01087 
01088     //update related grade item
01089     quiz_grade_item_update($quiz);
01090 }
01091 
01101 function quiz_update_events($quiz, $override = null) {
01102     global $DB;
01103 
01104     // Load the old events relating to this quiz.
01105     $conds = array('modulename'=>'quiz',
01106                    'instance'=>$quiz->id);
01107     if (!empty($override)) {
01108         // only load events for this override
01109         $conds['groupid'] = isset($override->groupid)?  $override->groupid : 0;
01110         $conds['userid'] = isset($override->userid)?  $override->userid : 0;
01111     }
01112     $oldevents = $DB->get_records('event', $conds);
01113 
01114     // Now make a todo list of all that needs to be updated
01115     if (empty($override)) {
01116         // We are updating the primary settings for the quiz, so we
01117         // need to add all the overrides
01118         $overrides = $DB->get_records('quiz_overrides', array('quiz' => $quiz->id));
01119         // as well as the original quiz (empty override)
01120         $overrides[] = new stdClass();
01121     } else {
01122         // Just do the one override
01123         $overrides = array($override);
01124     }
01125 
01126     foreach ($overrides as $current) {
01127         $groupid   = isset($current->groupid)?  $current->groupid : 0;
01128         $userid    = isset($current->userid)? $current->userid : 0;
01129         $timeopen  = isset($current->timeopen)?  $current->timeopen : $quiz->timeopen;
01130         $timeclose = isset($current->timeclose)? $current->timeclose : $quiz->timeclose;
01131 
01132         // only add open/close events for an override if they differ from the quiz default
01133         $addopen  = empty($current->id) || !empty($current->timeopen);
01134         $addclose = empty($current->id) || !empty($current->timeclose);
01135 
01136         $event = new stdClass();
01137         $event->description = $quiz->intro;
01138         // Events module won't show user events when the courseid is nonzero
01139         $event->courseid    = ($userid) ? 0 : $quiz->course;
01140         $event->groupid     = $groupid;
01141         $event->userid      = $userid;
01142         $event->modulename  = 'quiz';
01143         $event->instance    = $quiz->id;
01144         $event->timestart   = $timeopen;
01145         $event->timeduration = max($timeclose - $timeopen, 0);
01146         $event->visible     = instance_is_visible('quiz', $quiz);
01147         $event->eventtype   = 'open';
01148 
01149         // Determine the event name
01150         if ($groupid) {
01151             $params = new stdClass();
01152             $params->quiz = $quiz->name;
01153             $params->group = groups_get_group_name($groupid);
01154             if ($params->group === false) {
01155                 // group doesn't exist, just skip it
01156                 continue;
01157             }
01158             $eventname = get_string('overridegroupeventname', 'quiz', $params);
01159         } else if ($userid) {
01160             $params = new stdClass();
01161             $params->quiz = $quiz->name;
01162             $eventname = get_string('overrideusereventname', 'quiz', $params);
01163         } else {
01164             $eventname = $quiz->name;
01165         }
01166         if ($addopen or $addclose) {
01167             if ($timeclose and $timeopen and $event->timeduration <= QUIZ_MAX_EVENT_LENGTH) {
01168                 // Single event for the whole quiz.
01169                 if ($oldevent = array_shift($oldevents)) {
01170                     $event->id = $oldevent->id;
01171                 } else {
01172                     unset($event->id);
01173                 }
01174                 $event->name = $eventname;
01175                 // calendar_event::create will reuse a db record if the id field is set
01176                 calendar_event::create($event);
01177             } else {
01178                 // Separate start and end events.
01179                 $event->timeduration  = 0;
01180                 if ($timeopen && $addopen) {
01181                     if ($oldevent = array_shift($oldevents)) {
01182                         $event->id = $oldevent->id;
01183                     } else {
01184                         unset($event->id);
01185                     }
01186                     $event->name = $eventname.' ('.get_string('quizopens', 'quiz').')';
01187                     // calendar_event::create will reuse a db record if the id field is set
01188                     calendar_event::create($event);
01189                 }
01190                 if ($timeclose && $addclose) {
01191                     if ($oldevent = array_shift($oldevents)) {
01192                         $event->id = $oldevent->id;
01193                     } else {
01194                         unset($event->id);
01195                     }
01196                     $event->name      = $eventname.' ('.get_string('quizcloses', 'quiz').')';
01197                     $event->timestart = $timeclose;
01198                     $event->eventtype = 'close';
01199                     calendar_event::create($event);
01200                 }
01201             }
01202         }
01203     }
01204 
01205     // Delete any leftover events
01206     foreach ($oldevents as $badevent) {
01207         $badevent = calendar_event::load($badevent);
01208         $badevent->delete();
01209     }
01210 }
01211 
01215 function quiz_get_view_actions() {
01216     return array('view', 'view all', 'report', 'review');
01217 }
01218 
01222 function quiz_get_post_actions() {
01223     return array('attempt', 'close attempt', 'preview', 'editquestions',
01224             'delete attempt', 'manualgrade');
01225 }
01226 
01231 function quiz_questions_in_use($questionids) {
01232     global $DB, $CFG;
01233     require_once($CFG->libdir . '/questionlib.php');
01234     list($test, $params) = $DB->get_in_or_equal($questionids);
01235     return $DB->record_exists_select('quiz_question_instances',
01236             'question ' . $test, $params) || question_engine::questions_in_use(
01237             $questionids, new qubaid_join('{quiz_attempts} quiza',
01238             'quiza.uniqueid', 'quiza.preview = 0'));
01239 }
01240 
01247 function quiz_reset_course_form_definition($mform) {
01248     $mform->addElement('header', 'quizheader', get_string('modulenameplural', 'quiz'));
01249     $mform->addElement('advcheckbox', 'reset_quiz_attempts',
01250             get_string('removeallquizattempts', 'quiz'));
01251 }
01252 
01257 function quiz_reset_course_form_defaults($course) {
01258     return array('reset_quiz_attempts' => 1);
01259 }
01260 
01267 function quiz_reset_gradebook($courseid, $type='') {
01268     global $CFG, $DB;
01269 
01270     $quizzes = $DB->get_records_sql("
01271             SELECT q.*, cm.idnumber as cmidnumber, q.course as courseid
01272             FROM {modules} m
01273             JOIN {course_modules} cm ON m.id = cm.module
01274             JOIN {quiz} q ON cm.instance = q.id
01275             WHERE m.name = 'quiz' AND cm.course = ?", array($courseid));
01276 
01277     foreach ($quizzes as $quiz) {
01278         quiz_grade_item_update($quiz, 'reset');
01279     }
01280 }
01281 
01292 function quiz_reset_userdata($data) {
01293     global $CFG, $DB;
01294     require_once($CFG->libdir.'/questionlib.php');
01295 
01296     $componentstr = get_string('modulenameplural', 'quiz');
01297     $status = array();
01298 
01299     // Delete attempts.
01300     if (!empty($data->reset_quiz_attempts)) {
01301         require_once($CFG->libdir . '/questionlib.php');
01302 
01303         question_engine::delete_questions_usage_by_activities(new qubaid_join(
01304                 '{quiz_attempts} quiza JOIN {quiz} quiz ON quiza.quiz = quiz.id',
01305                 'quiza.uniqueid', 'quiz.course = :quizcourseid',
01306                 array('quizcourseid' => $data->courseid)));
01307 
01308         $DB->delete_records_select('quiz_attempts',
01309                 'quiz IN (SELECT id FROM {quiz} WHERE course = ?)', array($data->courseid));
01310         $status[] = array(
01311             'component' => $componentstr,
01312             'item' => get_string('attemptsdeleted', 'quiz'),
01313             'error' => false);
01314 
01315         // Remove all grades from gradebook
01316         $DB->delete_records_select('quiz_grades',
01317                 'quiz IN (SELECT id FROM {quiz} WHERE course = ?)', array($data->courseid));
01318         if (empty($data->reset_gradebook_grades)) {
01319             quiz_reset_gradebook($data->courseid);
01320         }
01321         $status[] = array(
01322             'component' => $componentstr,
01323             'item' => get_string('gradesdeleted', 'quiz'),
01324             'error' => false);
01325     }
01326 
01327     // Updating dates - shift may be negative too
01328     if ($data->timeshift) {
01329         shift_course_mod_dates('quiz', array('timeopen', 'timeclose'),
01330                 $data->timeshift, $data->courseid);
01331         $status[] = array(
01332             'component' => $componentstr,
01333             'item' => get_string('openclosedatesupdated', 'quiz'),
01334             'error' => false);
01335     }
01336 
01337     return $status;
01338 }
01339 
01348 function quiz_check_file_access($attemptuniqueid, $questionid, $context = null) {
01349     global $USER, $DB, $CFG;
01350     require_once(dirname(__FILE__).'/attemptlib.php');
01351     require_once(dirname(__FILE__).'/locallib.php');
01352 
01353     $attempt = $DB->get_record('quiz_attempts', array('uniqueid' => $attemptuniqueid));
01354     $attemptobj = quiz_attempt::create($attempt->id);
01355 
01356     // does question exist?
01357     if (!$question = $DB->get_record('question', array('id' => $questionid))) {
01358         return false;
01359     }
01360 
01361     if ($context === null) {
01362         $quiz = $DB->get_record('quiz', array('id' => $attempt->quiz));
01363         $cm = get_coursemodule_from_id('quiz', $quiz->id);
01364         $context = get_context_instance(CONTEXT_MODULE, $cm->id);
01365     }
01366 
01367     // Load those questions and the associated states.
01368     $attemptobj->load_questions(array($questionid));
01369     $attemptobj->load_question_states(array($questionid));
01370 
01371     // obtain state
01372     $state = $attemptobj->get_question_state($questionid);
01373     // obtain questoin
01374     $question = $attemptobj->get_question($questionid);
01375 
01376     // access granted if the current user submitted this file
01377     if ($attempt->userid != $USER->id) {
01378         return false;
01379     }
01380     // access granted if the current user has permission to grade quizzes in this course
01381     if (!(has_capability('mod/quiz:viewreports', $context) ||
01382             has_capability('mod/quiz:grade', $context))) {
01383         return false;
01384     }
01385 
01386     return array($question, $state, array());
01387 }
01388 
01394 function quiz_print_overview($courses, &$htmlarray) {
01395     global $USER, $CFG;
01396     // These next 6 Lines are constant in all modules (just change module name)
01397     if (empty($courses) || !is_array($courses) || count($courses) == 0) {
01398         return array();
01399     }
01400 
01401     if (!$quizzes = get_all_instances_in_courses('quiz', $courses)) {
01402         return;
01403     }
01404 
01405     // Fetch some language strings outside the main loop.
01406     $strquiz = get_string('modulename', 'quiz');
01407     $strnoattempts = get_string('noattempts', 'quiz');
01408 
01409     // We want to list quizzes that are currently available, and which have a close date.
01410     // This is the same as what the lesson does, and the dabate is in MDL-10568.
01411     $now = time();
01412     foreach ($quizzes as $quiz) {
01413         if ($quiz->timeclose >= $now && $quiz->timeopen < $now) {
01414             // Give a link to the quiz, and the deadline.
01415             $str = '<div class="quiz overview">' .
01416                     '<div class="name">' . $strquiz . ': <a ' .
01417                     ($quiz->visible ? '' : ' class="dimmed"') .
01418                     ' href="' . $CFG->wwwroot . '/mod/quiz/view.php?id=' .
01419                     $quiz->coursemodule . '">' .
01420                     $quiz->name . '</a></div>';
01421             $str .= '<div class="info">' . get_string('quizcloseson', 'quiz',
01422                     userdate($quiz->timeclose)) . '</div>';
01423 
01424             // Now provide more information depending on the uers's role.
01425             $context = get_context_instance(CONTEXT_MODULE, $quiz->coursemodule);
01426             if (has_capability('mod/quiz:viewreports', $context)) {
01427                 // For teacher-like people, show a summary of the number of student attempts.
01428                 // The $quiz objects returned by get_all_instances_in_course have the necessary $cm
01429                 // fields set to make the following call work.
01430                 $str .= '<div class="info">' .
01431                         quiz_num_attempt_summary($quiz, $quiz, true) . '</div>';
01432             } else if (has_any_capability(array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'),
01433                     $context)) { // Student
01434                 // For student-like people, tell them how many attempts they have made.
01435                 if (isset($USER->id) &&
01436                         ($attempts = quiz_get_user_attempts($quiz->id, $USER->id))) {
01437                     $numattempts = count($attempts);
01438                     $str .= '<div class="info">' .
01439                             get_string('numattemptsmade', 'quiz', $numattempts) . '</div>';
01440                 } else {
01441                     $str .= '<div class="info">' . $strnoattempts . '</div>';
01442                 }
01443             } else {
01444                 // For ayone else, there is no point listing this quiz, so stop processing.
01445                 continue;
01446             }
01447 
01448             // Add the output for this quiz to the rest.
01449             $str .= '</div>';
01450             if (empty($htmlarray[$quiz->course]['quiz'])) {
01451                 $htmlarray[$quiz->course]['quiz'] = $str;
01452             } else {
01453                 $htmlarray[$quiz->course]['quiz'] .= $str;
01454             }
01455         }
01456     }
01457 }
01458 
01473 function quiz_num_attempt_summary($quiz, $cm, $returnzero = false, $currentgroup = 0) {
01474     global $DB, $USER;
01475     $numattempts = $DB->count_records('quiz_attempts', array('quiz'=> $quiz->id, 'preview'=>0));
01476     if ($numattempts || $returnzero) {
01477         if (groups_get_activity_groupmode($cm)) {
01478             $a = new stdClass();
01479             $a->total = $numattempts;
01480             if ($currentgroup) {
01481                 $a->group = $DB->count_records_sql('SELECT COUNT(DISTINCT qa.id) FROM ' .
01482                         '{quiz_attempts} qa JOIN ' .
01483                         '{groups_members} gm ON qa.userid = gm.userid ' .
01484                         'WHERE quiz = ? AND preview = 0 AND groupid = ?',
01485                         array($quiz->id, $currentgroup));
01486                 return get_string('attemptsnumthisgroup', 'quiz', $a);
01487             } else if ($groups = groups_get_all_groups($cm->course, $USER->id, $cm->groupingid)) {
01488                 list($usql, $params) = $DB->get_in_or_equal(array_keys($groups));
01489                 $a->group = $DB->count_records_sql('SELECT COUNT(DISTINCT qa.id) FROM ' .
01490                         '{quiz_attempts} qa JOIN ' .
01491                         '{groups_members} gm ON qa.userid = gm.userid ' .
01492                         'WHERE quiz = ? AND preview = 0 AND ' .
01493                         "groupid $usql", array_merge(array($quiz->id), $params));
01494                 return get_string('attemptsnumyourgroups', 'quiz', $a);
01495             }
01496         }
01497         return get_string('attemptsnum', 'quiz', $numattempts);
01498     }
01499     return '';
01500 }
01501 
01516 function quiz_attempt_summary_link_to_reports($quiz, $cm, $context, $returnzero = false,
01517         $currentgroup = 0) {
01518     global $CFG;
01519     $summary = quiz_num_attempt_summary($quiz, $cm, $returnzero, $currentgroup);
01520     if (!$summary) {
01521         return '';
01522     }
01523 
01524     require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php');
01525     $url = new moodle_url('/mod/quiz/report.php', array(
01526             'id' => $cm->id, 'mode' => quiz_report_default_report($context)));
01527     return html_writer::link($url, $summary);
01528 }
01529 
01534 function quiz_supports($feature) {
01535     switch($feature) {
01536         case FEATURE_GROUPS:                  return true;
01537         case FEATURE_GROUPINGS:               return true;
01538         case FEATURE_GROUPMEMBERSONLY:        return true;
01539         case FEATURE_MOD_INTRO:               return true;
01540         case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
01541         case FEATURE_GRADE_HAS_GRADE:         return true;
01542         case FEATURE_GRADE_OUTCOMES:          return false;
01543         case FEATURE_BACKUP_MOODLE2:          return true;
01544         case FEATURE_SHOW_DESCRIPTION:        return true;
01545 
01546         default: return null;
01547     }
01548 }
01549 
01553 function quiz_get_extra_capabilities() {
01554     global $CFG;
01555     require_once($CFG->libdir.'/questionlib.php');
01556     $caps = question_get_all_capabilities();
01557     $caps[] = 'moodle/site:accessallgroups';
01558     return $caps;
01559 }
01560 
01572 function quiz_extend_navigation($quiznode, $course, $module, $cm) {
01573     global $CFG;
01574 
01575     $context = get_context_instance(CONTEXT_MODULE, $cm->id);
01576 
01577     if (has_capability('mod/quiz:view', $context)) {
01578         $url = new moodle_url('/mod/quiz/view.php', array('id'=>$cm->id));
01579         $quiznode->add(get_string('info', 'quiz'), $url, navigation_node::TYPE_SETTING,
01580                 null, null, new pix_icon('i/info', ''));
01581     }
01582 
01583     if (has_any_capability(array('mod/quiz:viewreports', 'mod/quiz:grade'), $context)) {
01584         require_once($CFG->dirroot.'/mod/quiz/report/reportlib.php');
01585         $reportlist = quiz_report_list($context);
01586 
01587         $url = new moodle_url('/mod/quiz/report.php',
01588                 array('id' => $cm->id, 'mode' => reset($reportlist)));
01589         $reportnode = $quiznode->add(get_string('results', 'quiz'), $url,
01590                 navigation_node::TYPE_SETTING,
01591                 null, null, new pix_icon('i/report', ''));
01592 
01593         foreach ($reportlist as $report) {
01594             $url = new moodle_url('/mod/quiz/report.php',
01595                     array('id' => $cm->id, 'mode' => $report));
01596             $reportnode->add(get_string($report, 'quiz_'.$report), $url,
01597                     navigation_node::TYPE_SETTING,
01598                     null, 'quiz_report_' . $report, new pix_icon('i/item', ''));
01599         }
01600     }
01601 }
01602 
01612 function quiz_extend_settings_navigation($settings, $quiznode) {
01613     global $PAGE, $CFG;
01614 
01619     require_once($CFG->libdir . '/questionlib.php');
01620 
01621     // We want to add these new nodes after the Edit settings node, and before the
01622     // Locally assigned roles node. Of course, both of those are controlled by capabilities.
01623     $keys = $quiznode->get_children_key_list();
01624     $beforekey = null;
01625     $i = array_search('modedit', $keys);
01626     if ($i === false and array_key_exists(0, $keys)) {
01627         $beforekey = $keys[0];
01628     } else if (array_key_exists($i + 1, $keys)) {
01629         $beforekey = $keys[$i + 1];
01630     }
01631 
01632     if (has_capability('mod/quiz:manageoverrides', $PAGE->cm->context)) {
01633         $url = new moodle_url('/mod/quiz/overrides.php', array('cmid'=>$PAGE->cm->id));
01634         $node = navigation_node::create(get_string('groupoverrides', 'quiz'),
01635                 new moodle_url($url, array('mode'=>'group')),
01636                 navigation_node::TYPE_SETTING, null, 'mod_quiz_groupoverrides');
01637         $quiznode->add_node($node, $beforekey);
01638 
01639         $node = navigation_node::create(get_string('useroverrides', 'quiz'),
01640                 new moodle_url($url, array('mode'=>'user')),
01641                 navigation_node::TYPE_SETTING, null, 'mod_quiz_useroverrides');
01642         $quiznode->add_node($node, $beforekey);
01643     }
01644 
01645     if (has_capability('mod/quiz:manage', $PAGE->cm->context)) {
01646         $node = navigation_node::create(get_string('editquiz', 'quiz'),
01647                 new moodle_url('/mod/quiz/edit.php', array('cmid'=>$PAGE->cm->id)),
01648                 navigation_node::TYPE_SETTING, null, 'mod_quiz_edit',
01649                 new pix_icon('t/edit', ''));
01650         $quiznode->add_node($node, $beforekey);
01651     }
01652 
01653     if (has_capability('mod/quiz:preview', $PAGE->cm->context)) {
01654         $url = new moodle_url('/mod/quiz/startattempt.php',
01655                 array('cmid'=>$PAGE->cm->id, 'sesskey'=>sesskey()));
01656         $node = navigation_node::create(get_string('preview', 'quiz'), $url,
01657                 navigation_node::TYPE_SETTING, null, 'mod_quiz_preview',
01658                 new pix_icon('t/preview', ''));
01659         $quiznode->add_node($node, $beforekey);
01660     }
01661 
01662     question_extend_settings_navigation($quiznode, $PAGE->cm->context)->trim_if_empty();
01663 }
01664 
01676 function quiz_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
01677     global $CFG, $DB;
01678 
01679     if ($context->contextlevel != CONTEXT_MODULE) {
01680         return false;
01681     }
01682 
01683     require_login($course, false, $cm);
01684 
01685     if (!$quiz = $DB->get_record('quiz', array('id'=>$cm->instance))) {
01686         return false;
01687     }
01688 
01689     // 'intro' area is served by pluginfile.php
01690     $fileareas = array('feedback');
01691     if (!in_array($filearea, $fileareas)) {
01692         return false;
01693     }
01694 
01695     $feedbackid = (int)array_shift($args);
01696     if (!$feedback = $DB->get_record('quiz_feedback', array('id'=>$feedbackid))) {
01697         return false;
01698     }
01699 
01700     $fs = get_file_storage();
01701     $relativepath = implode('/', $args);
01702     $fullpath = "/$context->id/mod_quiz/$filearea/$feedbackid/$relativepath";
01703     if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
01704         return false;
01705     }
01706     send_stored_file($file, 0, 0, true);
01707 }
01708 
01721 function mod_quiz_question_pluginfile($course, $context, $component,
01722         $filearea, $qubaid, $slot, $args, $forcedownload) {
01723     global $CFG;
01724     require_once($CFG->dirroot . '/mod/quiz/locallib.php');
01725 
01726     $attemptobj = quiz_attempt::create_from_usage_id($qubaid);
01727     require_login($attemptobj->get_courseid(), false, $attemptobj->get_cm());
01728 
01729     if ($attemptobj->is_own_attempt() && !$attemptobj->is_finished()) {
01730         // In the middle of an attempt.
01731         if (!$attemptobj->is_preview_user()) {
01732             $attemptobj->require_capability('mod/quiz:attempt');
01733         }
01734         $isreviewing = false;
01735 
01736     } else {
01737         // Reviewing an attempt.
01738         $attemptobj->check_review_capability();
01739         $isreviewing = true;
01740     }
01741 
01742     if (!$attemptobj->check_file_access($slot, $isreviewing, $context->id,
01743             $component, $filearea, $args, $forcedownload)) {
01744         send_file_not_found();
01745     }
01746 
01747     $fs = get_file_storage();
01748     $relativepath = implode('/', $args);
01749     $fullpath = "/$context->id/$component/$filearea/$relativepath";
01750     if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
01751         send_file_not_found();
01752     }
01753 
01754     send_stored_file($file, 0, 0, $forcedownload);
01755 }
01756 
01763 function quiz_page_type_list($pagetype, $parentcontext, $currentcontext) {
01764     $module_pagetype = array(
01765         'mod-quiz-*'=>get_string('page-mod-quiz-x', 'quiz'),
01766         'mod-quiz-edit'=>get_string('page-mod-quiz-edit', 'quiz'));
01767     return $module_pagetype;
01768 }
 All Data Structures Namespaces Files Functions Variables Enumerations