|
Moodle
2.2.1
http://www.collinsharper.com
|
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 require_once(dirname(__FILE__) . '/../../config.php'); 00031 require_once($CFG->dirroot . '/mod/quiz/locallib.php'); 00032 00033 // Get submitted parameters. 00034 $id = required_param('cmid', PARAM_INT); // Course module id 00035 $forcenew = optional_param('forcenew', false, PARAM_BOOL); // Used to force a new preview 00036 $page = optional_param('page', 0, PARAM_INT); // Page to jump to in the attempt. 00037 00038 if (!$cm = get_coursemodule_from_id('quiz', $id)) { 00039 print_error('invalidcoursemodule'); 00040 } 00041 if (!$course = $DB->get_record('course', array('id' => $cm->course))) { 00042 print_error("coursemisconf"); 00043 } 00044 00045 $quizobj = quiz::create($cm->instance, $USER->id); 00046 // This script should only ever be posted to, so set page URL to the view page. 00047 $PAGE->set_url($quizobj->view_url()); 00048 00049 // Check login and sesskey. 00050 require_login($quizobj->get_courseid(), false, $quizobj->get_cm()); 00051 require_sesskey(); 00052 00053 // if no questions have been set up yet redirect to edit.php or display an error. 00054 if (!$quizobj->has_questions()) { 00055 if ($quizobj->has_capability('mod/quiz:manage')) { 00056 redirect($quizobj->edit_url()); 00057 } else { 00058 print_error('cannotstartnoquestions', 'quiz', $quizobj->view_url()); 00059 } 00060 } 00061 00062 // Create an object to manage all the other (non-roles) access rules. 00063 $accessmanager = $quizobj->get_access_manager(time()); 00064 if ($quizobj->is_preview_user() && $forcenew) { 00065 $accessmanager->current_attempt_finished(); 00066 } 00067 00068 // Check capabilities. 00069 if (!$quizobj->is_preview_user()) { 00070 $quizobj->require_capability('mod/quiz:attempt'); 00071 } 00072 00073 // Check to see if a new preview was requested. 00074 if ($quizobj->is_preview_user() && $forcenew) { 00075 // To force the creation of a new preview, we set a finish time on the 00076 // current attempt (if any). It will then automatically be deleted below 00077 $DB->set_field('quiz_attempts', 'timefinish', time(), 00078 array('quiz' => $quizobj->get_quizid(), 'userid' => $USER->id)); 00079 } 00080 00081 // Look for an existing attempt. 00082 $attempts = quiz_get_user_attempts($quizobj->get_quizid(), $USER->id, 'all', true); 00083 $lastattempt = end($attempts); 00084 00085 // If an in-progress attempt exists, check password then redirect to it. 00086 if ($lastattempt && !$lastattempt->timefinish) { 00087 $currentattemptid = $lastattempt->id; 00088 $messages = $accessmanager->prevent_access(); 00089 00090 } else { 00091 // Get number for the next or unfinished attempt 00092 if ($lastattempt && !$lastattempt->preview && !$quizobj->is_preview_user()) { 00093 $attemptnumber = $lastattempt->attempt + 1; 00094 } else { 00095 $lastattempt = false; 00096 $attemptnumber = 1; 00097 } 00098 $currentattemptid = null; 00099 00100 $messages = $accessmanager->prevent_access() + 00101 $accessmanager->prevent_new_attempt(count($attempts), $lastattempt); 00102 } 00103 00104 // Check access. 00105 $output = $PAGE->get_renderer('mod_quiz'); 00106 if (!$quizobj->is_preview_user() && $messages) { 00107 print_error('attempterror', 'quiz', $quizobj->view_url(), 00108 $output->access_messages($messages)); 00109 } 00110 00111 if ($accessmanager->is_preflight_check_required($currentattemptid)) { 00112 // Need to do some checks before allowing the user to continue. 00113 $mform = $accessmanager->get_preflight_check_form( 00114 $quizobj->start_attempt_url($page), $currentattemptid); 00115 00116 if ($mform->is_cancelled()) { 00117 $accessmanager->back_to_view_page($output); 00118 00119 } else if (!$mform->get_data()) { 00120 00121 // Form not submitted successfully, re-display it and stop. 00122 $PAGE->set_url($quizobj->start_attempt_url($page)); 00123 $PAGE->set_title(format_string($quizobj->get_quiz_name())); 00124 $accessmanager->setup_attempt_page($PAGE); 00125 if (empty($quizobj->get_quiz()->showblocks)) { 00126 $PAGE->blocks->show_only_fake_blocks(); 00127 } 00128 00129 echo $output->start_attempt_page($quizobj, $mform); 00130 die(); 00131 } 00132 00133 // Pre-flight check passed. 00134 $accessmanager->notify_preflight_check_passed($currentattemptid); 00135 } 00136 if ($currentattemptid) { 00137 redirect($quizobj->attempt_url($currentattemptid, $page)); 00138 } 00139 00140 // Delete any previous preview attempts belonging to this user. 00141 quiz_delete_previews($quizobj->get_quiz(), $USER->id); 00142 00143 $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context()); 00144 $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour); 00145 00146 // Create the new attempt and initialize the question sessions 00147 $attempt = quiz_create_attempt($quizobj->get_quiz(), $attemptnumber, $lastattempt, time(), 00148 $quizobj->is_preview_user()); 00149 00150 if (!($quizobj->get_quiz()->attemptonlast && $lastattempt)) { 00151 // Starting a normal, new, quiz attempt. 00152 00153 // Fully load all the questions in this quiz. 00154 $quizobj->preload_questions(); 00155 $quizobj->load_questions(); 00156 00157 // Add them all to the $quba. 00158 $idstoslots = array(); 00159 $questionsinuse = array_keys($quizobj->get_questions()); 00160 foreach ($quizobj->get_questions() as $i => $questiondata) { 00161 if ($questiondata->qtype != 'random') { 00162 if (!$quizobj->get_quiz()->shuffleanswers) { 00163 $questiondata->options->shuffleanswers = false; 00164 } 00165 $question = question_bank::make_question($questiondata); 00166 00167 } else { 00168 $question = question_bank::get_qtype('random')->choose_other_question( 00169 $questiondata, $questionsinuse, $quizobj->get_quiz()->shuffleanswers); 00170 if (is_null($question)) { 00171 throw new moodle_exception('notenoughrandomquestions', 'quiz', 00172 $quizobj->view_url(), $questiondata); 00173 } 00174 } 00175 00176 $idstoslots[$i] = $quba->add_question($question, $questiondata->maxmark); 00177 $questionsinuse[] = $question->id; 00178 } 00179 00180 // Start all the questions. 00181 if ($attempt->preview) { 00182 $variantoffset = rand(1, 100); 00183 } else { 00184 $variantoffset = $attemptnumber; 00185 } 00186 $quba->start_all_questions( 00187 new question_variant_pseudorandom_no_repeats_strategy($variantoffset), 00188 time()); 00189 00190 // Update attempt layout. 00191 $newlayout = array(); 00192 foreach (explode(',', $attempt->layout) as $qid) { 00193 if ($qid != 0) { 00194 $newlayout[] = $idstoslots[$qid]; 00195 } else { 00196 $newlayout[] = 0; 00197 } 00198 } 00199 $attempt->layout = implode(',', $newlayout); 00200 00201 } else { 00202 // Starting a subsequent attempt in each attempt builds on last mode. 00203 00204 $oldquba = question_engine::load_questions_usage_by_activity($lastattempt->uniqueid); 00205 00206 $oldnumberstonew = array(); 00207 foreach ($oldquba->get_attempt_iterator() as $oldslot => $oldqa) { 00208 $newslot = $quba->add_question($oldqa->get_question(), $oldqa->get_max_mark()); 00209 00210 $quba->start_question_based_on($newslot, $oldqa); 00211 00212 $oldnumberstonew[$oldslot] = $newslot; 00213 } 00214 00215 // Update attempt layout. 00216 $newlayout = array(); 00217 foreach (explode(',', $lastattempt->layout) as $oldslot) { 00218 if ($oldslot != 0) { 00219 $newlayout[] = $oldnumberstonew[$oldslot]; 00220 } else { 00221 $newlayout[] = 0; 00222 } 00223 } 00224 $attempt->layout = implode(',', $newlayout); 00225 } 00226 00227 // Save the attempt in the database. 00228 $transaction = $DB->start_delegated_transaction(); 00229 question_engine::save_questions_usage_by_activity($quba); 00230 $attempt->uniqueid = $quba->get_id(); 00231 $attempt->id = $DB->insert_record('quiz_attempts', $attempt); 00232 00233 // Log the new attempt. 00234 if ($attempt->preview) { 00235 add_to_log($course->id, 'quiz', 'preview', 'view.php?id=' . $quizobj->get_cmid(), 00236 $quizobj->get_quizid(), $quizobj->get_cmid()); 00237 } else { 00238 add_to_log($course->id, 'quiz', 'attempt', 'review.php?attempt=' . $attempt->id, 00239 $quizobj->get_quizid(), $quizobj->get_cmid()); 00240 } 00241 00242 // Trigger event 00243 $eventdata = new stdClass(); 00244 $eventdata->component = 'mod_quiz'; 00245 $eventdata->attemptid = $attempt->id; 00246 $eventdata->timestart = $attempt->timestart; 00247 $eventdata->userid = $attempt->userid; 00248 $eventdata->quizid = $quizobj->get_quizid(); 00249 $eventdata->cmid = $quizobj->get_cmid(); 00250 $eventdata->courseid = $quizobj->get_courseid(); 00251 events_trigger('quiz_attempt_started', $eventdata); 00252 00253 $transaction->allow_commit(); 00254 00255 // Redirect to the attempt page. 00256 redirect($quizobj->attempt_url($attempt->id, $page));