MDL-20636 It is now possible to save a truefalse question you have created.
[moodle.git] / question / previewlib.php
CommitLineData
fe6ce234
DC
1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
06f8ed54 18
fe6ce234
DC
19/**
20 * Library functions used by question/preview.php.
21 *
06f8ed54 22 * @package moodlecore
fe6ce234
DC
23 * @subpackage questionengine
24 * @copyright 2010 The Open University
25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 */
27
06f8ed54
TH
28
29/**
30 * Settings form for the preview options.
31 *
32 * @copyright 2009 The Open University
33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34 */
35class preview_options_form extends moodleform {
36 public function definition() {
37 $mform = $this->_form;
38
39 $hiddenofvisible = array(
40 question_display_options::HIDDEN => get_string('notshown', 'question'),
41 question_display_options::VISIBLE => get_string('shown', 'question'),
42 );
43
44 $mform->addElement('header', 'optionsheader', get_string('changeoptions', 'question'));
45
46 $behaviours = question_engine::get_behaviour_options($this->_customdata->get_preferred_behaviour());
47 $mform->addElement('select', 'behaviour', get_string('howquestionsbehave', 'question'), $behaviours);
48 $mform->setHelpButton('behaviour', array('howquestionsbehave', get_string('howquestionsbehave', 'question'), 'question'));
49
50 $mform->addElement('text', 'maxmark', get_string('markedoutof', 'question'), array('size' => '5'));
51 $mform->setType('maxmark', PARAM_NUMBER);
52
53 $mform->addElement('select', 'correctness', get_string('whethercorrect', 'question'), $hiddenofvisible);
54
55 $marksoptions = array(
56 question_display_options::HIDDEN => get_string('notshown', 'question'),
57 question_display_options::MAX_ONLY => get_string('showmaxmarkonly', 'question'),
58 question_display_options::MARK_AND_MAX => get_string('showmarkandmax', 'question'),
59 );
60 $mform->addElement('select', 'marks', get_string('marks', 'question'), $marksoptions);
61
62 $mform->addElement('select', 'markdp', get_string('decimalplacesingrades', 'question'),
63 question_engine::get_dp_options());
64
65 $mform->addElement('select', 'feedback', get_string('specificfeedback', 'question'), $hiddenofvisible);
66
67 $mform->addElement('select', 'generalfeedback', get_string('generalfeedback', 'question'), $hiddenofvisible);
68
69 $mform->addElement('select', 'rightanswer', get_string('rightanswer', 'question'), $hiddenofvisible);
70
71 $mform->addElement('select', 'history', get_string('responsehistory', 'question'), $hiddenofvisible);
72
73 $mform->addElement('submit', 'submit', get_string('restartwiththeseoptions', 'question'), $hiddenofvisible);
74 }
75}
76
77
78/**
79 * Displays question preview options as default and set the options
80 * Setting default, getting and setting user preferences in question preview options.
81 *
82 * @copyright 2010 The Open University
83 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
84 */
85class question_preview_options extends question_display_options {
86 /** @var string the behaviour to use for this preview. */
87 public $behaviour;
88
89 /** @var number the maximum mark to use for this preview. */
90 public $maxmark;
91
92 /** @var string prefix to append to field names to get user_preference names. */
93 const OPTIONPREFIX = 'question_preview_options_';
94
95 /**
96 * Constructor.
97 */
98 public function __construct($question) {
99 global $CFG;
100 $this->behaviour = 'deferredfeedback';
101 $this->maxmark = $question->defaultmark;
102 $this->correctness = self::VISIBLE;
103 $this->marks = self::MARK_AND_MAX;
104 $this->markdp = $CFG->quiz_decimalpoints;
105 $this->feedback = self::VISIBLE;
106 $this->numpartscorrect = $this->feedback;
107 $this->generalfeedback = self::VISIBLE;
108 $this->rightanswer = self::VISIBLE;
109 $this->history = self::HIDDEN;
110 $this->flags = self::HIDDEN;
111 $this->manualcomment = self::HIDDEN;
112 }
113
114 /**
115 * @return array names of the options we store in the user preferences table.
116 */
117 protected function get_user_pref_fields() {
118 return array('behaviour', 'correctness', 'marks', 'markdp', 'feedback',
119 'generalfeedback', 'rightanswer', 'history');
120 }
121
122 /**
123 * @return array names and param types of the options we read from the request.
124 */
125 protected function get_field_types() {
126 return array(
127 'behaviour' => PARAM_ALPHA,
128 'maxmark' => PARAM_NUMBER,
129 'correctness' => PARAM_BOOL,
130 'marks' => PARAM_INT,
131 'markdp' => PARAM_INT,
132 'feedback' => PARAM_BOOL,
133 'generalfeedback' => PARAM_BOOL,
134 'rightanswer' => PARAM_BOOL,
135 'history' => PARAM_BOOL,
136 );
137 }
138
139 /**
140 * Load the value of the options from the user_preferences table.
141 */
142 public function load_user_defaults() {
143 foreach ($this->get_user_pref_fields() as $field) {
144 $this->$field = get_user_preferences(
145 self::OPTIONPREFIX . $field, $this->$field);
146 }
147 $this->numpartscorrect = $this->feedback;
148 }
149
150 /**
151 * Save a change to the user's preview options to the database.
152 * @param object $newoptions
153 */
154 public function save_user_preview_options($newoptions) {
155 foreach ($this->get_user_pref_fields() as $field) {
156 if (isset($newoptions->$field)) {
157 set_user_preference(self::OPTIONPREFIX . $field, $newoptions->$field);
158 }
159 }
160 }
161
162 /**
163 * Set the value of any fields included in the request.
164 */
165 public function set_from_request() {
166 foreach ($this->get_field_types() as $field => $type) {
167 $this->$field = optional_param($field, $this->$field, $type);
168 }
169 $this->numpartscorrect = $this->feedback;
170 }
171
172 /**
173 * @return string URL fragment. Parameters needed in the URL when continuing
174 * this preview.
175 */
176 public function get_query_string() {
177 $querystring = array();
178 foreach ($this->get_field_types() as $field => $notused) {
179 if ($field == 'behaviour' || $field == 'maxmark') {
180 continue;
181 }
182 $querystring[] = $field . '=' . $this->$field;
183 }
184 return implode('&', $querystring);
185 }
186}
187
188
fe6ce234
DC
189/**
190 * Called via pluginfile.php -> question_pluginfile to serve files belonging to
191 * a question in a question_attempt when that attempt is a preview.
192 *
193 * @param object $course course settings object
194 * @param object $context context object
195 * @param string $component the name of the component we are serving files for.
196 * @param string $filearea the name of the file area.
197 * @param array $args the remaining bits of the file path.
198 * @param bool $forcedownload whether the user must be forced to download the file.
199 * @return bool false if file not found, does not return if found - justsend the file
200 */
201function question_preview_question_pluginfile($course, $context, $component,
202 $filearea, $attemptid, $questionid, $args, $forcedownload) {
203 global $USER, $SESSION, $DB, $CFG;
204 require_once($CFG->dirroot . '/mod/quiz/locallib.php');
205
206 if (!$question = $DB->get_record('question', array('id' => $questionid))) {
207 return send_file_not_found();
208 }
209
210 if (!question_has_capability_on($question, 'use', $question->category)) {
211 send_file_not_found();
212 }
213
214 if (!isset($SESSION->quizpreview->states) || $SESSION->quizpreview->questionid != $questionid) {
215 send_file_not_found();
216 }
217
218 $states = end($SESSION->quizpreview->states);
219 if (!array_key_exists($question->id, $states)) {
220 send_file_not_found();
221 }
222 $state = $states[$question->id];
223
224 // Build fake cmoptions
225 $quiz = new cmoptions;
226 $quiz->id = 0;
227 $quiz->review = get_config('quiz', 'review');
228 if (empty($course->id)) {
229 $quiz->course = SITEID;
230 } else {
231 $quiz->course = $course->id;
232 }
233 $quiz->decimalpoints = get_config('quiz', 'decimalpoints');
234
235 $questions[$question->id] = $question;
236 get_question_options($questions);
237
238 // Build fake attempt
239 $timenow = time();
6bdfef5d 240 $attempt = new stdClass();
fe6ce234
DC
241 $attempt->quiz = $quiz->id;
242 $attempt->userid = $USER->id;
243 $attempt->attempt = 0;
244 $attempt->sumgrades = 0;
245 $attempt->timestart = $timenow;
246 $attempt->timefinish = 0;
247 $attempt->timemodified = $timenow;
248 $attempt->uniqueid = 0;
249 $attempt->id = 0;
250 $attempt->layout = $question->id;
251
252 $options = quiz_get_renderoptions($quiz, $attempt, $context, $state);
253 $options->noeditlink = true;
06f8ed54 254 // TODO: mulitichoice type needs quiz id to get maxgrade
fe6ce234
DC
255 $options->quizid = 0;
256
257 if (!question_check_file_access($question, $state, $options, $context->id, $component,
258 $filearea, $args, $forcedownload)) {
259 send_file_not_found();
260 }
261
262 $fs = get_file_storage();
263 $relativepath = implode('/', $args);
264 $fullpath = "/$context->id/$component/$filearea/$relativepath";
265 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
266 send_file_not_found();
267 }
268
269 send_stored_file($file, 0, 0, $forcedownload);
270}
06f8ed54
TH
271
272/**
273 * The the URL to use for actions relating to this preview.
274 * @param integer $questionid the question being previewed.
275 * @param integer $qubaid the id of the question usage for this preview.
276 * @param question_preview_options $options the options in use.
277 */
278function question_preview_action_url($questionid, $qubaid,
279 question_preview_options $options) {
280 global $CFG;
281 $url = $CFG->wwwroot . '/question/preview.php?id=' . $questionid . '&previewid=' . $qubaid;
282 return $url . '&' . $options->get_query_string();
283}
284
285/**
286 * Delete the current preview, if any, and redirect to start a new preview.
287 * @param integer $previewid
288 * @param integer $questionid
289 * @param object $displayoptions
290 */
291function restart_preview($previewid, $questionid, $displayoptions) {
292 if ($previewid) {
293 begin_sql();
294 question_engine::delete_questions_usage_by_activity($previewid);
295 commit_sql();
296 }
297 redirect(question_preview_url($questionid, $displayoptions->behaviour, $displayoptions->maxmark, $displayoptions));
298}