Commit | Line | Data |
---|---|---|
83192608 | 1 | <?php |
f9b0500f TH |
2 | // This file is part of Moodle - http://moodle.org/ |
3 | // | |
4 | // Moodle is free software: you can redistribute it and/or modify | |
5 | // it under the terms of the GNU General Public License as published by | |
6 | // the Free Software Foundation, either version 3 of the License, or | |
7 | // (at your option) any later version. | |
8 | // | |
9 | // Moodle is distributed in the hope that it will be useful, | |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | // GNU General Public License for more details. | |
13 | // | |
14 | // You should have received a copy of the GNU General Public License | |
15 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
f4b879dd | 17 | |
ee1fb969 | 18 | /** |
7ca4613b | 19 | * Page to edit quizzes |
20 | * | |
21 | * This page generally has two columns: | |
22 | * The right column lists all available questions in a chosen category and | |
23 | * allows them to be edited or more to be added. This column is only there if | |
24 | * the quiz does not already have student attempts | |
25 | * The left column lists all questions that have been added to the current quiz. | |
26 | * The lecturer can add questions from the right hand list to the quiz or remove them | |
27 | * | |
28 | * The script also processes a number of actions: | |
29 | * Actions affecting a quiz: | |
30 | * up and down Changes the order of questions and page breaks | |
31 | * addquestion Adds a single question to the quiz | |
32 | * add Adds several selected questions to the quiz | |
33 | * addrandom Adds a certain number of random questions to the quiz | |
34 | * repaginate Re-paginates the quiz | |
35 | * delete Removes a question from the quiz | |
36 | * savechanges Saves the order and grades for questions in the quiz | |
37 | * | |
01773a6d | 38 | * @package mod_quiz |
ba643847 TH |
39 | * @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com} |
40 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
f9b0500f TH |
41 | */ |
42 | ||
f4b879dd | 43 | |
324d6576 | 44 | require_once('../../config.php'); |
45 | require_once($CFG->dirroot . '/mod/quiz/editlib.php'); | |
94dbfb3a | 46 | require_once($CFG->dirroot . '/mod/quiz/addrandomform.php'); |
324d6576 | 47 | require_once($CFG->dirroot . '/question/category_class.php'); |
fa583f5f | 48 | |
9e83f3d1 TH |
49 | // These params are only passed from page request to request while we stay on |
50 | // this page otherwise they would go in question_edit_setup. | |
fd214b59 | 51 | $scrollpos = optional_param('scrollpos', '', PARAM_INT); |
56ed242b | 52 | |
fa583f5f | 53 | list($thispageurl, $contexts, $cmid, $cm, $quiz, $pagevars) = |
453b28d8 | 54 | question_edit_setup('editq', '/mod/quiz/edit.php', true); |
fa583f5f | 55 | |
56 | $defaultcategoryobj = question_make_default_categories($contexts->all()); | |
94dbfb3a | 57 | $defaultcategory = $defaultcategoryobj->id . ',' . $defaultcategoryobj->contextid; |
324d6576 | 58 | |
3e10e429 | 59 | $quizhasattempts = quiz_has_attempts($quiz->id); |
fa583f5f | 60 | |
eb02301a | 61 | $PAGE->set_url($thispageurl); |
fa583f5f | 62 | |
fa583f5f | 63 | // Get the course object and related bits. |
e1a2d0d9 CC |
64 | $course = $DB->get_record('course', array('id' => $quiz->course), '*', MUST_EXIST); |
65 | $quizobj = new quiz($quiz, $cm, $course); | |
66 | $structure = $quizobj->get_structure(); | |
971c6fca | 67 | |
324d6576 | 68 | // You need mod/quiz:manage in addition to question capabilities to access this page. |
fa583f5f | 69 | require_capability('mod/quiz:manage', $contexts->lowest()); |
7bd1aa1d | 70 | |
5e8f7365 MN |
71 | // Log this visit. |
72 | $params = array( | |
73 | 'courseid' => $course->id, | |
74 | 'context' => $contexts->lowest(), | |
75 | 'other' => array( | |
76 | 'quizid' => $quiz->id | |
77 | ) | |
78 | ); | |
79 | $event = \mod_quiz\event\edit_page_viewed::create($params); | |
80 | $event->trigger(); | |
81 | ||
e1a2d0d9 | 82 | // Process commands ============================================================. |
4299df1d | 83 | |
84 | // Get the list of question ids had their check-boxes ticked. | |
ccba5b88 | 85 | $selectedslots = array(); |
4299df1d | 86 | $params = (array) data_submitted(); |
87 | foreach ($params as $key => $value) { | |
88 | if (preg_match('!^s([0-9]+)$!', $key, $matches)) { | |
ccba5b88 | 89 | $selectedslots[] = $matches[1]; |
6a952ce7 | 90 | } |
fa583f5f | 91 | } |
92 | ||
fd214b59 TH |
93 | $afteractionurl = new moodle_url($thispageurl); |
94 | if ($scrollpos) { | |
95 | $afteractionurl->param('scrollpos', $scrollpos); | |
96 | } | |
4299df1d | 97 | |
98 | if (optional_param('repaginate', false, PARAM_BOOL) && confirm_sesskey()) { | |
9e83f3d1 | 99 | // Re-paginate the quiz. |
4299df1d | 100 | $questionsperpage = optional_param('questionsperpage', $quiz->questionsperpage, PARAM_INT); |
ccba5b88 | 101 | quiz_repaginate_questions($quiz->id, $questionsperpage ); |
f9b0500f | 102 | quiz_delete_previews($quiz); |
fd214b59 | 103 | redirect($afteractionurl); |
fa583f5f | 104 | } |
324d6576 | 105 | |
106 | if (($addquestion = optional_param('addquestion', 0, PARAM_INT)) && confirm_sesskey()) { | |
9e83f3d1 | 107 | // Add a single question to the current quiz. |
76cf77e4 | 108 | quiz_require_question_use($addquestion); |
324d6576 | 109 | $addonpage = optional_param('addonpage', 0, PARAM_INT); |
fa583f5f | 110 | quiz_add_quiz_question($addquestion, $quiz, $addonpage); |
4299df1d | 111 | quiz_delete_previews($quiz); |
f9b0500f | 112 | quiz_update_sumgrades($quiz); |
455d765f | 113 | $thispageurl->param('lastchanged', $addquestion); |
fd214b59 | 114 | redirect($afteractionurl); |
fa583f5f | 115 | } |
116 | ||
324d6576 | 117 | if (optional_param('add', false, PARAM_BOOL) && confirm_sesskey()) { |
e1a2d0d9 | 118 | $addonpage = optional_param('addonpage', 0, PARAM_INT); |
9e83f3d1 | 119 | // Add selected questions to the current quiz. |
fa583f5f | 120 | $rawdata = (array) data_submitted(); |
9e83f3d1 | 121 | foreach ($rawdata as $key => $value) { // Parse input for question ids. |
fa583f5f | 122 | if (preg_match('!^q([0-9]+)$!', $key, $matches)) { |
123 | $key = $matches[1]; | |
76cf77e4 | 124 | quiz_require_question_use($key); |
e1a2d0d9 | 125 | quiz_add_quiz_question($key, $quiz, $addonpage); |
fef9b51d | 126 | } |
fef9b51d | 127 | } |
4299df1d | 128 | quiz_delete_previews($quiz); |
f9b0500f | 129 | quiz_update_sumgrades($quiz); |
fd214b59 | 130 | redirect($afteractionurl); |
fa583f5f | 131 | } |
132 | ||
94dbfb3a | 133 | if ((optional_param('addrandom', false, PARAM_BOOL)) && confirm_sesskey()) { |
9e83f3d1 | 134 | // Add random questions to the quiz. |
fa583f5f | 135 | $recurse = optional_param('recurse', 0, PARAM_BOOL); |
324d6576 | 136 | $addonpage = optional_param('addonpage', 0, PARAM_INT); |
94dbfb3a TH |
137 | $categoryid = required_param('categoryid', PARAM_INT); |
138 | $randomcount = required_param('randomcount', PARAM_INT); | |
139 | quiz_add_random_questions($quiz, $addonpage, $categoryid, $randomcount, $recurse); | |
6a952ce7 | 140 | |
4299df1d | 141 | quiz_delete_previews($quiz); |
f9b0500f | 142 | quiz_update_sumgrades($quiz); |
fd214b59 | 143 | redirect($afteractionurl); |
fa583f5f | 144 | } |
324d6576 | 145 | |
324d6576 | 146 | if (optional_param('savechanges', false, PARAM_BOOL) && confirm_sesskey()) { |
f9b0500f TH |
147 | $deletepreviews = false; |
148 | $recomputesummarks = false; | |
149 | ||
3e10e429 | 150 | $rawdata = (array) data_submitted(); |
fa583f5f | 151 | $moveonpagequestions = array(); |
3e10e429 | 152 | $moveselectedonpage = optional_param('moveselectedonpagetop', 0, PARAM_INT); |
153 | if (!$moveselectedonpage) { | |
154 | $moveselectedonpage = optional_param('moveselectedonpagebottom', 0, PARAM_INT); | |
fa583f5f | 155 | } |
70c01adb | 156 | |
ccba5b88 | 157 | $newslotorder = array(); |
3e10e429 | 158 | foreach ($rawdata as $key => $value) { |
4299df1d | 159 | if (preg_match('!^g([0-9]+)$!', $key, $matches)) { |
9e83f3d1 | 160 | // Parse input for question -> grades. |
ccba5b88 TH |
161 | $slotnumber = $matches[1]; |
162 | $newgrade = unformat_float($value); | |
163 | quiz_update_slot_maxmark($DB->get_record('quiz_slots', | |
164 | array('quizid' => $quiz->id, 'slot' => $slotnumber), '*', MUST_EXIST), $newgrade); | |
f9b0500f TH |
165 | $deletepreviews = true; |
166 | $recomputesummarks = true; | |
b72ff476 | 167 | |
3e10e429 | 168 | } else if (preg_match('!^o(pg)?([0-9]+)$!', $key, $matches)) { |
9e83f3d1 | 169 | // Parse input for ordering info. |
ccba5b88 | 170 | $slotnumber = $matches[2]; |
3e10e429 | 171 | // Make sure two questions don't overwrite each other. If we get a second |
172 | // question with the same position, shift the second one along to the next gap. | |
1e12c120 | 173 | $value = clean_param($value, PARAM_INT); |
ccba5b88 | 174 | while (array_key_exists($value, $newslotorder)) { |
fa583f5f | 175 | $value++; |
176 | } | |
3e10e429 | 177 | if ($matches[1]) { |
178 | // This is a page-break entry. | |
ccba5b88 | 179 | $newslotorder[$value] = 0; |
3e10e429 | 180 | } else { |
ccba5b88 | 181 | $newslotorder[$value] = $slotnumber; |
3e10e429 | 182 | } |
f9b0500f | 183 | $deletepreviews = true; |
fa583f5f | 184 | } |
185 | } | |
4299df1d | 186 | |
4299df1d | 187 | if ($moveselectedonpage) { |
ccba5b88 TH |
188 | |
189 | // Make up a $newslotorder, then let the next if statement do the work. | |
190 | $oldslots = $DB->get_records('quiz_slots', array('quizid' => $quiz->id), 'slot'); | |
191 | ||
192 | $beforepage = array(); | |
193 | $onpage = array(); | |
194 | $afterpage = array(); | |
195 | foreach ($oldslots as $oldslot) { | |
196 | if (in_array($oldslot->slot, $selectedslots)) { | |
197 | $onpage[] = $oldslot; | |
198 | } else if ($oldslot->page <= $moveselectedonpage) { | |
199 | $beforepage[] = $oldslot; | |
200 | } else { | |
201 | $afterpage[] = $oldslot; | |
218dfb91 | 202 | } |
203 | } | |
324d6576 | 204 | |
ccba5b88 TH |
205 | $newslotorder = array(); |
206 | $currentpage = 1; | |
207 | $index = 10; | |
208 | foreach ($beforepage as $slot) { | |
209 | while ($currentpage < $slot->page) { | |
210 | $newslotorder[$index] = 0; | |
211 | $index += 10; | |
212 | $currentpage += 1; | |
213 | } | |
214 | $newslotorder[$index] = $slot->slot; | |
215 | $index += 10; | |
216 | } | |
800944b0 | 217 | |
ccba5b88 TH |
218 | while ($currentpage < $moveselectedonpage) { |
219 | $newslotorder[$index] = 0; | |
220 | $index += 10; | |
221 | $currentpage += 1; | |
222 | } | |
223 | foreach ($onpage as $slot) { | |
224 | $newslotorder[$index] = $slot->slot; | |
225 | $index += 10; | |
800944b0 | 226 | } |
800944b0 | 227 | |
ccba5b88 TH |
228 | foreach ($afterpage as $slot) { |
229 | while ($currentpage < $slot->page) { | |
230 | $newslotorder[$index] = 0; | |
231 | $index += 10; | |
232 | $currentpage += 1; | |
233 | } | |
234 | $newslotorder[$index] = $slot->slot; | |
235 | $index += 10; | |
236 | } | |
237 | } | |
800944b0 | 238 | |
ccba5b88 TH |
239 | // If ordering info was given, reorder the questions. |
240 | if ($newslotorder) { | |
241 | ksort($newslotorder); | |
242 | $currentpage = 1; | |
243 | $currentslot = 1; | |
244 | $slotreorder = array(); | |
245 | $slotpages = array(); | |
246 | foreach ($newslotorder as $slotnumber) { | |
247 | if ($slotnumber == 0) { | |
248 | $currentpage += 1; | |
249 | continue; | |
250 | } | |
251 | $slotreorder[$slotnumber] = $currentslot; | |
252 | $slotpages[$currentslot] = $currentpage; | |
253 | $currentslot += 1; | |
254 | } | |
255 | $trans = $DB->start_delegated_transaction(); | |
256 | update_field_with_unique_index('quiz_slots', | |
257 | 'slot', $slotreorder, array('quizid' => $quiz->id)); | |
258 | foreach ($slotpages as $slotnumber => $page) { | |
259 | $DB->set_field('quiz_slots', 'page', $page, array('quizid' => $quiz->id, 'slot' => $slotnumber)); | |
260 | } | |
261 | $trans->allow_commit(); | |
f9b0500f | 262 | $deletepreviews = true; |
fa583f5f | 263 | } |
3e10e429 | 264 | |
9e83f3d1 | 265 | // If rescaling is required save the new maximum. |
7f5f3844 | 266 | $maxgrade = unformat_float(optional_param('maxgrade', -1, PARAM_RAW)); |
fa583f5f | 267 | if ($maxgrade >= 0) { |
3e10e429 | 268 | quiz_set_grade($maxgrade, $quiz); |
70c01adb | 269 | } |
ee1fb969 | 270 | |
f9b0500f TH |
271 | if ($deletepreviews) { |
272 | quiz_delete_previews($quiz); | |
273 | } | |
274 | if ($recomputesummarks) { | |
275 | quiz_update_sumgrades($quiz); | |
276 | quiz_update_all_attempt_sumgrades($quiz); | |
277 | quiz_update_all_final_grades($quiz); | |
278 | quiz_update_grades($quiz, 0, true); | |
279 | } | |
fd214b59 | 280 | redirect($afteractionurl); |
fa583f5f | 281 | } |
6551bb89 | 282 | |
e1a2d0d9 CC |
283 | // Get the question bank view. |
284 | $questionbank = new quiz_question_bank_view($contexts, $thispageurl, $course, $cm, $quiz); | |
285 | $questionbank->set_quiz_has_attempts($quizhasattempts); | |
f4b879dd | 286 | $questionbank->process_actions($thispageurl, $cm); |
b37cacae | 287 | |
e1a2d0d9 CC |
288 | // End of process commands =====================================================. |
289 | ||
290 | $PAGE->set_pagelayout('incourse'); | |
291 | $PAGE->set_pagetype('mod-quiz-edit'); | |
292 | ||
293 | $output = $PAGE->get_renderer('mod_quiz', 'edit'); | |
fa583f5f | 294 | |
4c7f377d | 295 | $PAGE->set_title(get_string('editingquizx', 'quiz', format_string($quiz->name))); |
56ed242b | 296 | $PAGE->set_heading($course->fullname); |
eb02301a TH |
297 | $node = $PAGE->settingsnav->find('mod_quiz_edit', navigation_node::TYPE_SETTING); |
298 | if ($node) { | |
299 | $node->make_active(); | |
300 | } | |
03da0c39 | 301 | echo $OUTPUT->header(); |
6f030b0f | 302 | |
1372b5d2 | 303 | // Initialise the JavaScript. |
0ff4bd08 | 304 | $quizeditconfig = new stdClass(); |
b9bc2019 | 305 | $quizeditconfig->url = $thispageurl->out(true, array('qbanktool' => '0')); |
3e10e429 | 306 | $quizeditconfig->dialoglisteners = array(); |
ccba5b88 TH |
307 | $numberoflisteners = $DB->get_field_sql(" |
308 | SELECT COALESCE(MAX(page), 1) | |
309 | FROM {quiz_slots} | |
310 | WHERE quizid = ?", array($quiz->id)); | |
311 | ||
1372b5d2 | 312 | for ($pageiter = 1; $pageiter <= $numberoflisteners; $pageiter++) { |
313 | $quizeditconfig->dialoglisteners[] = 'addrandomdialoglaunch_' . $pageiter; | |
314 | } | |
e1a2d0d9 | 315 | |
cf615522 | 316 | $PAGE->requires->data_for_js('quiz_edit_config', $quizeditconfig); |
fd214b59 | 317 | $PAGE->requires->js('/question/qengine.js'); |
16cad79b | 318 | |
e1a2d0d9 CC |
319 | // Questions wrapper start. |
320 | echo html_writer::start_tag('div', array('class' => 'mod-quiz-edit-content')); | |
fa583f5f | 321 | |
e1a2d0d9 | 322 | echo $output->edit_page($quizobj, $structure, $contexts, $thispageurl, $pagevars); |
fa583f5f | 323 | |
e1a2d0d9 CC |
324 | // Questions wrapper end. |
325 | echo html_writer::end_tag('div'); | |
56ed242b | 326 | |
867847e3 | 327 | echo $OUTPUT->footer(); |