Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/question/engine/questionattempt.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 
00027 defined('MOODLE_INTERNAL') || die();
00028 
00029 
00044 class question_attempt {
00049     const USE_RAW_DATA = 'use raw data';
00050 
00055     const PARAM_MARK = 'parammark';
00056 
00061     const PARAM_FILES = 'paramfiles';
00062 
00067     const PARAM_CLEANHTML_FILES = 'paramcleanhtmlfiles';
00068 
00070     protected $id = null;
00071 
00073     protected $usageid;
00074 
00076     protected $slot = null;
00077 
00082     protected $behaviour = null;
00083 
00085     protected $question;
00086 
00088     protected $variant;
00089 
00091     protected $maxmark;
00092 
00097     protected $minfraction = null;
00098 
00103     protected $questionsummary = null;
00104 
00109     protected $responsesummary = null;
00110 
00116     protected $rightanswer = null;
00117 
00119     protected $steps = array();
00120 
00122     protected $flagged = false;
00123 
00125     protected $observer;
00126 
00131     const KEEP = true;
00132     const DISCARD = false;
00148     public function __construct(question_definition $question, $usageid,
00149             question_usage_observer $observer = null, $maxmark = null) {
00150         $this->question = $question;
00151         $this->usageid = $usageid;
00152         if (is_null($observer)) {
00153             $observer = new question_usage_null_observer();
00154         }
00155         $this->observer = $observer;
00156         if (!is_null($maxmark)) {
00157             $this->maxmark = $maxmark;
00158         } else {
00159             $this->maxmark = $question->defaultmark;
00160         }
00161     }
00162 
00168     public function get_full_qa() {
00169         return $this;
00170     }
00171 
00173     public function get_question() {
00174         return $this->question;
00175     }
00176 
00181     public function get_variant() {
00182         return $this->variant;
00183     }
00184 
00190     public function set_slot($slot) {
00191         $this->slot = $slot;
00192     }
00193 
00195     public function get_slot() {
00196         return $this->slot;
00197     }
00198 
00203     public function get_database_id() {
00204         return $this->id;
00205     }
00206 
00212     public function set_database_id($id) {
00213         $this->id = $id;
00214     }
00215 
00221     public function set_observer($observer) {
00222         $this->observer = $observer;
00223     }
00224 
00226     public function get_usage_id() {
00227         return $this->usageid;
00228     }
00229 
00235     public function set_usage_id($usageid) {
00236         $this->usageid = $usageid;
00237     }
00238 
00240     public function get_behaviour_name() {
00241         return $this->behaviour->get_name();
00242     }
00243 
00248     public function get_behaviour() {
00249         return $this->behaviour;
00250     }
00251 
00256     public function set_flagged($flagged) {
00257         $this->flagged = $flagged;
00258         $this->observer->notify_attempt_modified($this);
00259     }
00260 
00262     public function is_flagged() {
00263         return $this->flagged;
00264     }
00265 
00272     public function get_flag_field_name() {
00273         return $this->get_control_field_name('flagged');
00274     }
00275 
00286     public function get_qt_field_name($varname) {
00287         return $this->get_field_prefix() . $varname;
00288     }
00289 
00300     public function get_behaviour_field_name($varname) {
00301         return $this->get_field_prefix() . '-' . $varname;
00302     }
00303 
00313     public function get_control_field_name($varname) {
00314         return $this->get_field_prefix() . ':' . $varname;
00315     }
00316 
00327     public function get_field_prefix() {
00328         return 'q' . $this->usageid . ':' . $this->slot . '_';
00329     }
00330 
00337     public function get_step($i) {
00338         if ($i < 0 || $i >= count($this->steps)) {
00339             throw new coding_exception('Index out of bounds in question_attempt::get_step.');
00340         }
00341         return $this->steps[$i];
00342     }
00343 
00349     public function get_num_steps() {
00350         return count($this->steps);
00351     }
00352 
00358     public function get_last_step() {
00359         if (count($this->steps) == 0) {
00360             return new question_null_step();
00361         }
00362         return end($this->steps);
00363     }
00364 
00369     public function get_step_iterator() {
00370         return new question_attempt_step_iterator($this);
00371     }
00372 
00381     public function get_full_step_iterator() {
00382         return $this->get_step_iterator();
00383     }
00384 
00389     public function get_reverse_step_iterator() {
00390         return new question_attempt_reverse_step_iterator($this);
00391     }
00392 
00402     public function get_last_qt_data($default = array()) {
00403         foreach ($this->get_reverse_step_iterator() as $step) {
00404             $response = $step->get_qt_data();
00405             if (!empty($response)) {
00406                 return $response;
00407             }
00408         }
00409         return $default;
00410     }
00411 
00418     public function get_last_step_with_qt_var($name) {
00419         foreach ($this->get_reverse_step_iterator() as $step) {
00420             if ($step->has_qt_var($name)) {
00421                 return $step;
00422             }
00423         }
00424         return new question_attempt_step_read_only();
00425     }
00426 
00433     public function get_last_step_with_behaviour_var($name) {
00434         foreach ($this->get_reverse_step_iterator() as $step) {
00435             if ($step->has_behaviour_var($name)) {
00436                 return $step;
00437             }
00438         }
00439         return new question_attempt_step_read_only();
00440     }
00441 
00452     public function get_last_qt_var($name, $default = null) {
00453         $step = $this->get_last_step_with_qt_var($name);
00454         if ($step->has_qt_var($name)) {
00455             return $step->get_qt_var($name);
00456         } else {
00457             return $default;
00458         }
00459     }
00460 
00468     public function get_last_qt_files($name, $contextid) {
00469         foreach ($this->get_reverse_step_iterator() as $step) {
00470             if ($step->has_qt_var($name)) {
00471                 return $step->get_qt_files($name, $contextid);
00472             }
00473         }
00474         return array();
00475     }
00476 
00483     public function get_response_file_url(stored_file $file) {
00484         return file_encode_url(new moodle_url('/pluginfile.php'), '/' . implode('/', array(
00485                 $file->get_contextid(),
00486                 $file->get_component(),
00487                 $file->get_filearea(),
00488                 $this->usageid,
00489                 $this->slot,
00490                 $file->get_itemid())) .
00491                 $file->get_filepath() . $file->get_filename(), true);
00492     }
00493 
00503     public function prepare_response_files_draft_itemid($name, $contextid) {
00504         foreach ($this->get_reverse_step_iterator() as $step) {
00505             if ($step->has_qt_var($name)) {
00506                 return $step->prepare_response_files_draft_itemid($name, $contextid);
00507             }
00508         }
00509 
00510         // No files yet.
00511         $draftid = 0; // Will be filled in by file_prepare_draft_area.
00512         file_prepare_draft_area($draftid, $contextid, 'question', 'response_' . $name, null);
00513         return $draftid;
00514     }
00515 
00526     public function get_last_behaviour_var($name, $default = null) {
00527         foreach ($this->get_reverse_step_iterator() as $step) {
00528             if ($step->has_behaviour_var($name)) {
00529                 return $step->get_behaviour_var($name);
00530             }
00531         }
00532         return $default;
00533     }
00534 
00540     public function get_state() {
00541         return $this->get_last_step()->get_state();
00542     }
00543 
00549     public function get_state_string($showcorrectness) {
00550         return $this->behaviour->get_state_string($showcorrectness);
00551     }
00552 
00558     public function get_state_class($showcorrectness) {
00559         return $this->get_state()->get_state_class($showcorrectness);
00560     }
00561 
00565     public function get_last_action_time() {
00566         return $this->get_last_step()->get_timecreated();
00567     }
00568 
00574     public function get_fraction() {
00575         return $this->get_last_step()->get_fraction();
00576     }
00577 
00579     public function has_marks() {
00580         // Since grades are stored in the database as NUMBER(12,7).
00581         return $this->maxmark >= 0.00000005;
00582     }
00583 
00588     public function get_mark() {
00589         return $this->fraction_to_mark($this->get_fraction());
00590     }
00591 
00599     public function get_current_manual_mark() {
00600         $mark = $this->get_submitted_var($this->get_behaviour_field_name('mark'), question_attempt::PARAM_MARK);
00601         if (is_null($mark)) {
00602             return $this->get_mark();
00603         } else {
00604             return $mark;
00605         }
00606     }
00607 
00612     public function fraction_to_mark($fraction) {
00613         if (is_null($fraction)) {
00614             return null;
00615         }
00616         return $fraction * $this->maxmark;
00617     }
00618 
00620     public function get_max_mark() {
00621         return $this->maxmark;
00622     }
00623 
00625     public function get_min_fraction() {
00626         if (is_null($this->minfraction)) {
00627             throw new coding_exception('This question_attempt has not been started yet, the min fraction is not yet konwn.');
00628         }
00629         return $this->minfraction;
00630     }
00631 
00638     public function format_mark($dp) {
00639         return $this->format_fraction_as_mark($this->get_fraction(), $dp);
00640     }
00641 
00648     public function format_fraction_as_mark($fraction, $dp) {
00649         return format_float($this->fraction_to_mark($fraction), $dp);
00650     }
00651 
00659     public function format_max_mark($dp) {
00660         return format_float($this->maxmark, $dp);
00661     }
00662 
00667     public function get_applicable_hint() {
00668         return $this->behaviour->get_applicable_hint();
00669     }
00670 
00676     public function summarise_action(question_attempt_step $step) {
00677         return $this->behaviour->summarise_action($step);
00678     }
00679 
00685     protected function extra_file_path_components() {
00686         return array($this->get_usage_id(), $this->get_slot());
00687     }
00688 
00698     public function rewrite_pluginfile_urls($text, $component, $filearea, $itemid) {
00699         return question_rewrite_question_urls($text, 'pluginfile.php',
00700                 $this->question->contextid, $component, $filearea,
00701                 $this->extra_file_path_components(), $itemid);
00702     }
00703 
00714     public function rewrite_response_pluginfile_urls($text, $contextid, $name,
00715             question_attempt_step $step) {
00716         return $step->rewrite_response_pluginfile_urls($text, $contextid, $name,
00717                 $this->extra_file_path_components());
00718     }
00719 
00728     public function render($options, $number, $page = null) {
00729         if (is_null($page)) {
00730             global $PAGE;
00731             $page = $PAGE;
00732         }
00733         $qoutput = $page->get_renderer('core', 'question');
00734         $qtoutput = $this->question->get_renderer($page);
00735         return $this->behaviour->render($options, $number, $qoutput, $qtoutput);
00736     }
00737 
00743     public function render_head_html($page = null) {
00744         if (is_null($page)) {
00745             global $PAGE;
00746             $page = $PAGE;
00747         }
00748         // TODO go via behaviour.
00749         return $this->question->get_renderer($page)->head_code($this) .
00750                 $this->behaviour->get_renderer($page)->head_code($this);
00751     }
00752 
00763     public function render_at_step($seq, $options, $number, $preferredbehaviour) {
00764         $restrictedqa = new question_attempt_with_restricted_history($this, $seq, $preferredbehaviour);
00765         return $restrictedqa->render($options, $number);
00766     }
00767 
00777     public function check_file_access($options, $component, $filearea, $args, $forcedownload) {
00778         return $this->behaviour->check_file_access($options, $component, $filearea, $args, $forcedownload);
00779     }
00780 
00785     protected function add_step(question_attempt_step $step) {
00786         $this->steps[] = $step;
00787         end($this->steps);
00788         $this->observer->notify_step_added($step, $this, key($this->steps));
00789     }
00790 
00796     public function select_variant(question_variant_selection_strategy $variantstrategy) {
00797         return $variantstrategy->choose_variant($this->get_question()->get_num_variants(),
00798                 $this->get_question()->get_variants_selection_seed());
00799     }
00800 
00817     public function start($preferredbehaviour, $variant, $submitteddata = array(),
00818             $timestamp = null, $userid = null, $existingstepid = null) {
00819 
00820         // Initialise the behaviour.
00821         $this->variant = $variant;
00822         if (is_string($preferredbehaviour)) {
00823             $this->behaviour =
00824                     $this->question->make_behaviour($this, $preferredbehaviour);
00825         } else {
00826             $class = get_class($preferredbehaviour);
00827             $this->behaviour = new $class($this, $preferredbehaviour);
00828         }
00829 
00830         // Record the minimum fraction.
00831         $this->minfraction = $this->behaviour->get_min_fraction();
00832 
00833         // Initialise the first step.
00834         $firststep = new question_attempt_step($submitteddata, $timestamp, $userid, $existingstepid);
00835         $firststep->set_state(question_state::$todo);
00836         if ($submitteddata) {
00837             $this->question->apply_attempt_state($firststep);
00838         } else {
00839             $this->behaviour->init_first_step($firststep, $variant);
00840         }
00841         $this->add_step($firststep);
00842 
00843         // Record questionline and correct answer.
00844         $this->questionsummary = $this->behaviour->get_question_summary();
00845         $this->rightanswer = $this->behaviour->get_right_answer_summary();
00846     }
00847 
00858     public function start_based_on(question_attempt $oldqa) {
00859         $this->start($oldqa->behaviour, $oldqa->get_variant(), $oldqa->get_resume_data());
00860     }
00861 
00867     protected function get_resume_data() {
00868         return $this->behaviour->get_resume_data();
00869     }
00870 
00882     public function get_submitted_var($name, $type, $postdata = null) {
00883         switch ($type) {
00884             case self::PARAM_MARK:
00885                 // Special case to work around PARAM_NUMBER converting '' to 0.
00886                 $mark = $this->get_submitted_var($name, PARAM_RAW_TRIMMED, $postdata);
00887                 if ($mark === '') {
00888                     return $mark;
00889                 } else {
00890                     return $this->get_submitted_var($name, PARAM_NUMBER, $postdata);
00891                 }
00892 
00893             case self::PARAM_FILES:
00894                 return $this->process_response_files($name, $name, $postdata);
00895 
00896             case self::PARAM_CLEANHTML_FILES:
00897                 $var = $this->get_submitted_var($name, PARAM_CLEANHTML, $postdata);
00898                 return $this->process_response_files($name, $name . ':itemid', $postdata, $var);
00899 
00900             default:
00901                 if (is_null($postdata)) {
00902                     $var = optional_param($name, null, $type);
00903                 } else if (array_key_exists($name, $postdata)) {
00904                     $var = clean_param($postdata[$name], $type);
00905                 } else {
00906                     $var = null;
00907                 }
00908 
00909                 return $var;
00910         }
00911     }
00912 
00923     protected function process_response_files($name, $draftidname, $postdata = null, $text = null) {
00924         if ($postdata) {
00925             // There can be no files with test data (at the moment).
00926             return null;
00927         }
00928 
00929         $draftitemid = file_get_submitted_draft_itemid($draftidname);
00930         if (!$draftitemid) {
00931             return null;
00932         }
00933 
00934         return new question_file_saver($draftitemid, 'question', 'response_' .
00935                 str_replace($this->get_field_prefix(), '', $name), $text);
00936     }
00937 
00944     protected function get_expected_data($expected, $postdata, $extraprefix) {
00945         $submitteddata = array();
00946         foreach ($expected as $name => $type) {
00947             $value = $this->get_submitted_var(
00948                     $this->get_field_prefix() . $extraprefix . $name, $type, $postdata);
00949             if (!is_null($value)) {
00950                 $submitteddata[$extraprefix . $name] = $value;
00951             }
00952         }
00953         return $submitteddata;
00954     }
00955 
00961     protected function get_all_submitted_qt_vars($postdata) {
00962         if (is_null($postdata)) {
00963             $postdata = $_POST;
00964         }
00965 
00966         $pattern = '/^' . preg_quote($this->get_field_prefix()) . '[^-:]/';
00967         $prefixlen = strlen($this->get_field_prefix());
00968 
00969         $submitteddata = array();
00970         foreach ($_POST as $name => $value) {
00971             if (preg_match($pattern, $name)) {
00972                 $submitteddata[substr($name, $prefixlen)] = $value;
00973             }
00974         }
00975 
00976         return $submitteddata;
00977     }
00978 
00986     public function get_submitted_data($postdata = null) {
00987         $submitteddata = $this->get_expected_data(
00988                 $this->behaviour->get_expected_data(), $postdata, '-');
00989 
00990         $expected = $this->behaviour->get_expected_qt_data();
00991         if ($expected === self::USE_RAW_DATA) {
00992             $submitteddata += $this->get_all_submitted_qt_vars($postdata);
00993         } else {
00994             $submitteddata += $this->get_expected_data($expected, $postdata, '');
00995         }
00996         return $submitteddata;
00997     }
00998 
01005     public function get_correct_response() {
01006         $response = $this->question->get_correct_response();
01007         if (is_null($response)) {
01008             return null;
01009         }
01010         $imvars = $this->behaviour->get_correct_response();
01011         foreach ($imvars as $name => $value) {
01012             $response['-' . $name] = $value;
01013         }
01014         return $response;
01015     }
01016 
01023     public function set_question_summary($questionsummary) {
01024         $this->questionsummary = $questionsummary;
01025         $this->observer->notify_attempt_modified($this);
01026     }
01027 
01031     public function get_question_summary() {
01032         return $this->questionsummary;
01033     }
01034 
01038     public function get_response_summary() {
01039         return $this->responsesummary;
01040     }
01041 
01045     public function get_right_answer_summary() {
01046         return $this->rightanswer;
01047     }
01048 
01055     public function process_action($submitteddata, $timestamp = null, $userid = null, $existingstepid = null) {
01056         $pendingstep = new question_attempt_pending_step($submitteddata, $timestamp, $userid, $existingstepid);
01057         if ($this->behaviour->process_action($pendingstep) == self::KEEP) {
01058             $this->add_step($pendingstep);
01059             if ($pendingstep->response_summary_changed()) {
01060                 $this->responsesummary = $pendingstep->get_new_response_summary();
01061             }
01062         }
01063     }
01064 
01074     public function finish($timestamp = null, $userid = null) {
01075         $this->process_action(array('-finish' => 1), $timestamp, $userid);
01076     }
01077 
01085     public function regrade(question_attempt $oldqa, $finished) {
01086         $first = true;
01087         foreach ($oldqa->get_step_iterator() as $step) {
01088             $this->observer->notify_step_deleted($step, $this);
01089             if ($first) {
01090                 $first = false;
01091                 $this->start($oldqa->behaviour, $oldqa->get_variant(), $step->get_all_data(),
01092                         $step->get_timecreated(), $step->get_user_id(), $step->get_id());
01093             } else {
01094                 $this->process_action($step->get_submitted_data(),
01095                         $step->get_timecreated(), $step->get_user_id(), $step->get_id());
01096             }
01097         }
01098         if ($finished) {
01099             $this->finish();
01100         }
01101     }
01102 
01111     public function manual_grade($comment, $mark, $timestamp = null, $userid = null) {
01112         $submitteddata = array('-comment' => $comment);
01113         if (!is_null($mark)) {
01114             $submitteddata['-mark'] = $mark;
01115             $submitteddata['-maxmark'] = $this->maxmark;
01116         }
01117         $this->process_action($submitteddata, $timestamp, $userid);
01118     }
01119 
01121     public function has_manual_comment() {
01122         foreach ($this->steps as $step) {
01123             if ($step->has_behaviour_var('comment')) {
01124                 return true;
01125             }
01126         }
01127         return false;
01128     }
01129 
01134     public function get_manual_comment() {
01135         foreach ($this->get_reverse_step_iterator() as $step) {
01136             if ($step->has_behaviour_var('comment')) {
01137                 return array($step->get_behaviour_var('comment'),
01138                         $step->get_behaviour_var('commentformat'));
01139             }
01140         }
01141         return array(null, null);
01142     }
01143 
01151     public function classify_response() {
01152         return $this->behaviour->classify_response();
01153     }
01154 
01164     public static function load_from_records($records, $questionattemptid,
01165             question_usage_observer $observer, $preferredbehaviour) {
01166         $record = $records->current();
01167         while ($record->questionattemptid != $questionattemptid) {
01168             $record = $records->next();
01169             if (!$records->valid()) {
01170                 throw new coding_exception("Question attempt $questionattemptid not found in the database.");
01171             }
01172             $record = $records->current();
01173         }
01174 
01175         try {
01176             $question = question_bank::load_question($record->questionid);
01177         } catch (Exception $e) {
01178             // The question must have been deleted somehow. Create a missing
01179             // question to use in its place.
01180             $question = question_bank::get_qtype('missingtype')->make_deleted_instance(
01181                     $record->questionid, $record->maxmark + 0);
01182         }
01183 
01184         $qa = new question_attempt($question, $record->questionusageid,
01185                 null, $record->maxmark + 0);
01186         $qa->set_database_id($record->questionattemptid);
01187         $qa->set_slot($record->slot);
01188         $qa->variant = $record->variant + 0;
01189         $qa->minfraction = $record->minfraction + 0;
01190         $qa->set_flagged($record->flagged);
01191         $qa->questionsummary = $record->questionsummary;
01192         $qa->rightanswer = $record->rightanswer;
01193         $qa->responsesummary = $record->responsesummary;
01194         $qa->timemodified = $record->timemodified;
01195 
01196         $qa->behaviour = question_engine::make_behaviour(
01197                 $record->behaviour, $qa, $preferredbehaviour);
01198 
01199         $i = 0;
01200         while ($record && $record->questionattemptid == $questionattemptid && !is_null($record->attemptstepid)) {
01201             $qa->steps[$i] = question_attempt_step::load_from_records($records, $record->attemptstepid);
01202             if ($i == 0) {
01203                 $question->apply_attempt_state($qa->steps[0]);
01204             }
01205             $i++;
01206             if ($records->valid()) {
01207                 $record = $records->current();
01208             } else {
01209                 $record = false;
01210             }
01211         }
01212 
01213         $qa->observer = $observer;
01214 
01215         return $qa;
01216     }
01217 }
01218 
01219 
01229 class question_attempt_with_restricted_history extends question_attempt {
01233     protected $baseqa;
01234 
01242     public function __construct(question_attempt $baseqa, $lastseq, $preferredbehaviour) {
01243         $this->baseqa = $baseqa->get_full_qa();
01244 
01245         if ($lastseq < 0 || $lastseq >= $this->baseqa->get_num_steps()) {
01246             throw new coding_exception('$lastseq out of range', $lastseq);
01247         }
01248 
01249         $this->steps = array_slice($this->baseqa->steps, 0, $lastseq + 1);
01250         $this->observer = new question_usage_null_observer();
01251 
01252         // This should be a straight copy of all the remaining fields.
01253         $this->id = $this->baseqa->id;
01254         $this->usageid = $this->baseqa->usageid;
01255         $this->slot = $this->baseqa->slot;
01256         $this->question = $this->baseqa->question;
01257         $this->maxmark = $this->baseqa->maxmark;
01258         $this->minfraction = $this->baseqa->minfraction;
01259         $this->questionsummary = $this->baseqa->questionsummary;
01260         $this->responsesummary = $this->baseqa->responsesummary;
01261         $this->rightanswer = $this->baseqa->rightanswer;
01262         $this->flagged = $this->baseqa->flagged;
01263 
01264         // Except behaviour, where we need to create a new one.
01265         $this->behaviour = question_engine::make_behaviour(
01266                 $this->baseqa->get_behaviour_name(), $this, $preferredbehaviour);
01267     }
01268 
01269     public function get_full_qa() {
01270         return $this->baseqa;
01271     }
01272 
01273     public function get_full_step_iterator() {
01274         return $this->baseqa->get_step_iterator();
01275     }
01276 
01277     protected function add_step(question_attempt_step $step) {
01278         coding_exception('Cannot modify a question_attempt_with_restricted_history.');
01279     }
01280     public function process_action($submitteddata, $timestamp = null, $userid = null) {
01281         coding_exception('Cannot modify a question_attempt_with_restricted_history.');
01282     }
01283     public function start($preferredbehaviour, $variant, $submitteddata = array(), $timestamp = null, $userid = null) {
01284         coding_exception('Cannot modify a question_attempt_with_restricted_history.');
01285     }
01286 
01287     public function set_database_id($id) {
01288         coding_exception('Cannot modify a question_attempt_with_restricted_history.');
01289     }
01290     public function set_flagged($flagged) {
01291         coding_exception('Cannot modify a question_attempt_with_restricted_history.');
01292     }
01293     public function set_slot($slot) {
01294         coding_exception('Cannot modify a question_attempt_with_restricted_history.');
01295     }
01296     public function set_question_summary($questionsummary) {
01297         coding_exception('Cannot modify a question_attempt_with_restricted_history.');
01298     }
01299     public function set_usage_id($usageid) {
01300         coding_exception('Cannot modify a question_attempt_with_restricted_history.');
01301     }
01302 }
01303 
01304 
01314 class question_attempt_step_iterator implements Iterator, ArrayAccess {
01316     protected $qa;
01318     protected $i;
01319 
01325     public function __construct(question_attempt $qa) {
01326         $this->qa = $qa;
01327         $this->rewind();
01328     }
01329 
01331     public function current() {
01332         return $this->offsetGet($this->i);
01333     }
01335     public function key() {
01336         return $this->i;
01337     }
01338     public function next() {
01339         ++$this->i;
01340     }
01341     public function rewind() {
01342         $this->i = 0;
01343     }
01345     public function valid() {
01346         return $this->offsetExists($this->i);
01347     }
01348 
01350     public function offsetExists($i) {
01351         return $i >= 0 && $i < $this->qa->get_num_steps();
01352     }
01354     public function offsetGet($i) {
01355         return $this->qa->get_step($i);
01356     }
01357     public function offsetSet($offset, $value) {
01358         throw new coding_exception('You are only allowed read-only access to question_attempt::states through a question_attempt_step_iterator. Cannot set.');
01359     }
01360     public function offsetUnset($offset) {
01361         throw new coding_exception('You are only allowed read-only access to question_attempt::states through a question_attempt_step_iterator. Cannot unset.');
01362     }
01363 }
01364 
01365 
01373 class question_attempt_reverse_step_iterator extends question_attempt_step_iterator {
01374     public function next() {
01375         --$this->i;
01376     }
01377 
01378     public function rewind() {
01379         $this->i = $this->qa->get_num_steps() - 1;
01380     }
01381 }
 All Data Structures Namespaces Files Functions Variables Enumerations