Commit | Line | Data |
---|---|---|
87f83794 | 1 | <?php |
2 | ||
0a4abb73 SH |
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 | ||
5491947a | 18 | /** |
19 | * Local library file for Lesson. These are non-standard functions that are used | |
20 | * only by Lesson. | |
21 | * | |
cc3dbaaa PS |
22 | * @package mod |
23 | * @subpackage lesson | |
0a4abb73 | 24 | * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} |
cc3dbaaa | 25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or late |
5491947a | 26 | **/ |
4b55d2af | 27 | |
0a4abb73 | 28 | /** Make sure this isn't being directly accessed */ |
1e7f8ea2 | 29 | defined('MOODLE_INTERNAL') || die(); |
0a4abb73 SH |
30 | |
31 | /** Include the files that are required by this module */ | |
1e7f8ea2 | 32 | require_once($CFG->dirroot.'/course/moodleform_mod.php'); |
0a4abb73 SH |
33 | require_once($CFG->dirroot . '/mod/lesson/lib.php'); |
34 | ||
44cb7e63 PS |
35 | /** This page */ |
36 | define('LESSON_THISPAGE', 0); | |
0a4abb73 SH |
37 | /** Next page -> any page not seen before */ |
38 | define("LESSON_UNSEENPAGE", 1); | |
39 | /** Next page -> any page not answered correctly */ | |
40 | define("LESSON_UNANSWEREDPAGE", 2); | |
41 | /** Jump to Next Page */ | |
42 | define("LESSON_NEXTPAGE", -1); | |
43 | /** End of Lesson */ | |
44 | define("LESSON_EOL", -9); | |
45 | /** Jump to an unseen page within a branch and end of branch or end of lesson */ | |
46 | define("LESSON_UNSEENBRANCHPAGE", -50); | |
47 | /** Jump to Previous Page */ | |
48 | define("LESSON_PREVIOUSPAGE", -40); | |
49 | /** Jump to a random page within a branch and end of branch or end of lesson */ | |
50 | define("LESSON_RANDOMPAGE", -60); | |
51 | /** Jump to a random Branch */ | |
52 | define("LESSON_RANDOMBRANCH", -70); | |
53 | /** Cluster Jump */ | |
54 | define("LESSON_CLUSTERJUMP", -80); | |
55 | /** Undefined */ | |
56 | define("LESSON_UNDEFINED", -99); | |
5e7856af | 57 | |
1e7f8ea2 PS |
58 | /** LESSON_MAX_EVENT_LENGTH = 432000 ; 5 days maximum */ |
59 | define("LESSON_MAX_EVENT_LENGTH", "432000"); | |
60 | ||
61 | ||
5e7856af | 62 | ////////////////////////////////////////////////////////////////////////////////////// |
86342d63 | 63 | /// Any other lesson functions go here. Each of them must have a name that |
5e7856af | 64 | /// starts with lesson_ |
65 | ||
4b55d2af | 66 | /** |
86342d63 | 67 | * Checks to see if a LESSON_CLUSTERJUMP or |
4b55d2af | 68 | * a LESSON_UNSEENBRANCHPAGE is used in a lesson. |
69 | * | |
86342d63 | 70 | * This function is only executed when a teacher is |
4b55d2af | 71 | * checking the navigation for a lesson. |
72 | * | |
06469639 | 73 | * @param int $lesson Id of the lesson that is to be checked. |
4b55d2af | 74 | * @return boolean True or false. |
75 | **/ | |
06469639 | 76 | function lesson_display_teacher_warning($lesson) { |
646fc290 | 77 | global $DB; |
86342d63 | 78 | |
ac8e16be | 79 | // get all of the lesson answers |
0a4abb73 | 80 | $params = array ("lessonid" => $lesson->id); |
646fc290 | 81 | if (!$lessonanswers = $DB->get_records_select("lesson_answers", "lessonid = :lessonid", $params)) { |
ac8e16be | 82 | // no answers, then not useing cluster or unseen |
83 | return false; | |
84 | } | |
85 | // just check for the first one that fulfills the requirements | |
86 | foreach ($lessonanswers as $lessonanswer) { | |
87 | if ($lessonanswer->jumpto == LESSON_CLUSTERJUMP || $lessonanswer->jumpto == LESSON_UNSEENBRANCHPAGE) { | |
88 | return true; | |
89 | } | |
90 | } | |
86342d63 | 91 | |
ac8e16be | 92 | // if no answers use either of the two jumps |
93 | return false; | |
5e7856af | 94 | } |
95 | ||
4b55d2af | 96 | /** |
97 | * Interprets the LESSON_UNSEENBRANCHPAGE jump. | |
86342d63 | 98 | * |
4b55d2af | 99 | * will return the pageid of a random unseen page that is within a branch |
100 | * | |
0a4abb73 | 101 | * @param lesson $lesson |
f521f98a | 102 | * @param int $userid Id of the user. |
4b55d2af | 103 | * @param int $pageid Id of the page from which we are jumping. |
104 | * @return int Id of the next page. | |
4b55d2af | 105 | **/ |
5e7856af | 106 | function lesson_unseen_question_jump($lesson, $user, $pageid) { |
646fc290 | 107 | global $DB; |
86342d63 | 108 | |
ac8e16be | 109 | // get the number of retakes |
0a4abb73 | 110 | if (!$retakes = $DB->count_records("lesson_grades", array("lessonid"=>$lesson->id, "userid"=>$user))) { |
ac8e16be | 111 | $retakes = 0; |
112 | } | |
113 | ||
114 | // get all the lesson_attempts aka what the user has seen | |
0a4abb73 | 115 | if ($viewedpages = $DB->get_records("lesson_attempts", array("lessonid"=>$lesson->id, "userid"=>$user, "retry"=>$retakes), "timeseen DESC")) { |
ac8e16be | 116 | foreach($viewedpages as $viewed) { |
117 | $seenpages[] = $viewed->pageid; | |
118 | } | |
119 | } else { | |
120 | $seenpages = array(); | |
121 | } | |
122 | ||
123 | // get the lesson pages | |
0a4abb73 | 124 | $lessonpages = $lesson->load_all_pages(); |
86342d63 | 125 | |
ac8e16be | 126 | if ($pageid == LESSON_UNSEENBRANCHPAGE) { // this only happens when a student leaves in the middle of an unseen question within a branch series |
127 | $pageid = $seenpages[0]; // just change the pageid to the last page viewed inside the branch table | |
128 | } | |
129 | ||
130 | // go up the pages till branch table | |
131 | while ($pageid != 0) { // this condition should never be satisfied... only happens if there are no branch tables above this page | |
0a4abb73 | 132 | if ($lessonpages[$pageid]->qtype == LESSON_PAGE_BRANCHTABLE) { |
ac8e16be | 133 | break; |
134 | } | |
135 | $pageid = $lessonpages[$pageid]->prevpageid; | |
136 | } | |
86342d63 | 137 | |
0a4abb73 | 138 | $pagesinbranch = $this->get_sub_pages_of($pageid, array(LESSON_PAGE_BRANCHTABLE, LESSON_PAGE_ENDOFBRANCH)); |
86342d63 | 139 | |
ac8e16be | 140 | // this foreach loop stores all the pages that are within the branch table but are not in the $seenpages array |
141 | $unseen = array(); | |
86342d63 | 142 | foreach($pagesinbranch as $page) { |
ac8e16be | 143 | if (!in_array($page->id, $seenpages)) { |
144 | $unseen[] = $page->id; | |
145 | } | |
146 | } | |
147 | ||
148 | if(count($unseen) == 0) { | |
149 | if(isset($pagesinbranch)) { | |
150 | $temp = end($pagesinbranch); | |
151 | $nextpage = $temp->nextpageid; // they have seen all the pages in the branch, so go to EOB/next branch table/EOL | |
152 | } else { | |
153 | // there are no pages inside the branch, so return the next page | |
154 | $nextpage = $lessonpages[$pageid]->nextpageid; | |
155 | } | |
156 | if ($nextpage == 0) { | |
157 | return LESSON_EOL; | |
158 | } else { | |
159 | return $nextpage; | |
160 | } | |
161 | } else { | |
162 | return $unseen[rand(0, count($unseen)-1)]; // returns a random page id for the next page | |
163 | } | |
5e7856af | 164 | } |
165 | ||
4b55d2af | 166 | /** |
167 | * Handles the unseen branch table jump. | |
168 | * | |
0a4abb73 | 169 | * @param lesson $lesson |
f521f98a | 170 | * @param int $userid User id. |
4b55d2af | 171 | * @return int Will return the page id of a branch table or end of lesson |
4b55d2af | 172 | **/ |
0a4abb73 | 173 | function lesson_unseen_branch_jump($lesson, $userid) { |
646fc290 | 174 | global $DB; |
86342d63 | 175 | |
0a4abb73 | 176 | if (!$retakes = $DB->count_records("lesson_grades", array("lessonid"=>$lesson->id, "userid"=>$userid))) { |
ac8e16be | 177 | $retakes = 0; |
178 | } | |
179 | ||
0a4abb73 | 180 | $params = array ("lessonid" => $lesson->id, "userid" => $userid, "retry" => $retakes); |
646fc290 | 181 | if (!$seenbranches = $DB->get_records_select("lesson_branch", "lessonid = :lessonid AND userid = :userid AND retry = :retry", $params, |
ac8e16be | 182 | "timeseen DESC")) { |
86f93345 | 183 | print_error('cannotfindrecords', 'lesson'); |
ac8e16be | 184 | } |
185 | ||
186 | // get the lesson pages | |
0a4abb73 | 187 | $lessonpages = $lesson->load_all_pages(); |
86342d63 | 188 | |
ff85f902 | 189 | // this loads all the viewed branch tables into $seen until it finds the branch table with the flag |
ac8e16be | 190 | // which is the branch table that starts the unseenbranch function |
86342d63 | 191 | $seen = array(); |
ac8e16be | 192 | foreach ($seenbranches as $seenbranch) { |
193 | if (!$seenbranch->flag) { | |
194 | $seen[$seenbranch->pageid] = $seenbranch->pageid; | |
195 | } else { | |
196 | $start = $seenbranch->pageid; | |
197 | break; | |
198 | } | |
199 | } | |
200 | // this function searches through the lesson pages to find all the branch tables | |
201 | // that follow the flagged branch table | |
202 | $pageid = $lessonpages[$start]->nextpageid; // move down from the flagged branch table | |
203 | while ($pageid != 0) { // grab all of the branch table till eol | |
0a4abb73 | 204 | if ($lessonpages[$pageid]->qtype == LESSON_PAGE_BRANCHTABLE) { |
ac8e16be | 205 | $branchtables[] = $lessonpages[$pageid]->id; |
206 | } | |
207 | $pageid = $lessonpages[$pageid]->nextpageid; | |
208 | } | |
209 | $unseen = array(); | |
210 | foreach ($branchtables as $branchtable) { | |
211 | // load all of the unseen branch tables into unseen | |
212 | if (!array_key_exists($branchtable, $seen)) { | |
213 | $unseen[] = $branchtable; | |
214 | } | |
215 | } | |
216 | if (count($unseen) > 0) { | |
217 | return $unseen[rand(0, count($unseen)-1)]; // returns a random page id for the next page | |
218 | } else { | |
219 | return LESSON_EOL; // has viewed all of the branch tables | |
220 | } | |
5e7856af | 221 | } |
222 | ||
4b55d2af | 223 | /** |
224 | * Handles the random jump between a branch table and end of branch or end of lesson (LESSON_RANDOMPAGE). | |
86342d63 | 225 | * |
0a4abb73 | 226 | * @param lesson $lesson |
4b55d2af | 227 | * @param int $pageid The id of the page that we are jumping from (?) |
228 | * @return int The pageid of a random page that is within a branch table | |
4b55d2af | 229 | **/ |
0a4abb73 | 230 | function lesson_random_question_jump($lesson, $pageid) { |
646fc290 | 231 | global $DB; |
86342d63 | 232 | |
ac8e16be | 233 | // get the lesson pages |
0a4abb73 | 234 | $params = array ("lessonid" => $lesson->id); |
646fc290 | 235 | if (!$lessonpages = $DB->get_records_select("lesson_pages", "lessonid = :lessonid", $params)) { |
86f93345 | 236 | print_error('cannotfindpages', 'lesson'); |
ac8e16be | 237 | } |
238 | ||
239 | // go up the pages till branch table | |
240 | while ($pageid != 0) { // this condition should never be satisfied... only happens if there are no branch tables above this page | |
241 | ||
0a4abb73 | 242 | if ($lessonpages[$pageid]->qtype == LESSON_PAGE_BRANCHTABLE) { |
ac8e16be | 243 | break; |
244 | } | |
245 | $pageid = $lessonpages[$pageid]->prevpageid; | |
246 | } | |
247 | ||
86342d63 | 248 | // get the pages within the branch |
0a4abb73 | 249 | $pagesinbranch = $this->get_sub_pages_of($pageid, array(LESSON_PAGE_BRANCHTABLE, LESSON_PAGE_ENDOFBRANCH)); |
86342d63 | 250 | |
ac8e16be | 251 | if(count($pagesinbranch) == 0) { |
252 | // there are no pages inside the branch, so return the next page | |
253 | return $lessonpages[$pageid]->nextpageid; | |
254 | } else { | |
255 | return $pagesinbranch[rand(0, count($pagesinbranch)-1)]->id; // returns a random page id for the next page | |
256 | } | |
5e7856af | 257 | } |
258 | ||
4b55d2af | 259 | /** |
260 | * Calculates a user's grade for a lesson. | |
261 | * | |
4b55d2af | 262 | * @param object $lesson The lesson that the user is taking. |
4b55d2af | 263 | * @param int $retries The attempt number. |
ff85f902 | 264 | * @param int $userid Id of the user (optional, default current user). |
88427c07 | 265 | * @return object { nquestions => number of questions answered |
266 | attempts => number of question attempts | |
267 | total => max points possible | |
268 | earned => points earned by student | |
269 | grade => calculated percentage grade | |
270 | nmanual => number of manually graded questions | |
271 | manualpoints => point value for manually graded questions } | |
4b55d2af | 272 | */ |
86342d63 | 273 | function lesson_grade($lesson, $ntries, $userid = 0) { |
646fc290 | 274 | global $USER, $DB; |
ac8e16be | 275 | |
88427c07 | 276 | if (empty($userid)) { |
277 | $userid = $USER->id; | |
278 | } | |
86342d63 | 279 | |
88427c07 | 280 | // Zero out everything |
281 | $ncorrect = 0; | |
282 | $nviewed = 0; | |
283 | $score = 0; | |
284 | $nmanual = 0; | |
285 | $manualpoints = 0; | |
286 | $thegrade = 0; | |
287 | $nquestions = 0; | |
288 | $total = 0; | |
289 | $earned = 0; | |
290 | ||
646fc290 | 291 | $params = array ("lessonid" => $lesson->id, "userid" => $userid, "retry" => $ntries); |
86342d63 | 292 | if ($useranswers = $DB->get_records_select("lesson_attempts", "lessonid = :lessonid AND |
646fc290 | 293 | userid = :userid AND retry = :retry", $params, "timeseen")) { |
88427c07 | 294 | // group each try with its page |
295 | $attemptset = array(); | |
296 | foreach ($useranswers as $useranswer) { | |
86342d63 | 297 | $attemptset[$useranswer->pageid][] = $useranswer; |
ac8e16be | 298 | } |
86342d63 | 299 | |
88427c07 | 300 | // Drop all attempts that go beyond max attempts for the lesson |
301 | foreach ($attemptset as $key => $set) { | |
302 | $attemptset[$key] = array_slice($set, 0, $lesson->maxattempts); | |
303 | } | |
86342d63 | 304 | |
88427c07 | 305 | // get only the pages and their answers that the user answered |
646fc290 | 306 | list($usql, $parameters) = $DB->get_in_or_equal(array_keys($attemptset)); |
0a4abb73 SH |
307 | array_unshift($parameters, $lesson->id); |
308 | $pages = $DB->get_records_select("lesson_pages", "lessonid = ? AND id $usql", $parameters); | |
309 | $answers = $DB->get_records_select("lesson_answers", "lessonid = ? AND pageid $usql", $parameters); | |
86342d63 | 310 | |
88427c07 | 311 | // Number of pages answered |
312 | $nquestions = count($pages); | |
313 | ||
314 | foreach ($attemptset as $attempts) { | |
0a4abb73 | 315 | $page = lesson_page::load($pages[end($attempts)->pageid], $lesson); |
88427c07 | 316 | if ($lesson->custom) { |
317 | $attempt = end($attempts); | |
318 | // If essay question, handle it, otherwise add to score | |
0a4abb73 SH |
319 | if ($page->requires_manual_grading()) { |
320 | $earned += $page->earned_score($answers, $attempt); | |
88427c07 | 321 | $nmanual++; |
322 | $manualpoints += $answers[$attempt->answerid]->score; | |
ab1e7c39 | 323 | } else if (!empty($attempt->answerid)) { |
0a4abb73 | 324 | $earned += $page->earned_score($answers, $attempt); |
88427c07 | 325 | } |
326 | } else { | |
327 | foreach ($attempts as $attempt) { | |
328 | $earned += $attempt->correct; | |
329 | } | |
330 | $attempt = end($attempts); // doesn't matter which one | |
331 | // If essay question, increase numbers | |
0a4abb73 | 332 | if ($page->requires_manual_grading()) { |
88427c07 | 333 | $nmanual++; |
334 | $manualpoints++; | |
ac8e16be | 335 | } |
336 | } | |
88427c07 | 337 | // Number of times answered |
338 | $nviewed += count($attempts); | |
339 | } | |
86342d63 | 340 | |
88427c07 | 341 | if ($lesson->custom) { |
ac8e16be | 342 | $bestscores = array(); |
88427c07 | 343 | // Find the highest possible score per page to get our total |
344 | foreach ($answers as $answer) { | |
46341ab7 | 345 | if(!isset($bestscores[$answer->pageid])) { |
88427c07 | 346 | $bestscores[$answer->pageid] = $answer->score; |
46341ab7 | 347 | } else if ($bestscores[$answer->pageid] < $answer->score) { |
88427c07 | 348 | $bestscores[$answer->pageid] = $answer->score; |
ac8e16be | 349 | } |
350 | } | |
88427c07 | 351 | $total = array_sum($bestscores); |
352 | } else { | |
353 | // Check to make sure the student has answered the minimum questions | |
354 | if ($lesson->minquestions and $nquestions < $lesson->minquestions) { | |
355 | // Nope, increase number viewed by the amount of unanswered questions | |
356 | $total = $nviewed + ($lesson->minquestions - $nquestions); | |
357 | } else { | |
358 | $total = $nviewed; | |
359 | } | |
ac8e16be | 360 | } |
88427c07 | 361 | } |
86342d63 | 362 | |
88427c07 | 363 | if ($total) { // not zero |
364 | $thegrade = round(100 * $earned / $total, 5); | |
365 | } | |
86342d63 | 366 | |
88427c07 | 367 | // Build the grade information object |
368 | $gradeinfo = new stdClass; | |
369 | $gradeinfo->nquestions = $nquestions; | |
370 | $gradeinfo->attempts = $nviewed; | |
371 | $gradeinfo->total = $total; | |
372 | $gradeinfo->earned = $earned; | |
373 | $gradeinfo->grade = $thegrade; | |
374 | $gradeinfo->nmanual = $nmanual; | |
375 | $gradeinfo->manualpoints = $manualpoints; | |
86342d63 | 376 | |
88427c07 | 377 | return $gradeinfo; |
378 | } | |
379 | ||
62eda6ea | 380 | /** |
381 | * Determines if a user can view the left menu. The determining factor | |
382 | * is whether a user has a grade greater than or equal to the lesson setting | |
383 | * of displayleftif | |
384 | * | |
385 | * @param object $lesson Lesson object of the current lesson | |
386 | * @return boolean 0 if the user cannot see, or $lesson->displayleft to keep displayleft unchanged | |
62eda6ea | 387 | **/ |
388 | function lesson_displayleftif($lesson) { | |
646fc290 | 389 | global $CFG, $USER, $DB; |
86342d63 | 390 | |
62eda6ea | 391 | if (!empty($lesson->displayleftif)) { |
392 | // get the current user's max grade for this lesson | |
646fc290 | 393 | $params = array ("userid" => $USER->id, "lessonid" => $lesson->id); |
394 | if ($maxgrade = $DB->get_record_sql('SELECT userid, MAX(grade) AS maxgrade FROM {lesson_grades} WHERE userid = :userid AND lessonid = :lessonid GROUP BY userid', $params)) { | |
62eda6ea | 395 | if ($maxgrade->maxgrade < $lesson->displayleftif) { |
396 | return 0; // turn off the displayleft | |
397 | } | |
398 | } else { | |
399 | return 0; // no grades | |
400 | } | |
401 | } | |
86342d63 | 402 | |
62eda6ea | 403 | // if we get to here, keep the original state of displayleft lesson setting |
404 | return $lesson->displayleft; | |
405 | } | |
5e7856af | 406 | |
4262a2f8 | 407 | /** |
86342d63 | 408 | * |
4262a2f8 | 409 | * @param $cm |
410 | * @param $lesson | |
411 | * @param $page | |
412 | * @return unknown_type | |
413 | */ | |
414 | function lesson_add_pretend_blocks($page, $cm, $lesson, $timer = null) { | |
415 | $bc = lesson_menu_block_contents($cm->id, $lesson); | |
416 | if (!empty($bc)) { | |
417 | $regions = $page->blocks->get_regions(); | |
418 | $firstregion = reset($regions); | |
419 | $page->blocks->add_pretend_block($bc, $firstregion); | |
420 | } | |
421 | ||
422 | $bc = lesson_mediafile_block_contents($cm->id, $lesson); | |
423 | if (!empty($bc)) { | |
424 | $page->blocks->add_pretend_block($bc, $page->blocks->get_default_region()); | |
425 | } | |
426 | ||
427 | if (!empty($timer)) { | |
428 | $bc = lesson_clock_block_contents($cm->id, $lesson, $timer, $page); | |
429 | if (!empty($bc)) { | |
430 | $page->blocks->add_pretend_block($bc, $page->blocks->get_default_region()); | |
431 | } | |
432 | } | |
433 | } | |
434 | ||
f521f98a | 435 | /** |
86342d63 | 436 | * If there is a media file associated with this |
4262a2f8 | 437 | * lesson, return a block_contents that displays it. |
f521f98a | 438 | * |
439 | * @param int $cmid Course Module ID for this lesson | |
440 | * @param object $lesson Full lesson record object | |
4262a2f8 | 441 | * @return block_contents |
f521f98a | 442 | **/ |
4262a2f8 | 443 | function lesson_mediafile_block_contents($cmid, $lesson) { |
d68ccdba | 444 | global $OUTPUT; |
0a4abb73 | 445 | if (empty($lesson->mediafile) && empty($lesson->mediafileid)) { |
4262a2f8 | 446 | return null; |
f521f98a | 447 | } |
4262a2f8 | 448 | |
0a4abb73 SH |
449 | $options = array(); |
450 | $options['menubar'] = 0; | |
451 | $options['location'] = 0; | |
452 | $options['left'] = 5; | |
453 | $options['top'] = 5; | |
454 | $options['scrollbars'] = 1; | |
455 | $options['resizable'] = 1; | |
456 | $options['width'] = $lesson->mediawidth; | |
457 | $options['height'] = $lesson->mediaheight; | |
4262a2f8 | 458 | |
9bf16314 PS |
459 | $link = new moodle_url('/mod/lesson/mediafile.php?id='.$cmid); |
460 | $action = new popup_action('click', $link, 'lessonmediafile', $options); | |
461 | $content = $OUTPUT->action_link($link, get_string('mediafilepopup', 'lesson'), $action, array('title'=>get_string('mediafilepopup', 'lesson'))); | |
86342d63 | 462 | |
4262a2f8 | 463 | $bc = new block_contents(); |
464 | $bc->title = get_string('linkedmedia', 'lesson'); | |
0235e247 | 465 | $bc->attributes['class'] = 'mediafile'; |
4262a2f8 | 466 | $bc->content = $content; |
467 | ||
468 | return $bc; | |
f521f98a | 469 | } |
470 | ||
471 | /** | |
472 | * If a timed lesson and not a teacher, then | |
4262a2f8 | 473 | * return a block_contents containing the clock. |
f521f98a | 474 | * |
475 | * @param int $cmid Course Module ID for this lesson | |
476 | * @param object $lesson Full lesson record object | |
477 | * @param object $timer Full timer record object | |
4262a2f8 | 478 | * @return block_contents |
f521f98a | 479 | **/ |
4262a2f8 | 480 | function lesson_clock_block_contents($cmid, $lesson, $timer, $page) { |
481 | // Display for timed lessons and for students only | |
f521f98a | 482 | $context = get_context_instance(CONTEXT_MODULE, $cmid); |
4262a2f8 | 483 | if(!$lesson->timed || has_capability('mod/lesson:manage', $context)) { |
484 | return null; | |
485 | } | |
f521f98a | 486 | |
4262a2f8 | 487 | $content = '<div class="jshidewhenenabled">'; |
0a4abb73 | 488 | $content .= $lesson->time_remaining($timer->starttime); |
4262a2f8 | 489 | $content .= '</div>'; |
ba458143 | 490 | |
4262a2f8 | 491 | $clocksettings = array('starttime'=>$timer->starttime, 'servertime'=>time(),'testlength'=>($lesson->maxtime * 60)); |
227255b8 PS |
492 | $page->requires->data_for_js('clocksettings', $clocksettings); |
493 | $page->requires->js('/mod/lesson/timer.js'); | |
494 | $page->requires->js_function_call('show_clock'); | |
ba458143 | 495 | |
4262a2f8 | 496 | $bc = new block_contents(); |
497 | $bc->title = get_string('timeremaining', 'lesson'); | |
6605ff8c | 498 | $bc->attributes['class'] = 'clock block'; |
4262a2f8 | 499 | $bc->content = $content; |
500 | ||
501 | return $bc; | |
f521f98a | 502 | } |
503 | ||
504 | /** | |
505 | * If left menu is turned on, then this will | |
506 | * print the menu in a block | |
507 | * | |
508 | * @param int $cmid Course Module ID for this lesson | |
0a4abb73 | 509 | * @param lesson $lesson Full lesson record object |
f521f98a | 510 | * @return void |
511 | **/ | |
4262a2f8 | 512 | function lesson_menu_block_contents($cmid, $lesson) { |
646fc290 | 513 | global $CFG, $DB; |
f521f98a | 514 | |
4262a2f8 | 515 | if (!$lesson->displayleft) { |
516 | return null; | |
517 | } | |
f521f98a | 518 | |
0a4abb73 SH |
519 | $pages = $lesson->load_all_pages(); |
520 | foreach ($pages as $page) { | |
521 | if ((int)$page->prevpageid === 0) { | |
522 | $pageid = $page->id; | |
523 | break; | |
524 | } | |
525 | } | |
4262a2f8 | 526 | $currentpageid = optional_param('pageid', $pageid, PARAM_INT); |
f521f98a | 527 | |
4262a2f8 | 528 | if (!$pageid || !$pages) { |
529 | return null; | |
f521f98a | 530 | } |
f521f98a | 531 | |
4262a2f8 | 532 | $content = '<a href="#maincontent" class="skip">'.get_string('skip', 'lesson')."</a>\n<div class=\"menuwrapper\">\n<ul>\n"; |
888f0c54 | 533 | |
4262a2f8 | 534 | while ($pageid != 0) { |
535 | $page = $pages[$pageid]; | |
536 | ||
537 | // Only process branch tables with display turned on | |
0a4abb73 | 538 | if ($page->displayinmenublock && $page->display) { |
86342d63 | 539 | if ($page->id == $currentpageid) { |
4262a2f8 | 540 | $content .= '<li class="selected">'.format_string($page->title,true)."</li>\n"; |
541 | } else { | |
542 | $content .= "<li class=\"notselected\"><a href=\"$CFG->wwwroot/mod/lesson/view.php?id=$cmid&pageid=$page->id\">".format_string($page->title,true)."</a></li>\n"; | |
543 | } | |
86342d63 | 544 | |
888f0c54 | 545 | } |
4262a2f8 | 546 | $pageid = $page->nextpageid; |
888f0c54 | 547 | } |
4262a2f8 | 548 | $content .= "</ul>\n</div>\n"; |
888f0c54 | 549 | |
4262a2f8 | 550 | $bc = new block_contents(); |
551 | $bc->title = get_string('lessonmenu', 'lesson'); | |
6605ff8c | 552 | $bc->attributes['class'] = 'menu block'; |
4262a2f8 | 553 | $bc->content = $content; |
888f0c54 | 554 | |
4262a2f8 | 555 | return $bc; |
448052a5 SH |
556 | } |
557 | ||
558 | /** | |
559 | * Adds header buttons to the page for the lesson | |
560 | * | |
561 | * @param object $cm | |
562 | * @param object $context | |
563 | * @param bool $extraeditbuttons | |
564 | * @param int $lessonpageid | |
565 | */ | |
566 | function lesson_add_header_buttons($cm, $context, $extraeditbuttons=false, $lessonpageid=null) { | |
567 | global $CFG, $PAGE, $OUTPUT; | |
92059c7e SH |
568 | if (has_capability('mod/lesson:edit', $context) && $extraeditbuttons) { |
569 | if ($lessonpageid === null) { | |
570 | print_error('invalidpageid', 'lesson'); | |
571 | } | |
572 | if (!empty($lessonpageid) && $lessonpageid != LESSON_EOL) { | |
52b2f0ba | 573 | $url = new moodle_url('/mod/lesson/editpage.php', array('id'=>$cm->id, 'pageid'=>$lessonpageid, 'edit'=>1)); |
5c2ed7e2 | 574 | $PAGE->set_button($OUTPUT->single_button($url, get_string('editpagecontent', 'lesson'))); |
448052a5 | 575 | } |
448052a5 | 576 | } |
5c2ed7e2 | 577 | } |
9b56a34f PS |
578 | |
579 | /** | |
580 | * This is a function used to detect media types and generate html code. | |
581 | * | |
582 | * @global object $CFG | |
583 | * @global object $PAGE | |
584 | * @param object $lesson | |
585 | * @param object $context | |
586 | * @return string $code the html code of media | |
587 | */ | |
588 | function lesson_get_media_html($lesson, $context) { | |
589 | global $CFG, $PAGE, $OUTPUT; | |
590 | require_once("$CFG->libdir/resourcelib.php"); | |
591 | ||
64f93798 | 592 | // get the media file link |
3d1b7452 | 593 | $url = moodle_url::make_pluginfile_url($context->id, 'mod_lesson', 'mediafile', $lesson->timemodified, '/', $lesson->mediafile); |
9b56a34f PS |
594 | $title = $lesson->mediafile; |
595 | ||
596 | $clicktoopen = html_writer::link(new moodle_url($url), get_string('download')); | |
597 | ||
598 | $mimetype = resourcelib_guess_url_mimetype($url); | |
599 | ||
600 | // find the correct type and print it out | |
601 | if (in_array($mimetype, array('image/gif','image/jpeg','image/png'))) { // It's an image | |
602 | $code = resourcelib_embed_image($url, $title); | |
603 | ||
604 | } else if ($mimetype == 'audio/mp3') { | |
605 | // MP3 audio file | |
606 | $code = resourcelib_embed_mp3($url, $title, $clicktoopen); | |
607 | ||
608 | } else if ($mimetype == 'video/x-flv') { | |
609 | // Flash video file | |
610 | $code = resourcelib_embed_flashvideo($url, $title, $clicktoopen); | |
611 | ||
612 | } else if ($mimetype == 'application/x-shockwave-flash') { | |
613 | // Flash file | |
614 | $code = resourcelib_embed_flash($url, $title, $clicktoopen); | |
615 | ||
616 | } else if (substr($mimetype, 0, 10) == 'video/x-ms') { | |
617 | // Windows Media Player file | |
618 | $code = resourcelib_embed_mediaplayer($url, $title, $clicktoopen); | |
619 | ||
620 | } else if ($mimetype == 'video/quicktime') { | |
621 | // Quicktime file | |
622 | $code = resourcelib_embed_quicktime($url, $title, $clicktoopen); | |
623 | ||
624 | } else if ($mimetype == 'video/mpeg') { | |
625 | // Mpeg file | |
626 | $code = resourcelib_embed_mpeg($url, $title, $clicktoopen); | |
627 | ||
628 | } else if ($mimetype == 'audio/x-pn-realaudio-plugin') { | |
629 | // RealMedia file | |
630 | $code = resourcelib_embed_real($url, $title, $clicktoopen); | |
631 | ||
632 | } else { | |
633 | // anything else - just try object tag enlarged as much as possible | |
634 | $code = resourcelib_embed_general($url, $title, $clicktoopen, $mimetype); | |
635 | } | |
636 | ||
637 | return $code; | |
638 | } | |
1e7f8ea2 PS |
639 | |
640 | ||
641 | /** | |
642 | * Abstract class that page type's MUST inherit from. | |
643 | * | |
644 | * This is the abstract class that ALL add page type forms must extend. | |
645 | * You will notice that all but two of the methods this class contains are final. | |
646 | * Essentially the only thing that extending classes can do is extend custom_definition. | |
647 | * OR if it has a special requirement on creation it can extend construction_override | |
648 | * | |
649 | * @abstract | |
cc3dbaaa PS |
650 | * @copyright 2009 Sam Hemelryk |
651 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
1e7f8ea2 PS |
652 | */ |
653 | abstract class lesson_add_page_form_base extends moodleform { | |
654 | ||
655 | /** | |
656 | * This is the classic define that is used to identify this pagetype. | |
657 | * Will be one of LESSON_* | |
658 | * @var int | |
659 | */ | |
660 | public $qtype; | |
661 | ||
662 | /** | |
663 | * The simple string that describes the page type e.g. truefalse, multichoice | |
664 | * @var string | |
665 | */ | |
666 | public $qtypestring; | |
667 | ||
668 | /** | |
669 | * An array of options used in the htmleditor | |
670 | * @var array | |
671 | */ | |
672 | protected $editoroptions = array(); | |
673 | ||
674 | /** | |
675 | * True if this is a standard page of false if it does something special. | |
676 | * Questions are standard pages, branch tables are not | |
677 | * @var bool | |
678 | */ | |
679 | protected $standard = true; | |
680 | ||
681 | /** | |
682 | * Each page type can and should override this to add any custom elements to | |
683 | * the basic form that they want | |
684 | */ | |
685 | public function custom_definition() {} | |
686 | ||
687 | /** | |
688 | * Used to determine if this is a standard page or a special page | |
689 | * @return bool | |
690 | */ | |
691 | public final function is_standard() { | |
692 | return (bool)$this->standard; | |
693 | } | |
694 | ||
695 | /** | |
696 | * Add the required basic elements to the form. | |
697 | * | |
698 | * This method adds the basic elements to the form including title and contents | |
699 | * and then calls custom_definition(); | |
700 | */ | |
701 | public final function definition() { | |
702 | $mform = $this->_form; | |
703 | $editoroptions = $this->_customdata['editoroptions']; | |
704 | ||
705 | $mform->addElement('header', 'qtypeheading', get_string('addaquestionpage', 'lesson', get_string($this->qtypestring, 'lesson'))); | |
706 | ||
707 | $mform->addElement('hidden', 'id'); | |
708 | $mform->setType('id', PARAM_INT); | |
709 | ||
710 | $mform->addElement('hidden', 'pageid'); | |
711 | $mform->setType('pageid', PARAM_INT); | |
712 | ||
713 | if ($this->standard === true) { | |
714 | $mform->addElement('hidden', 'qtype'); | |
3fa2f030 | 715 | $mform->setType('qtype', PARAM_SAFEDIR); |
1e7f8ea2 | 716 | |
3fa2f030 | 717 | $mform->addElement('text', 'title', get_string('pagetitle', 'lesson'), array('size'=>70)); |
1e7f8ea2 | 718 | $mform->setType('title', PARAM_TEXT); |
3fa2f030 PS |
719 | $mform->addRule('title', get_string('required'), 'required', null, 'client'); |
720 | ||
1e7f8ea2 | 721 | $this->editoroptions = array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$this->_customdata['maxbytes']); |
3fa2f030 PS |
722 | $mform->addElement('editor', 'contents_editor', get_string('pagecontents', 'lesson'), null, $this->editoroptions); |
723 | $mform->setType('contents_editor', PARAM_RAW); | |
724 | $mform->addRule('contents_editor', get_string('required'), 'required', null, 'client'); | |
1e7f8ea2 PS |
725 | } |
726 | ||
727 | $this->custom_definition(); | |
728 | ||
729 | if ($this->_customdata['edit'] === true) { | |
730 | $mform->addElement('hidden', 'edit', 1); | |
3fa2f030 | 731 | $this->add_action_buttons(get_string('cancel'), get_string('savepage', 'lesson')); |
1e7f8ea2 | 732 | } else { |
3fa2f030 | 733 | $this->add_action_buttons(get_string('cancel'), get_string('addaquestionpage', 'lesson')); |
1e7f8ea2 PS |
734 | } |
735 | } | |
736 | ||
737 | /** | |
738 | * Convenience function: Adds a jumpto select element | |
739 | * | |
740 | * @param string $name | |
741 | * @param string|null $label | |
742 | * @param int $selected The page to select by default | |
743 | */ | |
744 | protected final function add_jumpto($name, $label=null, $selected=LESSON_NEXTPAGE) { | |
745 | $title = get_string("jump", "lesson"); | |
746 | if ($label === null) { | |
747 | $label = $title; | |
748 | } | |
749 | if (is_int($name)) { | |
750 | $name = "jumpto[$name]"; | |
751 | } | |
752 | $this->_form->addElement('select', $name, $label, $this->_customdata['jumpto']); | |
753 | $this->_form->setDefault($name, $selected); | |
754 | $this->_form->addHelpButton($name, 'jumps', 'lesson'); | |
755 | } | |
756 | ||
757 | /** | |
758 | * Convenience function: Adds a score input element | |
759 | * | |
760 | * @param string $name | |
761 | * @param string|null $label | |
762 | * @param mixed $value The default value | |
763 | */ | |
764 | protected final function add_score($name, $label=null, $value=null) { | |
765 | if ($label === null) { | |
766 | $label = get_string("score", "lesson"); | |
767 | } | |
768 | if (is_int($name)) { | |
769 | $name = "score[$name]"; | |
770 | } | |
771 | $this->_form->addElement('text', $name, $label, array('size'=>5)); | |
772 | if ($value !== null) { | |
773 | $this->_form->setDefault($name, $value); | |
774 | } | |
775 | } | |
776 | ||
777 | /** | |
778 | * Convenience function: Adds an answer editor | |
779 | * | |
780 | * @param int $count The count of the element to add | |
a675ada5 PS |
781 | * @param string $label, NULL means default |
782 | * @param bool $required | |
783 | * @return void | |
1e7f8ea2 | 784 | */ |
a675ada5 PS |
785 | protected final function add_answer($count, $label = NULL, $required = false) { |
786 | if ($label === NULL) { | |
787 | $label = get_string('answer', 'lesson'); | |
788 | } | |
cb2c1963 | 789 | $this->_form->addElement('editor', 'answer_editor['.$count.']', $label, array('rows'=>'4', 'columns'=>'80'), array('noclean'=>true)); |
1e7f8ea2 | 790 | $this->_form->setDefault('answer_editor['.$count.']', array('text'=>'', 'format'=>FORMAT_MOODLE)); |
a675ada5 PS |
791 | if ($required) { |
792 | $this->_form->addRule('answer_editor['.$count.']', get_string('required'), 'required', null, 'client'); | |
793 | } | |
1e7f8ea2 PS |
794 | } |
795 | /** | |
796 | * Convenience function: Adds an response editor | |
797 | * | |
798 | * @param int $count The count of the element to add | |
a675ada5 PS |
799 | * @param string $label, NULL means default |
800 | * @param bool $required | |
801 | * @return void | |
1e7f8ea2 | 802 | */ |
a675ada5 PS |
803 | protected final function add_response($count, $label = NULL, $required = false) { |
804 | if ($label === NULL) { | |
805 | $label = get_string('response', 'lesson'); | |
806 | } | |
cb2c1963 | 807 | $this->_form->addElement('editor', 'response_editor['.$count.']', $label, array('rows'=>'4', 'columns'=>'80'), array('noclean'=>true)); |
1e7f8ea2 | 808 | $this->_form->setDefault('response_editor['.$count.']', array('text'=>'', 'format'=>FORMAT_MOODLE)); |
a675ada5 PS |
809 | if ($required) { |
810 | $this->_form->addRule('response_editor['.$count.']', get_string('required'), 'required', null, 'client'); | |
811 | } | |
1e7f8ea2 PS |
812 | } |
813 | ||
814 | /** | |
815 | * A function that gets called upon init of this object by the calling script. | |
816 | * | |
817 | * This can be used to process an immediate action if required. Currently it | |
818 | * is only used in special cases by non-standard page types. | |
819 | * | |
820 | * @return bool | |
821 | */ | |
822 | public function construction_override() { | |
823 | return true; | |
824 | } | |
825 | } | |
826 | ||
827 | ||
828 | ||
829 | /** | |
830 | * Class representation of a lesson | |
831 | * | |
832 | * This class is used the interact with, and manage a lesson once instantiated. | |
833 | * If you need to fetch a lesson object you can do so by calling | |
834 | * | |
835 | * <code> | |
836 | * lesson::load($lessonid); | |
837 | * // or | |
838 | * $lessonrecord = $DB->get_record('lesson', $lessonid); | |
839 | * $lesson = new lesson($lessonrecord); | |
840 | * </code> | |
841 | * | |
842 | * The class itself extends lesson_base as all classes within the lesson module should | |
843 | * | |
844 | * These properties are from the database | |
845 | * @property int $id The id of this lesson | |
846 | * @property int $course The ID of the course this lesson belongs to | |
847 | * @property string $name The name of this lesson | |
848 | * @property int $practice Flag to toggle this as a practice lesson | |
849 | * @property int $modattempts Toggle to allow the user to go back and review answers | |
850 | * @property int $usepassword Toggle the use of a password for entry | |
851 | * @property string $password The password to require users to enter | |
ff85f902 | 852 | * @property int $dependency ID of another lesson this lesson is dependent on |
1e7f8ea2 PS |
853 | * @property string $conditions Conditions of the lesson dependency |
854 | * @property int $grade The maximum grade a user can achieve (%) | |
855 | * @property int $custom Toggle custom scoring on or off | |
856 | * @property int $ongoing Toggle display of an ongoing score | |
857 | * @property int $usemaxgrade How retakes are handled (max=1, mean=0) | |
858 | * @property int $maxanswers The max number of answers or branches | |
859 | * @property int $maxattempts The maximum number of attempts a user can record | |
860 | * @property int $review Toggle use or wrong answer review button | |
861 | * @property int $nextpagedefault Override the default next page | |
862 | * @property int $feedback Toggles display of default feedback | |
863 | * @property int $minquestions Sets a minimum value of pages seen when calculating grades | |
864 | * @property int $maxpages Maximum number of pages this lesson can contain | |
865 | * @property int $retake Flag to allow users to retake a lesson | |
866 | * @property int $activitylink Relate this lesson to another lesson | |
867 | * @property string $mediafile File to pop up to or webpage to display | |
868 | * @property int $mediaheight Sets the height of the media file popup | |
869 | * @property int $mediawidth Sets the width of the media file popup | |
870 | * @property int $mediaclose Toggle display of a media close button | |
871 | * @property int $slideshow Flag for whether branch pages should be shown as slideshows | |
872 | * @property int $width Width of slideshow | |
873 | * @property int $height Height of slideshow | |
874 | * @property string $bgcolor Background colour of slideshow | |
ff85f902 | 875 | * @property int $displayleft Display a left menu |
1e7f8ea2 PS |
876 | * @property int $displayleftif Sets the condition on which the left menu is displayed |
877 | * @property int $progressbar Flag to toggle display of a lesson progress bar | |
878 | * @property int $highscores Flag to toggle collection of high scores | |
879 | * @property int $maxhighscores Number of high scores to limit to | |
880 | * @property int $available Timestamp of when this lesson becomes available | |
881 | * @property int $deadline Timestamp of when this lesson is no longer available | |
882 | * @property int $timemodified Timestamp when lesson was last modified | |
883 | * | |
884 | * These properties are calculated | |
885 | * @property int $firstpageid Id of the first page of this lesson (prevpageid=0) | |
886 | * @property int $lastpageid Id of the last page of this lesson (nextpageid=0) | |
887 | * | |
cc3dbaaa PS |
888 | * @copyright 2009 Sam Hemelryk |
889 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
1e7f8ea2 PS |
890 | */ |
891 | class lesson extends lesson_base { | |
892 | ||
893 | /** | |
894 | * The id of the first page (where prevpageid = 0) gets set and retrieved by | |
895 | * {@see get_firstpageid()} by directly calling <code>$lesson->firstpageid;</code> | |
896 | * @var int | |
897 | */ | |
898 | protected $firstpageid = null; | |
899 | /** | |
900 | * The id of the last page (where nextpageid = 0) gets set and retrieved by | |
901 | * {@see get_lastpageid()} by directly calling <code>$lesson->lastpageid;</code> | |
902 | * @var int | |
903 | */ | |
904 | protected $lastpageid = null; | |
905 | /** | |
906 | * An array used to cache the pages associated with this lesson after the first | |
907 | * time they have been loaded. | |
908 | * A note to developers: If you are going to be working with MORE than one or | |
909 | * two pages from a lesson you should probably call {@see $lesson->load_all_pages()} | |
910 | * in order to save excess database queries. | |
911 | * @var array An array of lesson_page objects | |
912 | */ | |
913 | protected $pages = array(); | |
914 | /** | |
915 | * Flag that gets set to true once all of the pages associated with the lesson | |
916 | * have been loaded. | |
917 | * @var bool | |
918 | */ | |
919 | protected $loadedallpages = false; | |
920 | ||
921 | /** | |
922 | * Simply generates a lesson object given an array/object of properties | |
923 | * Overrides {@see lesson_base->create()} | |
924 | * @static | |
925 | * @param object|array $properties | |
926 | * @return lesson | |
927 | */ | |
928 | public static function create($properties) { | |
929 | return new lesson($properties); | |
930 | } | |
931 | ||
932 | /** | |
933 | * Generates a lesson object from the database given its id | |
934 | * @static | |
935 | * @param int $lessonid | |
936 | * @return lesson | |
937 | */ | |
938 | public static function load($lessonid) { | |
939 | if (!$lesson = $DB->get_record('lesson', array('id' => $lessonid))) { | |
940 | print_error('invalidcoursemodule'); | |
941 | } | |
942 | return new lesson($lesson); | |
943 | } | |
944 | ||
945 | /** | |
946 | * Deletes this lesson from the database | |
947 | */ | |
948 | public function delete() { | |
949 | global $CFG, $DB; | |
950 | require_once($CFG->libdir.'/gradelib.php'); | |
951 | require_once($CFG->dirroot.'/calendar/lib.php'); | |
952 | ||
953 | $DB->delete_records("lesson", array("id"=>$this->properties->id));; | |
954 | $DB->delete_records("lesson_pages", array("lessonid"=>$this->properties->id)); | |
955 | $DB->delete_records("lesson_answers", array("lessonid"=>$this->properties->id)); | |
956 | $DB->delete_records("lesson_attempts", array("lessonid"=>$this->properties->id)); | |
957 | $DB->delete_records("lesson_grades", array("lessonid"=>$this->properties->id)); | |
958 | $DB->delete_records("lesson_timer", array("lessonid"=>$this->properties->id)); | |
959 | $DB->delete_records("lesson_branch", array("lessonid"=>$this->properties->id)); | |
960 | $DB->delete_records("lesson_high_scores", array("lessonid"=>$this->properties->id)); | |
961 | if ($events = $DB->get_records('event', array("modulename"=>'lesson', "instance"=>$this->properties->id))) { | |
962 | foreach($events as $event) { | |
963 | $event = calendar_event::load($event); | |
964 | $event->delete(); | |
965 | } | |
966 | } | |
967 | ||
968 | grade_update('mod/lesson', $this->properties->course, 'mod', 'lesson', $this->properties->id, 0, NULL, array('deleted'=>1)); | |
969 | return true; | |
970 | } | |
971 | ||
972 | /** | |
973 | * Fetches messages from the session that may have been set in previous page | |
974 | * actions. | |
975 | * | |
976 | * <code> | |
977 | * // Do not call this method directly instead use | |
978 | * $lesson->messages; | |
979 | * </code> | |
980 | * | |
981 | * @return array | |
982 | */ | |
983 | protected function get_messages() { | |
984 | global $SESSION; | |
985 | ||
986 | $messages = array(); | |
987 | if (!empty($SESSION->lesson_messages) && is_array($SESSION->lesson_messages) && array_key_exists($this->properties->id, $SESSION->lesson_messages)) { | |
988 | $messages = $SESSION->lesson_messages[$this->properties->id]; | |
989 | unset($SESSION->lesson_messages[$this->properties->id]); | |
990 | } | |
991 | ||
992 | return $messages; | |
993 | } | |
994 | ||
995 | /** | |
996 | * Get all of the attempts for the current user. | |
997 | * | |
998 | * @param int $retries | |
999 | * @param bool $correct Optional: only fetch correct attempts | |
1000 | * @param int $pageid Optional: only fetch attempts at the given page | |
1001 | * @param int $userid Optional: defaults to the current user if not set | |
1002 | * @return array|false | |
1003 | */ | |
1004 | public function get_attempts($retries, $correct=false, $pageid=null, $userid=null) { | |
1005 | global $USER, $DB; | |
1006 | $params = array("lessonid"=>$this->properties->id, "userid"=>$userid, "retry"=>$retries); | |
1007 | if ($correct) { | |
1008 | $params['correct'] = 1; | |
1009 | } | |
1010 | if ($pageid !== null) { | |
1011 | $params['pageid'] = $pageid; | |
1012 | } | |
1013 | if ($userid === null) { | |
1014 | $params['userid'] = $USER->id; | |
1015 | } | |
ef4ba975 | 1016 | return $DB->get_records('lesson_attempts', $params, 'timeseen ASC'); |
1e7f8ea2 PS |
1017 | } |
1018 | ||
1019 | /** | |
1020 | * Returns the first page for the lesson or false if there isn't one. | |
1021 | * | |
1022 | * This method should be called via the magic method __get(); | |
1023 | * <code> | |
1024 | * $firstpage = $lesson->firstpage; | |
1025 | * </code> | |
1026 | * | |
1027 | * @return lesson_page|bool Returns the lesson_page specialised object or false | |
1028 | */ | |
1029 | protected function get_firstpage() { | |
1030 | $pages = $this->load_all_pages(); | |
1031 | if (count($pages) > 0) { | |
1032 | foreach ($pages as $page) { | |
1033 | if ((int)$page->prevpageid === 0) { | |
1034 | return $page; | |
1035 | } | |
1036 | } | |
1037 | } | |
1038 | return false; | |
1039 | } | |
1040 | ||
1041 | /** | |
1042 | * Returns the last page for the lesson or false if there isn't one. | |
1043 | * | |
1044 | * This method should be called via the magic method __get(); | |
1045 | * <code> | |
1046 | * $lastpage = $lesson->lastpage; | |
1047 | * </code> | |
1048 | * | |
1049 | * @return lesson_page|bool Returns the lesson_page specialised object or false | |
1050 | */ | |
1051 | protected function get_lastpage() { | |
1052 | $pages = $this->load_all_pages(); | |
1053 | if (count($pages) > 0) { | |
1054 | foreach ($pages as $page) { | |
1055 | if ((int)$page->nextpageid === 0) { | |
1056 | return $page; | |
1057 | } | |
1058 | } | |
1059 | } | |
1060 | return false; | |
1061 | } | |
1062 | ||
1063 | /** | |
1064 | * Returns the id of the first page of this lesson. (prevpageid = 0) | |
1065 | * @return int | |
1066 | */ | |
1067 | protected function get_firstpageid() { | |
1068 | global $DB; | |
1069 | if ($this->firstpageid == null) { | |
1070 | if (!$this->loadedallpages) { | |
1071 | $firstpageid = $DB->get_field('lesson_pages', 'id', array('lessonid'=>$this->properties->id, 'prevpageid'=>0)); | |
1072 | if (!$firstpageid) { | |
1073 | print_error('cannotfindfirstpage', 'lesson'); | |
1074 | } | |
1075 | $this->firstpageid = $firstpageid; | |
1076 | } else { | |
1077 | $firstpage = $this->get_firstpage(); | |
1078 | $this->firstpageid = $firstpage->id; | |
1079 | } | |
1080 | } | |
1081 | return $this->firstpageid; | |
1082 | } | |
1083 | ||
1084 | /** | |
1085 | * Returns the id of the last page of this lesson. (nextpageid = 0) | |
1086 | * @return int | |
1087 | */ | |
1088 | public function get_lastpageid() { | |
1089 | global $DB; | |
1090 | if ($this->lastpageid == null) { | |
1091 | if (!$this->loadedallpages) { | |
1092 | $lastpageid = $DB->get_field('lesson_pages', 'id', array('lessonid'=>$this->properties->id, 'nextpageid'=>0)); | |
1093 | if (!$lastpageid) { | |
1094 | print_error('cannotfindlastpage', 'lesson'); | |
1095 | } | |
1096 | $this->lastpageid = $lastpageid; | |
1097 | } else { | |
1098 | $lastpageid = $this->get_lastpage(); | |
1099 | $this->lastpageid = $lastpageid->id; | |
1100 | } | |
1101 | } | |
1102 | ||
1103 | return $this->lastpageid; | |
1104 | } | |
1105 | ||
1106 | /** | |
1107 | * Gets the next page to display after the one that is provided. | |
1108 | * @param int $nextpageid | |
1109 | * @return bool | |
1110 | */ | |
1111 | public function get_next_page($nextpageid) { | |
1112 | global $USER; | |
1113 | $allpages = $this->load_all_pages(); | |
1114 | if ($this->properties->nextpagedefault) { | |
1115 | // in Flash Card mode...first get number of retakes | |
1116 | shuffle($allpages); | |
1117 | $found = false; | |
1118 | if ($this->properties->nextpagedefault == LESSON_UNSEENPAGE) { | |
1119 | foreach ($allpages as $nextpage) { | |
1120 | if (!$DB->count_records("lesson_attempts", array("pageid"=>$nextpage->id, "userid"=>$USER->id, "retry"=>$nretakes))) { | |
1121 | $found = true; | |
1122 | break; | |
1123 | } | |
1124 | } | |
1125 | } elseif ($this->properties->nextpagedefault == LESSON_UNANSWEREDPAGE) { | |
1126 | foreach ($allpages as $nextpage) { | |
1127 | if (!$DB->count_records("lesson_attempts", array('pageid'=>$nextpage->id, 'userid'=>$USER->id, 'correct'=>1, 'retry'=>$nretakes))) { | |
1128 | $found = true; | |
1129 | break; | |
1130 | } | |
1131 | } | |
1132 | } | |
1133 | if ($found) { | |
1134 | if ($this->properties->maxpages) { | |
1135 | // check number of pages viewed (in the lesson) | |
1136 | $nretakes = $DB->count_records("lesson_grades", array("lessonid"=>$this->properties->id, "userid"=>$USER->id)); | |
1137 | if ($DB->count_records("lesson_attempts", array("lessonid"=>$this->properties->id, "userid"=>$USER->id, "retry"=>$nretakes)) >= $this->properties->maxpages) { | |
1138 | return false; | |
1139 | } | |
1140 | } | |
1141 | return $nextpage; | |
1142 | } | |
1143 | } | |
1144 | // In a normal lesson mode | |
1145 | foreach ($allpages as $nextpage) { | |
1146 | if ((int)$nextpage->id===(int)$nextpageid) { | |
1147 | return $nextpage; | |
1148 | } | |
1149 | } | |
1150 | return false; | |
1151 | } | |
1152 | ||
1153 | /** | |
1154 | * Sets a message against the session for this lesson that will displayed next | |
1155 | * time the lesson processes messages | |
1156 | * | |
1157 | * @param string $message | |
1158 | * @param string $class | |
1159 | * @param string $align | |
1160 | * @return bool | |
1161 | */ | |
1162 | public function add_message($message, $class="notifyproblem", $align='center') { | |
1163 | global $SESSION; | |
1164 | ||
1165 | if (empty($SESSION->lesson_messages) || !is_array($SESSION->lesson_messages)) { | |
1166 | $SESSION->lesson_messages = array(); | |
1167 | $SESSION->lesson_messages[$this->properties->id] = array(); | |
1168 | } else if (!array_key_exists($this->properties->id, $SESSION->lesson_messages)) { | |
1169 | $SESSION->lesson_messages[$this->properties->id] = array(); | |
1170 | } | |
1171 | ||
1172 | $SESSION->lesson_messages[$this->properties->id][] = array($message, $class, $align); | |
1173 | ||
1174 | return true; | |
1175 | } | |
1176 | ||
1177 | /** | |
1178 | * Check if the lesson is accessible at the present time | |
1179 | * @return bool True if the lesson is accessible, false otherwise | |
1180 | */ | |
1181 | public function is_accessible() { | |
1182 | $available = $this->properties->available; | |
1183 | $deadline = $this->properties->deadline; | |
1184 | return (($available == 0 || time() >= $available) && ($deadline == 0 || time() < $deadline)); | |
1185 | } | |
1186 | ||
1187 | /** | |
1188 | * Starts the lesson time for the current user | |
1189 | * @return bool Returns true | |
1190 | */ | |
1191 | public function start_timer() { | |
1192 | global $USER, $DB; | |
1193 | $USER->startlesson[$this->properties->id] = true; | |
1194 | $startlesson = new stdClass; | |
1195 | $startlesson->lessonid = $this->properties->id; | |
1196 | $startlesson->userid = $USER->id; | |
1197 | $startlesson->starttime = time(); | |
1198 | $startlesson->lessontime = time(); | |
1199 | $DB->insert_record('lesson_timer', $startlesson); | |
1200 | if ($this->properties->timed) { | |
1201 | $this->add_message(get_string('maxtimewarning', 'lesson', $this->properties->maxtime), 'center'); | |
1202 | } | |
1203 | return true; | |
1204 | } | |
1205 | ||
1206 | /** | |
1207 | * Updates the timer to the current time and returns the new timer object | |
1208 | * @param bool $restart If set to true the timer is restarted | |
1209 | * @param bool $continue If set to true AND $restart=true then the timer | |
1210 | * will continue from a previous attempt | |
1211 | * @return stdClass The new timer | |
1212 | */ | |
1213 | public function update_timer($restart=false, $continue=false) { | |
1214 | global $USER, $DB; | |
1215 | // clock code | |
1216 | // get time information for this user | |
1217 | if (!$timer = $DB->get_records('lesson_timer', array ("lessonid" => $this->properties->id, "userid" => $USER->id), 'starttime DESC', '*', 0, 1)) { | |
1218 | print_error('cannotfindtimer', 'lesson'); | |
1219 | } else { | |
1220 | $timer = current($timer); // this will get the latest start time record | |
1221 | } | |
1222 | ||
1223 | if ($restart) { | |
1224 | if ($continue) { | |
1225 | // continue a previous test, need to update the clock (think this option is disabled atm) | |
1226 | $timer->starttime = time() - ($timer->lessontime - $timer->starttime); | |
1227 | } else { | |
1228 | // starting over, so reset the clock | |
1229 | $timer->starttime = time(); | |
1230 | } | |
1231 | } | |
1232 | ||
1233 | $timer->lessontime = time(); | |
1234 | $DB->update_record('lesson_timer', $timer); | |
1235 | return $timer; | |
1236 | } | |
1237 | ||
1238 | /** | |
1239 | * Updates the timer to the current time then stops it by unsetting the user var | |
1240 | * @return bool Returns true | |
1241 | */ | |
1242 | public function stop_timer() { | |
1243 | global $USER, $DB; | |
1244 | unset($USER->startlesson[$this->properties->id]); | |
1245 | return $this->update_timer(false, false); | |
1246 | } | |
1247 | ||
1248 | /** | |
1249 | * Checks to see if the lesson has pages | |
1250 | */ | |
1251 | public function has_pages() { | |
1252 | global $DB; | |
1253 | $pagecount = $DB->count_records('lesson_pages', array('lessonid'=>$this->properties->id)); | |
1254 | return ($pagecount>0); | |
1255 | } | |
1256 | ||
1257 | /** | |
1258 | * Returns the link for the related activity | |
1259 | * @return array|false | |
1260 | */ | |
1261 | public function link_for_activitylink() { | |
1262 | global $DB; | |
1263 | $module = $DB->get_record('course_modules', array('id' => $this->properties->activitylink)); | |
1264 | if ($module) { | |
1265 | $modname = $DB->get_field('modules', 'name', array('id' => $module->module)); | |
1266 | if ($modname) { | |
1267 | $instancename = $DB->get_field($modname, 'name', array('id' => $module->instance)); | |
1268 | if ($instancename) { | |
1269 | return html_writer::link(new moodle_url('/mod/'.$modname.'/view.php', array('id'=>$this->properties->activitylink)), | |
1270 | get_string('returnto', 'lesson', get_string('activitylinkname', 'lesson', $instancename)), | |
1271 | array('class'=>'centerpadded lessonbutton standardbutton')); | |
1272 | } | |
1273 | } | |
1274 | } | |
1275 | return ''; | |
1276 | } | |
1277 | ||
1278 | /** | |
1279 | * Loads the requested page. | |
1280 | * | |
1281 | * This function will return the requested page id as either a specialised | |
1282 | * lesson_page object OR as a generic lesson_page. | |
1283 | * If the page has been loaded previously it will be returned from the pages | |
1284 | * array, otherwise it will be loaded from the database first | |
1285 | * | |
1286 | * @param int $pageid | |
1287 | * @return lesson_page A lesson_page object or an object that extends it | |
1288 | */ | |
1289 | public function load_page($pageid) { | |
1290 | if (!array_key_exists($pageid, $this->pages)) { | |
1291 | $manager = lesson_page_type_manager::get($this); | |
1292 | $this->pages[$pageid] = $manager->load_page($pageid, $this); | |
1293 | } | |
1294 | return $this->pages[$pageid]; | |
1295 | } | |
1296 | ||
1297 | /** | |
1298 | * Loads ALL of the pages for this lesson | |
1299 | * | |
1300 | * @return array An array containing all pages from this lesson | |
1301 | */ | |
1302 | public function load_all_pages() { | |
1303 | if (!$this->loadedallpages) { | |
1304 | $manager = lesson_page_type_manager::get($this); | |
1305 | $this->pages = $manager->load_all_pages($this); | |
1306 | $this->loadedallpages = true; | |
1307 | } | |
1308 | return $this->pages; | |
1309 | } | |
1310 | ||
1311 | /** | |
ff85f902 | 1312 | * Determines if a jumpto value is correct or not. |
1e7f8ea2 PS |
1313 | * |
1314 | * returns true if jumpto page is (logically) after the pageid page or | |
1315 | * if the jumpto value is a special value. Returns false in all other cases. | |
1316 | * | |
1317 | * @param int $pageid Id of the page from which you are jumping from. | |
1318 | * @param int $jumpto The jumpto number. | |
1319 | * @return boolean True or false after a series of tests. | |
1320 | **/ | |
1321 | public function jumpto_is_correct($pageid, $jumpto) { | |
1322 | global $DB; | |
1323 | ||
1324 | // first test the special values | |
1325 | if (!$jumpto) { | |
1326 | // same page | |
1327 | return false; | |
1328 | } elseif ($jumpto == LESSON_NEXTPAGE) { | |
1329 | return true; | |
1330 | } elseif ($jumpto == LESSON_UNSEENBRANCHPAGE) { | |
1331 | return true; | |
1332 | } elseif ($jumpto == LESSON_RANDOMPAGE) { | |
1333 | return true; | |
1334 | } elseif ($jumpto == LESSON_CLUSTERJUMP) { | |
1335 | return true; | |
1336 | } elseif ($jumpto == LESSON_EOL) { | |
1337 | return true; | |
1338 | } | |
1339 | ||
1340 | $pages = $this->load_all_pages(); | |
1341 | $apageid = $pages[$pageid]->nextpageid; | |
1342 | while ($apageid != 0) { | |
1343 | if ($jumpto == $apageid) { | |
1344 | return true; | |
1345 | } | |
1346 | $apageid = $pages[$apageid]->nextpageid; | |
1347 | } | |
1348 | return false; | |
1349 | } | |
1350 | ||
1351 | /** | |
1352 | * Returns the time a user has remaining on this lesson | |
1353 | * @param int $starttime Starttime timestamp | |
1354 | * @return string | |
1355 | */ | |
1356 | public function time_remaining($starttime) { | |
1357 | $timeleft = $starttime + $this->maxtime * 60 - time(); | |
1358 | $hours = floor($timeleft/3600); | |
1359 | $timeleft = $timeleft - ($hours * 3600); | |
1360 | $minutes = floor($timeleft/60); | |
1361 | $secs = $timeleft - ($minutes * 60); | |
1362 | ||
1363 | if ($minutes < 10) { | |
1364 | $minutes = "0$minutes"; | |
1365 | } | |
1366 | if ($secs < 10) { | |
1367 | $secs = "0$secs"; | |
1368 | } | |
1369 | $output = array(); | |
1370 | $output[] = $hours; | |
1371 | $output[] = $minutes; | |
1372 | $output[] = $secs; | |
1373 | $output = implode(':', $output); | |
1374 | return $output; | |
1375 | } | |
1376 | ||
1377 | /** | |
1378 | * Interprets LESSON_CLUSTERJUMP jumpto value. | |
1379 | * | |
1380 | * This will select a page randomly | |
ff85f902 | 1381 | * and the page selected will be inbetween a cluster page and end of clutter or end of lesson |
1e7f8ea2 PS |
1382 | * and the page selected will be a page that has not been viewed already |
1383 | * and if any pages are within a branch table or end of branch then only 1 page within | |
1384 | * the branch table or end of branch will be randomly selected (sub clustering). | |
1385 | * | |
1386 | * @param int $pageid Id of the current page from which we are jumping from. | |
1387 | * @param int $userid Id of the user. | |
1388 | * @return int The id of the next page. | |
1389 | **/ | |
1390 | public function cluster_jump($pageid, $userid=null) { | |
1391 | global $DB, $USER; | |
1392 | ||
1393 | if ($userid===null) { | |
1394 | $userid = $USER->id; | |
1395 | } | |
1396 | // get the number of retakes | |
1397 | if (!$retakes = $DB->count_records("lesson_grades", array("lessonid"=>$this->properties->id, "userid"=>$userid))) { | |
1398 | $retakes = 0; | |
1399 | } | |
1400 | // get all the lesson_attempts aka what the user has seen | |
1401 | $seenpages = array(); | |
1402 | if ($attempts = $this->get_attempts($retakes)) { | |
1403 | foreach ($attempts as $attempt) { | |
1404 | $seenpages[$attempt->pageid] = $attempt->pageid; | |
1405 | } | |
1406 | ||
1407 | } | |
1408 | ||
1409 | // get the lesson pages | |
1410 | $lessonpages = $this->load_all_pages(); | |
1411 | // find the start of the cluster | |
1412 | while ($pageid != 0) { // this condition should not be satisfied... should be a cluster page | |
1413 | if ($lessonpages[$pageid]->qtype == LESSON_PAGE_CLUSTER) { | |
1414 | break; | |
1415 | } | |
1416 | $pageid = $lessonpages[$pageid]->prevpageid; | |
1417 | } | |
1418 | ||
1419 | $clusterpages = array(); | |
1420 | $clusterpages = $this->get_sub_pages_of($pageid, array(LESSON_PAGE_ENDOFCLUSTER)); | |
1421 | $unseen = array(); | |
1422 | foreach ($clusterpages as $key=>$cluster) { | |
1423 | if ($cluster->type !== lesson_page::TYPE_QUESTION) { | |
1424 | unset($clusterpages[$key]); | |
1425 | } elseif ($cluster->is_unseen($seenpages)) { | |
1426 | $unseen[] = $cluster; | |
1427 | } | |
1428 | } | |
1429 | ||
1430 | if (count($unseen) > 0) { | |
1431 | // it does not contain elements, then use exitjump, otherwise find out next page/branch | |
1432 | $nextpage = $unseen[rand(0, count($unseen)-1)]; | |
1433 | if ($nextpage->qtype == LESSON_PAGE_BRANCHTABLE) { | |
1434 | // if branch table, then pick a random page inside of it | |
1435 | $branchpages = $this->get_sub_pages_of($nextpage->id, array(LESSON_PAGE_BRANCHTABLE, LESSON_PAGE_ENDOFBRANCH)); | |
1436 | return $branchpages[rand(0, count($branchpages)-1)]->id; | |
1437 | } else { // otherwise, return the page's id | |
1438 | return $nextpage->id; | |
1439 | } | |
1440 | } else { | |
1441 | // seen all there is to see, leave the cluster | |
1442 | if (end($clusterpages)->nextpageid == 0) { | |
1443 | return LESSON_EOL; | |
1444 | } else { | |
1445 | $clusterendid = $pageid; | |
1446 | while ($clusterendid != 0) { // this condition should not be satisfied... should be a cluster page | |
1447 | if ($lessonpages[$clusterendid]->qtype == LESSON_PAGE_CLUSTER) { | |
1448 | break; | |
1449 | } | |
1450 | $clusterendid = $lessonpages[$clusterendid]->prevpageid; | |
1451 | } | |
1452 | $exitjump = $DB->get_field("lesson_answers", "jumpto", array("pageid" => $clusterendid, "lessonid" => $this->properties->id)); | |
1453 | if ($exitjump == LESSON_NEXTPAGE) { | |
1454 | $exitjump = $lessonpages[$pageid]->nextpageid; | |
1455 | } | |
1456 | if ($exitjump == 0) { | |
1457 | return LESSON_EOL; | |
1458 | } else if (in_array($exitjump, array(LESSON_EOL, LESSON_PREVIOUSPAGE))) { | |
1459 | return $exitjump; | |
1460 | } else { | |
1461 | if (!array_key_exists($exitjump, $lessonpages)) { | |
1462 | $found = false; | |
1463 | foreach ($lessonpages as $page) { | |
1464 | if ($page->id === $clusterendid) { | |
1465 | $found = true; | |
1466 | } else if ($page->qtype == LESSON_PAGE_ENDOFCLUSTER) { | |
1467 | $exitjump = $DB->get_field("lesson_answers", "jumpto", array("pageid" => $page->id, "lessonid" => $this->properties->id)); | |
1468 | break; | |
1469 | } | |
1470 | } | |
1471 | } | |
1472 | if (!array_key_exists($exitjump, $lessonpages)) { | |
1473 | return LESSON_EOL; | |
1474 | } | |
1475 | return $exitjump; | |
1476 | } | |
1477 | } | |
1478 | } | |
1479 | } | |
1480 | ||
1481 | /** | |
1482 | * Finds all pages that appear to be a subtype of the provided pageid until | |
1483 | * an end point specified within $ends is encountered or no more pages exist | |
1484 | * | |
1485 | * @param int $pageid | |
1486 | * @param array $ends An array of LESSON_PAGE_* types that signify an end of | |
1487 | * the subtype | |
1488 | * @return array An array of specialised lesson_page objects | |
1489 | */ | |
1490 | public function get_sub_pages_of($pageid, array $ends) { | |
1491 | $lessonpages = $this->load_all_pages(); | |
1492 | $pageid = $lessonpages[$pageid]->nextpageid; // move to the first page after the branch table | |
1493 | $pages = array(); | |
1494 | ||
1495 | while (true) { | |
1496 | if ($pageid == 0 || in_array($lessonpages[$pageid]->qtype, $ends)) { | |
1497 | break; | |
1498 | } | |
1499 | $pages[] = $lessonpages[$pageid]; | |
1500 | $pageid = $lessonpages[$pageid]->nextpageid; | |
1501 | } | |
1502 | ||
1503 | return $pages; | |
1504 | } | |
1505 | ||
1506 | /** | |
1507 | * Checks to see if the specified page[id] is a subpage of a type specified in | |
1508 | * the $types array, until either there are no more pages of we find a type | |
ff85f902 | 1509 | * corresponding to that of a type specified in $ends |
1e7f8ea2 PS |
1510 | * |
1511 | * @param int $pageid The id of the page to check | |
1512 | * @param array $types An array of types that would signify this page was a subpage | |
1513 | * @param array $ends An array of types that mean this is not a subpage | |
1514 | * @return bool | |
1515 | */ | |
1516 | public function is_sub_page_of_type($pageid, array $types, array $ends) { | |
1517 | $pages = $this->load_all_pages(); | |
1518 | $pageid = $pages[$pageid]->prevpageid; // move up one | |
1519 | ||
1520 | array_unshift($ends, 0); | |
1521 | // go up the pages till branch table | |
1522 | while (true) { | |
1523 | if ($pageid==0 || in_array($pages[$pageid]->qtype, $ends)) { | |
1524 | return false; | |
1525 | } else if (in_array($pages[$pageid]->qtype, $types)) { | |
1526 | return true; | |
1527 | } | |
1528 | $pageid = $pages[$pageid]->prevpageid; | |
1529 | } | |
1530 | } | |
1531 | } | |
1532 | ||
1533 | ||
1534 | /** | |
1535 | * Abstract class to provide a core functions to the all lesson classes | |
1536 | * | |
1537 | * This class should be abstracted by ALL classes with the lesson module to ensure | |
1538 | * that all classes within this module can be interacted with in the same way. | |
1539 | * | |
1540 | * This class provides the user with a basic properties array that can be fetched | |
ff85f902 | 1541 | * or set via magic methods, or alternatively by defining methods get_blah() or |
1e7f8ea2 PS |
1542 | * set_blah() within the extending object. |
1543 | * | |
cc3dbaaa PS |
1544 | * @copyright 2009 Sam Hemelryk |
1545 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
1e7f8ea2 PS |
1546 | */ |
1547 | abstract class lesson_base { | |
1548 | ||
1549 | /** | |
1550 | * An object containing properties | |
1551 | * @var stdClass | |
1552 | */ | |
1553 | protected $properties; | |
1554 | ||
1555 | /** | |
1556 | * The constructor | |
1557 | * @param stdClass $properties | |
1558 | */ | |
1559 | public function __construct($properties) { | |
1560 | $this->properties = (object)$properties; | |
1561 | } | |
1562 | ||
1563 | /** | |
1564 | * Magic property method | |
1565 | * | |
1566 | * Attempts to call a set_$key method if one exists otherwise falls back | |
1567 | * to simply set the property | |
1568 | * | |
1569 | * @param string $key | |
1570 | * @param mixed $value | |
1571 | */ | |
1572 | public function __set($key, $value) { | |
1573 | if (method_exists($this, 'set_'.$key)) { | |
1574 | $this->{'set_'.$key}($value); | |
1575 | } | |
1576 | $this->properties->{$key} = $value; | |
1577 | } | |
1578 | ||
1579 | /** | |
1580 | * Magic get method | |
1581 | * | |
1582 | * Attempts to call a get_$key method to return the property and ralls over | |
1583 | * to return the raw property | |
1584 | * | |
1585 | * @param str $key | |
1586 | * @return mixed | |
1587 | */ | |
1588 | public function __get($key) { | |
1589 | if (method_exists($this, 'get_'.$key)) { | |
1590 | return $this->{'get_'.$key}(); | |
1591 | } | |
1592 | return $this->properties->{$key}; | |
1593 | } | |
1594 | ||
1595 | /** | |
1596 | * Stupid PHP needs an isset magic method if you use the get magic method and | |
1597 | * still want empty calls to work.... blah ~! | |
1598 | * | |
1599 | * @param string $key | |
1600 | * @return bool | |
1601 | */ | |
1602 | public function __isset($key) { | |
1603 | if (method_exists($this, 'get_'.$key)) { | |
1604 | $val = $this->{'get_'.$key}(); | |
1605 | return !empty($val); | |
1606 | } | |
1607 | return !empty($this->properties->{$key}); | |
1608 | } | |
1609 | ||
1610 | /** | |
ff85f902 | 1611 | * If overridden should create a new instance, save it in the DB and return it |
1e7f8ea2 PS |
1612 | */ |
1613 | public static function create() {} | |
1614 | /** | |
ff85f902 | 1615 | * If overridden should load an instance from the DB and return it |
1e7f8ea2 PS |
1616 | */ |
1617 | public static function load() {} | |
1618 | /** | |
1619 | * Fetches all of the properties of the object | |
1620 | * @return stdClass | |
1621 | */ | |
1622 | public function properties() { | |
1623 | return $this->properties; | |
1624 | } | |
1625 | } | |
1626 | ||
1627 | ||
1628 | /** | |
1629 | * Abstract class representation of a page associated with a lesson. | |
1630 | * | |
1631 | * This class should MUST be extended by all specialised page types defined in | |
1632 | * mod/lesson/pagetypes/. | |
1633 | * There are a handful of abstract methods that need to be defined as well as | |
1634 | * severl methods that can optionally be defined in order to make the page type | |
1635 | * operate in the desired way | |
1636 | * | |
1637 | * Database properties | |
1638 | * @property int $id The id of this lesson page | |
1639 | * @property int $lessonid The id of the lesson this page belongs to | |
1640 | * @property int $prevpageid The id of the page before this one | |
1641 | * @property int $nextpageid The id of the next page in the page sequence | |
1642 | * @property int $qtype Identifies the page type of this page | |
1643 | * @property int $qoption Used to record page type specific options | |
1644 | * @property int $layout Used to record page specific layout selections | |
1645 | * @property int $display Used to record page specific display selections | |
1646 | * @property int $timecreated Timestamp for when the page was created | |
1647 | * @property int $timemodified Timestamp for when the page was last modified | |
1648 | * @property string $title The title of this page | |
1649 | * @property string $contents The rich content shown to describe the page | |
1650 | * @property int $contentsformat The format of the contents field | |
1651 | * | |
1652 | * Calculated properties | |
1653 | * @property-read array $answers An array of answers for this page | |
1654 | * @property-read bool $displayinmenublock Toggles display in the left menu block | |
1655 | * @property-read array $jumps An array containing all the jumps this page uses | |
1656 | * @property-read lesson $lesson The lesson this page belongs to | |
1657 | * @property-read int $type The type of the page [question | structure] | |
1658 | * @property-read typeid The unique identifier for the page type | |
1659 | * @property-read typestring The string that describes this page type | |
1660 | * | |
1661 | * @abstract | |
cc3dbaaa PS |
1662 | * @copyright 2009 Sam Hemelryk |
1663 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
1e7f8ea2 PS |
1664 | */ |
1665 | abstract class lesson_page extends lesson_base { | |
1666 | ||
1667 | /** | |
1668 | * A reference to the lesson this page belongs to | |
1669 | * @var lesson | |
1670 | */ | |
1671 | protected $lesson = null; | |
1672 | /** | |
1673 | * Contains the answers to this lesson_page once loaded | |
1674 | * @var null|array | |
1675 | */ | |
1676 | protected $answers = null; | |
1677 | /** | |
1678 | * This sets the type of the page, can be one of the constants defined below | |
1679 | * @var int | |
1680 | */ | |
1681 | protected $type = 0; | |
1682 | ||
1683 | /** | |
1684 | * Constants used to identify the type of the page | |
1685 | */ | |
1686 | const TYPE_QUESTION = 0; | |
1687 | const TYPE_STRUCTURE = 1; | |
1688 | ||
1689 | /** | |
1690 | * This method should return the integer used to identify the page type within | |
ff85f902 | 1691 | * the database and throughout code. This maps back to the defines used in 1.x |
1e7f8ea2 PS |
1692 | * @abstract |
1693 | * @return int | |
1694 | */ | |
1695 | abstract protected function get_typeid(); | |
1696 | /** | |
1697 | * This method should return the string that describes the pagetype | |
1698 | * @abstract | |
1699 | * @return string | |
1700 | */ | |
1701 | abstract protected function get_typestring(); | |
1702 | ||
1703 | /** | |
1704 | * This method gets called to display the page to the user taking the lesson | |
1705 | * @abstract | |
1706 | * @param object $renderer | |
1707 | * @param object $attempt | |
1708 | * @return string | |
1709 | */ | |
1710 | abstract public function display($renderer, $attempt); | |
1711 | ||
1712 | /** | |
1713 | * Creates a new lesson_page within the database and returns the correct pagetype | |
1714 | * object to use to interact with the new lesson | |
1715 | * | |
1716 | * @final | |
1717 | * @static | |
1718 | * @param object $properties | |
1719 | * @param lesson $lesson | |
1720 | * @return lesson_page Specialised object that extends lesson_page | |
1721 | */ | |
1722 | final public static function create($properties, lesson $lesson, $context, $maxbytes) { | |
1723 | global $DB; | |
1724 | $newpage = new stdClass; | |
1725 | $newpage->title = $properties->title; | |
1726 | $newpage->contents = $properties->contents_editor['text']; | |
1727 | $newpage->contentsformat = $properties->contents_editor['format']; | |
1728 | $newpage->lessonid = $lesson->id; | |
1729 | $newpage->timecreated = time(); | |
1730 | $newpage->qtype = $properties->qtype; | |
1731 | $newpage->qoption = (isset($properties->qoption))?1:0; | |
1732 | $newpage->layout = (isset($properties->layout))?1:0; | |
1733 | $newpage->display = (isset($properties->display))?1:0; | |
1734 | $newpage->prevpageid = 0; // this is a first page | |
1735 | $newpage->nextpageid = 0; // this is the only page | |
1736 | ||
1737 | if ($properties->pageid) { | |
1738 | $prevpage = $DB->get_record("lesson_pages", array("id" => $properties->pageid), 'id, nextpageid'); | |
1739 | if (!$prevpage) { | |
1740 | print_error('cannotfindpages', 'lesson'); | |
1741 | } | |
1742 | $newpage->prevpageid = $prevpage->id; | |
1743 | $newpage->nextpageid = $prevpage->nextpageid; | |
1744 | } else { | |
1745 | $nextpage = $DB->get_record('lesson_pages', array('lessonid'=>$lesson->id, 'prevpageid'=>0), 'id'); | |
1746 | if ($nextpage) { | |
1747 | // This is the first page, there are existing pages put this at the start | |
1748 | $newpage->nextpageid = $nextpage->id; | |
1749 | } | |
1750 | } | |
1751 | ||
1752 | $newpage->id = $DB->insert_record("lesson_pages", $newpage); | |
1753 | ||
1754 | $editor = new stdClass; | |
1755 | $editor->id = $newpage->id; | |
1756 | $editor->contents_editor = $properties->contents_editor; | |
1757 | $editor = file_postupdate_standard_editor($editor, 'contents', array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$maxbytes), $context, 'mod_lesson', 'page_contents', $editor->id); | |
1758 | $DB->update_record("lesson_pages", $editor); | |
1759 | ||
1760 | if ($newpage->prevpageid > 0) { | |
1761 | $DB->set_field("lesson_pages", "nextpageid", $newpage->id, array("id" => $newpage->prevpageid)); | |
1762 | } | |
1763 | if ($newpage->nextpageid > 0) { | |
1764 | $DB->set_field("lesson_pages", "prevpageid", $newpage->id, array("id" => $newpage->nextpageid)); | |
1765 | } | |
1766 | ||
1767 | $page = lesson_page::load($newpage, $lesson); | |
1768 | $page->create_answers($properties); | |
1769 | ||
1770 | $lesson->add_message(get_string('insertedpage', 'lesson').': '.format_string($newpage->title, true), 'notifysuccess'); | |
1771 | ||
1772 | return $page; | |
1773 | } | |
1774 | ||
1775 | /** | |
1776 | * This method loads a page object from the database and returns it as a | |
1777 | * specialised object that extends lesson_page | |
1778 | * | |
1779 | * @final | |
1780 | * @static | |
1781 | * @param int $id | |
1782 | * @param lesson $lesson | |
1783 | * @return lesson_page Specialised lesson_page object | |
1784 | */ | |
1785 | final public static function load($id, lesson $lesson) { | |
1786 | global $DB; | |
1787 | ||
1788 | if (is_object($id) && !empty($id->qtype)) { | |
1789 | $page = $id; | |
1790 | } else { | |
1791 | $page = $DB->get_record("lesson_pages", array("id" => $id)); | |
1792 | if (!$page) { | |
1793 | print_error('cannotfindpages', 'lesson'); | |
1794 | } | |
1795 | } | |
1796 | $manager = lesson_page_type_manager::get($lesson); | |
1797 | ||
1798 | $class = 'lesson_page_type_'.$manager->get_page_type_idstring($page->qtype); | |
1799 | if (!class_exists($class)) { | |
1800 | $class = 'lesson_page'; | |
1801 | } | |
1802 | ||
1803 | return new $class($page, $lesson); | |
1804 | } | |
1805 | ||
1806 | /** | |
1807 | * Deletes a lesson_page from the database as well as any associated records. | |
1808 | * @final | |
1809 | * @return bool | |
1810 | */ | |
1811 | final public function delete() { | |
1812 | global $DB; | |
1813 | // first delete all the associated records... | |
1814 | $DB->delete_records("lesson_attempts", array("pageid" => $this->properties->id)); | |
1815 | // ...now delete the answers... | |
1816 | $DB->delete_records("lesson_answers", array("pageid" => $this->properties->id)); | |
1817 | // ..and the page itself | |
1818 | $DB->delete_records("lesson_pages", array("id" => $this->properties->id)); | |
1819 | ||
1820 | // repair the hole in the linkage | |
1821 | if (!$this->properties->prevpageid && !$this->properties->nextpageid) { | |
1822 | //This is the only page, no repair needed | |
1823 | } elseif (!$this->properties->prevpageid) { | |
1824 | // this is the first page... | |
1825 | $page = $this->lesson->load_page($this->properties->nextpageid); | |
1826 | $page->move(null, 0); | |
1827 | } elseif (!$this->properties->nextpageid) { | |
1828 | // this is the last page... | |
1829 | $page = $this->lesson->load_page($this->properties->prevpageid); | |
1830 | $page->move(0); | |
1831 | } else { | |
1832 | // page is in the middle... | |
1833 | $prevpage = $this->lesson->load_page($this->properties->prevpageid); | |
1834 | $nextpage = $this->lesson->load_page($this->properties->nextpageid); | |
1835 | ||
1836 | $prevpage->move($nextpage->id); | |
1837 | $nextpage->move(null, $prevpage->id); | |
1838 | } | |
1839 | return true; | |
1840 | } | |
1841 | ||
1842 | /** | |
1843 | * Moves a page by updating its nextpageid and prevpageid values within | |
1844 | * the database | |
1845 | * | |
1846 | * @final | |
1847 | * @param int $nextpageid | |
1848 | * @param int $prevpageid | |
1849 | */ | |
1850 | final public function move($nextpageid=null, $prevpageid=null) { | |
1851 | global $DB; | |
1852 | if ($nextpageid === null) { | |
1853 | $nextpageid = $this->properties->nextpageid; | |
1854 | } | |
1855 | if ($prevpageid === null) { | |
1856 | $prevpageid = $this->properties->prevpageid; | |
1857 | } | |
1858 | $obj = new stdClass; | |
1859 | $obj->id = $this->properties->id; | |
1860 | $obj->prevpageid = $prevpageid; | |
1861 | $obj->nextpageid = $nextpageid; | |
1862 | $DB->update_record('lesson_pages', $obj); | |
1863 | } | |
1864 | ||
1865 | /** | |
1866 | * Returns the answers that are associated with this page in the database | |
1867 | * | |
1868 | * @final | |
1869 | * @return array | |
1870 | */ | |
1871 | final public function get_answers() { | |
1872 | global $DB; | |
1873 | if ($this->answers === null) { | |
1874 | $this->answers = array(); | |
1875 | $answers = $DB->get_records('lesson_answers', array('pageid'=>$this->properties->id, 'lessonid'=>$this->lesson->id), 'id'); | |
1876 | if (!$answers) { | |
1877 | debugging(get_string('cannotfindanswer', 'lesson')); | |
1878 | return array(); | |
1879 | } | |
1880 | foreach ($answers as $answer) { | |
1881 | $this->answers[count($this->answers)] = new lesson_page_answer($answer); | |
1882 | } | |
1883 | } | |
1884 | return $this->answers; | |
1885 | } | |
1886 | ||
1887 | /** | |
1888 | * Returns the lesson this page is associated with | |
1889 | * @final | |
1890 | * @return lesson | |
1891 | */ | |
1892 | final protected function get_lesson() { | |
1893 | return $this->lesson; | |
1894 | } | |
1895 | ||
1896 | /** | |
1897 | * Returns the type of page this is. Not to be confused with page type | |
1898 | * @final | |
1899 | * @return int | |
1900 | */ | |
1901 | final protected function get_type() { | |
1902 | return $this->type; | |
1903 | } | |
1904 | ||
1905 | /** | |
1906 | * Records an attempt at this page | |
1907 | * | |
1908 | * @final | |
1909 | * @param stdClass $context | |
1910 | * @return stdClass Returns the result of the attempt | |
1911 | */ | |
1912 | final public function record_attempt($context) { | |
1913 | global $DB, $USER, $OUTPUT; | |
1914 | ||
1915 | /** | |
ff85f902 | 1916 | * This should be overridden by each page type to actually check the response |
1e7f8ea2 PS |
1917 | * against what ever custom criteria they have defined |
1918 | */ | |
1919 | $result = $this->check_answer(); | |
1920 | ||
1921 | $result->attemptsremaining = 0; | |
1922 | $result->maxattemptsreached = false; | |
1923 | ||
1924 | if ($result->noanswer) { | |
1925 | $result->newpageid = $this->properties->id; // display same page again | |
1926 | $result->feedback = get_string('noanswer', 'lesson'); | |
1927 | } else { | |
1928 | if (!has_capability('mod/lesson:manage', $context)) { | |
1929 | $nretakes = $DB->count_records("lesson_grades", array("lessonid"=>$this->lesson->id, "userid"=>$USER->id)); | |
1930 | // record student's attempt | |
1931 | $attempt = new stdClass; | |
1932 | $attempt->lessonid = $this->lesson->id; | |
1933 | $attempt->pageid = $this->properties->id; | |
1934 | $attempt->userid = $USER->id; | |
1935 | $attempt->answerid = $result->answerid; | |
1936 | $attempt->retry = $nretakes; | |
1937 | $attempt->correct = $result->correctanswer; | |
1938 | if($result->userresponse !== null) { | |
1939 | $attempt->useranswer = $result->userresponse; | |
1940 | } | |
1941 | ||
1942 | $attempt->timeseen = time(); | |
1943 | // if allow modattempts, then update the old attempt record, otherwise, insert new answer record | |
1944 | if (isset($USER->modattempts[$this->lesson->id])) { | |
1945 | $attempt->retry = $nretakes - 1; // they are going through on review, $nretakes will be too high | |
1946 | } | |
1947 | ||
1948 | $DB->insert_record("lesson_attempts", $attempt); | |
1949 | // "number of attempts remaining" message if $this->lesson->maxattempts > 1 | |
1950 | // displaying of message(s) is at the end of page for more ergonomic display | |
1951 | if (!$result->correctanswer && ($result->newpageid == 0)) { | |
1952 | // wrong answer and student is stuck on this page - check how many attempts | |
1953 | // the student has had at this page/question | |
1954 | $nattempts = $DB->count_records("lesson_attempts", array("pageid"=>$this->properties->id, "userid"=>$USER->id),"retry", $nretakes); | |
1955 | // retreive the number of attempts left counter for displaying at bottom of feedback page | |
1956 | if ($nattempts >= $this->lesson->maxattempts) { | |
1957 | if ($this->lesson->maxattempts > 1) { // don't bother with message if only one attempt | |
1958 | $result->maxattemptsreached = true; | |
1959 | } | |
1960 | $result->newpageid = LESSON_NEXTPAGE; | |
1961 | } else if ($this->lesson->maxattempts > 1) { // don't bother with message if only one attempt | |
1962 | $result->attemptsremaining = $this->lesson->maxattempts - $nattempts; | |
1963 | } | |
1964 | } | |
1965 | } | |
1966 | // TODO: merge this code with the jump code below. Convert jumpto page into a proper page id | |
1967 | if ($result->newpageid == 0) { | |
1968 | $result->newpageid = $this->properties->id; | |
1969 | } elseif ($result->newpageid == LESSON_NEXTPAGE) { | |
1970 | $nextpage = $this->lesson->get_next_page($this->properties->nextpageid); | |
1971 | if ($nextpage === false) { | |
1972 | $result->newpageid = LESSON_EOL; | |
1973 | } else { | |
1974 | $result->newpageid = $nextpage->id; | |
1975 | } | |
1976 | } | |
1977 | ||
1978 | // Determine default feedback if necessary | |
1979 | if (empty($result->response)) { | |
1980 | if (!$this->lesson->feedback && !$result->noanswer && !($this->lesson->review & !$result->correctanswer && !$result->isessayquestion)) { | |
1981 | // These conditions have been met: | |
1982 | // 1. The lesson manager has not supplied feedback to the student | |
1983 | // 2. Not displaying default feedback | |
1984 | // 3. The user did provide an answer | |
1985 | // 4. We are not reviewing with an incorrect answer (and not reviewing an essay question) | |
1986 | ||
1987 | $result->nodefaultresponse = true; // This will cause a redirect below | |
1988 | } else if ($result->isessayquestion) { | |
1989 | $result->response = get_string('defaultessayresponse', 'lesson'); | |
1990 | } else if ($result->correctanswer) { | |
1991 | $result->response = get_string('thatsthecorrectanswer', 'lesson'); | |
1992 | } else { | |
1993 | $result->response = get_string('thatsthewronganswer', 'lesson'); | |
1994 | } | |
1995 | } | |
1996 | ||
1997 | if ($result->response) { | |
1998 | if ($this->lesson->review && !$result->correctanswer && !$result->isessayquestion) { | |
1999 | $nretakes = $DB->count_records("lesson_grades", array("lessonid"=>$this->lesson->id, "userid"=>$USER->id)); | |
2000 | $qattempts = $DB->count_records("lesson_attempts", array("userid"=>$USER->id, "retry"=>$nretakes, "pageid"=>$this->properties->id)); | |
2001 | if ($qattempts == 1) { | |
2002 | $result->feedback = $OUTPUT->box(get_string("firstwrong", "lesson"), 'feedback'); | |
2003 | } else { | |
2004 | $result->feedback = $OUTPUT->BOX(get_string("secondpluswrong", "lesson"), 'feedback'); | |
2005 | } | |
2006 | } else { | |
2007 | $class = 'response'; | |
2008 | if ($result->correctanswer) { | |
2009 | $class .= ' correct'; //CSS over-ride this if they exist (!important) | |
2010 | } else if (!$result->isessayquestion) { | |
2011 | $class .= ' incorrect'; //CSS over-ride this if they exist (!important) | |
2012 | } | |
2013 | $options = new stdClass; | |
2014 | $options->noclean = true; | |
2015 | $options->para = true; | |
2016 | $result->feedback = $OUTPUT->box(format_text($this->properties->contents, $this->properties->contentsformat, $options), 'generalbox boxaligncenter'); | |
2017 | $result->feedback .= '<div class="correctanswer generalbox"><em>'.get_string("youranswer", "lesson").'</em> : '.$result->studentanswer; // already in clean html | |
2018 | $result->feedback .= $OUTPUT->box($result->response, $class); // already conerted to HTML | |
2019 | echo "</div>"; | |
2020 | } | |
2021 | } | |
2022 | } | |
2023 | ||
2024 | return $result; | |
2025 | } | |
2026 | ||
2027 | /** | |
2028 | * Returns the string for a jump name | |
2029 | * | |
2030 | * @final | |
2031 | * @param int $jumpto Jump code or page ID | |
2032 | * @return string | |
2033 | **/ | |
2034 | final protected function get_jump_name($jumpto) { | |
2035 | global $DB; | |
2036 | static $jumpnames = array(); | |
2037 | ||
2038 | if (!array_key_exists($jumpto, $jumpnames)) { | |
44cb7e63 | 2039 | if ($jumpto == LESSON_THISPAGE) { |
1e7f8ea2 PS |
2040 | $jumptitle = get_string('thispage', 'lesson'); |
2041 | } elseif ($jumpto == LESSON_NEXTPAGE) { | |
2042 | $jumptitle = get_string('nextpage', 'lesson'); | |
2043 | } elseif ($jumpto == LESSON_EOL) { | |
2044 | $jumptitle = get_string('endoflesson', 'lesson'); | |
2045 | } elseif ($jumpto == LESSON_UNSEENBRANCHPAGE) { | |
2046 | $jumptitle = get_string('unseenpageinbranch', 'lesson'); | |
2047 | } elseif ($jumpto == LESSON_PREVIOUSPAGE) { | |
2048 | $jumptitle = get_string('previouspage', 'lesson'); | |
2049 | } elseif ($jumpto == LESSON_RANDOMPAGE) { | |
2050 | $jumptitle = get_string('randompageinbranch', 'lesson'); | |
2051 | } elseif ($jumpto == LESSON_RANDOMBRANCH) { | |
2052 | $jumptitle = get_string('randombranch', 'lesson'); | |
2053 | } elseif ($jumpto == LESSON_CLUSTERJUMP) { | |
2054 | $jumptitle = get_string('clusterjump', 'lesson'); | |
2055 | } else { | |
2056 | if (!$jumptitle = $DB->get_field('lesson_pages', 'title', array('id' => $jumpto))) { | |
2057 | $jumptitle = '<strong>'.get_string('notdefined', 'lesson').'</strong>'; | |
2058 | } | |
2059 | } | |
2060 | $jumpnames[$jumpto] = format_string($jumptitle,true); | |
2061 | } | |
2062 | ||
2063 | return $jumpnames[$jumpto]; | |
2064 | } | |
2065 | ||
2066 | /** | |
ff85f902 | 2067 | * Constructor method |
1e7f8ea2 PS |
2068 | * @param object $properties |
2069 | * @param lesson $lesson | |
2070 | */ | |
2071 | public function __construct($properties, lesson $lesson) { | |
2072 | parent::__construct($properties); | |
2073 | $this->lesson = $lesson; | |
2074 | } | |
2075 | ||
2076 | /** | |
2077 | * Returns the score for the attempt | |
ff85f902 | 2078 | * This may be overridden by page types that require manual grading |
1e7f8ea2 PS |
2079 | * @param array $answers |
2080 | * @param object $attempt | |
2081 | * @return int | |
2082 | */ | |
2083 | public function earned_score($answers, $attempt) { | |
2084 | return $answers[$attempt->answerid]->score; | |
2085 | } | |
2086 | ||
2087 | /** | |
2088 | * This is a callback method that can be override and gets called when ever a page | |
2089 | * is viewed | |
2090 | * | |
2091 | * @param bool $canmanage True if the user has the manage cap | |
2092 | * @return mixed | |
2093 | */ | |
2094 | public function callback_on_view($canmanage) { | |
2095 | return true; | |
2096 | } | |
2097 | ||
2098 | /** | |
2099 | * Updates a lesson page and its answers within the database | |
2100 | * | |
2101 | * @param object $properties | |
2102 | * @return bool | |
2103 | */ | |
2104 | public function update($properties, $context = null, $maxbytes = null) { | |
7b30b24b | 2105 | global $DB, $PAGE; |
1e7f8ea2 PS |
2106 | $answers = $this->get_answers(); |
2107 | $properties->id = $this->properties->id; | |
2108 | $properties->lessonid = $this->lesson->id; | |
2109 | if (empty($properties->qoption)) { | |
2110 | $properties->qoption = '0'; | |
2111 | } | |
2112 | if (empty($context)) { | |
2113 | $context = $PAGE->context; | |
2114 | } | |
2115 | if ($maxbytes === null) { | |
2116 | $maxbytes =get_max_upload_file_size(); | |
2117 | } | |
2118 | $properties = file_postupdate_standard_editor($properties, 'contents', array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$maxbytes), $context, 'mod_lesson', 'page_contents', $properties->id); | |
2119 | $DB->update_record("lesson_pages", $properties); | |
2120 | ||
2121 | for ($i = 0; $i < $this->lesson->maxanswers; $i++) { | |
2122 | if (!array_key_exists($i, $this->answers)) { | |
2123 | $this->answers[$i] = new stdClass; | |
2124 | $this->answers[$i]->lessonid = $this->lesson->id; | |
2125 | $this->answers[$i]->pageid = $this->id; | |
2126 | $this->answers[$i]->timecreated = $this->timecreated; | |
2127 | } | |
2128 | if (!empty($properties->answer_editor[$i])) { | |
2129 | $this->answers[$i]->answer = $properties->answer_editor[$i]['text']; | |
2130 | $this->answers[$i]->answerformat = $properties->answer_editor[$i]['format']; | |
2131 | if (isset($properties->response_editor[$i])) { | |
2132 | $this->answers[$i]->response = $properties->response_editor[$i]['text']; | |
2133 | $this->answers[$i]->responseformat = $properties->response_editor[$i]['format']; | |
2134 | } | |
2135 | if (isset($properties->jumpto[$i])) { | |
2136 | $this->answers[$i]->jumpto = $properties->jumpto[$i]; | |
2137 | } | |
2138 | if ($this->lesson->custom && isset($properties->score[$i])) { | |
2139 | $this->answers[$i]->score = $properties->score[$i]; | |
2140 | } | |
2141 | if (!isset($this->answers[$i]->id)) { | |
2142 | $this->answers[$i]->id = $DB->insert_record("lesson_answers", $this->answers[$i]); | |
2143 | } else { | |
2144 | $DB->update_record("lesson_answers", $this->answers[$i]->properties()); | |
2145 | } | |
2146 | ||
2147 | } else { | |
2148 | break; | |
2149 | } | |
2150 | } | |
2151 | return true; | |
2152 | } | |
2153 | ||
2154 | /** | |
2155 | * Can be set to true if the page requires a static link to create a new instance | |
2156 | * instead of simply being included in the dropdown | |
2157 | * @param int $previd | |
2158 | * @return bool | |
2159 | */ | |
2160 | public function add_page_link($previd) { | |
2161 | return false; | |
2162 | } | |
2163 | ||
2164 | /** | |
2165 | * Returns true if a page has been viewed before | |
2166 | * | |
2167 | * @param array|int $param Either an array of pages that have been seen or the | |
2168 | * number of retakes a user has had | |
2169 | * @return bool | |
2170 | */ | |
2171 | public function is_unseen($param) { | |
2172 | global $USER, $DB; | |
2173 | if (is_array($param)) { | |
2174 | $seenpages = $param; | |
2175 | return (!array_key_exists($this->properties->id, $seenpages)); | |
2176 | } else { | |
2177 | $nretakes = $param; | |
2178 | if (!$DB->count_records("lesson_attempts", array("pageid"=>$this->properties->id, "userid"=>$USER->id, "retry"=>$nretakes))) { | |
2179 | return true; | |
2180 | } | |
2181 | } | |
2182 | return false; | |
2183 | } | |
2184 | ||
2185 | /** | |
2186 | * Checks to see if a page has been answered previously | |
2187 | * @param int $nretakes | |
2188 | * @return bool | |
2189 | */ | |
2190 | public function is_unanswered($nretakes) { | |
2191 | global $DB, $USER; | |
2192 | if (!$DB->count_records("lesson_attempts", array('pageid'=>$this->properties->id, 'userid'=>$USER->id, 'correct'=>1, 'retry'=>$nretakes))) { | |
2193 | return true; | |
2194 | } | |
2195 | return false; | |
2196 | } | |
2197 | ||
2198 | /** | |
2199 | * Creates answers within the database for this lesson_page. Usually only ever | |
2200 | * called when creating a new page instance | |
2201 | * @param object $properties | |
2202 | * @return array | |
2203 | */ | |
2204 | public function create_answers($properties) { | |
2205 | global $DB; | |
2206 | // now add the answers | |
2207 | $newanswer = new stdClass; | |
2208 | $newanswer->lessonid = $this->lesson->id; | |
2209 | $newanswer->pageid = $this->properties->id; | |
2210 | $newanswer->timecreated = $this->properties->timecreated; | |
2211 | ||
2212 | $answers = array(); | |
2213 | ||
2214 | for ($i = 0; $i < $this->lesson->maxanswers; $i++) { | |
2215 | $answer = clone($newanswer); | |
2216 | if (!empty($properties->answer_editor[$i])) { | |
2217 | $answer->answer = $properties->answer_editor[$i]['text']; | |
2218 | $answer->answerformat = $properties->answer_editor[$i]['format']; | |
2219 | if (isset($properties->response_editor[$i])) { | |
2220 | $answer->response = $properties->response_editor[$i]['text']; | |
2221 | $answer->responseformat = $properties->response_editor[$i]['format']; | |
2222 | } | |
2223 | if (isset($properties->jumpto[$i])) { | |
2224 | $answer->jumpto = $properties->jumpto[$i]; | |
2225 | } | |
2226 | if ($this->lesson->custom && isset($properties->score[$i])) { | |
2227 | $answer->score = $properties->score[$i]; | |
2228 | } | |
2229 | $answer->id = $DB->insert_record("lesson_answers", $answer); | |
2230 | $answers[$answer->id] = new lesson_page_answer($answer); | |
2231 | } else { | |
2232 | break; | |
2233 | } | |
2234 | } | |
2235 | ||
2236 | $this->answers = $answers; | |
2237 | return $answers; | |
2238 | } | |
2239 | ||
2240 | /** | |
ff85f902 | 2241 | * This method MUST be overridden by all question page types, or page types that |
1e7f8ea2 PS |
2242 | * wish to score a page. |
2243 | * | |
2244 | * The structure of result should always be the same so it is a good idea when | |
2245 | * overriding this method on a page type to call | |
2246 | * <code> | |
2247 | * $result = parent::check_answer(); | |
2248 | * </code> | |
ff85f902 | 2249 | * before modifying it as required. |
1e7f8ea2 PS |
2250 | * |
2251 | * @return stdClass | |
2252 | */ | |
2253 | public function check_answer() { | |
2254 | $result = new stdClass; | |
2255 | $result->answerid = 0; | |
2256 | $result->noanswer = false; | |
2257 | $result->correctanswer = false; | |
2258 | $result->isessayquestion = false; // use this to turn off review button on essay questions | |
2259 | $result->response = ''; | |
2260 | $result->newpageid = 0; // stay on the page | |
2261 | $result->studentanswer = ''; // use this to store student's answer(s) in order to display it on feedback page | |
2262 | $result->userresponse = null; | |
2263 | $result->feedback = ''; | |
2264 | $result->nodefaultresponse = false; // Flag for redirecting when default feedback is turned off | |
2265 | return $result; | |
2266 | } | |
2267 | ||
2268 | /** | |
2269 | * True if the page uses a custom option | |
2270 | * | |
2271 | * Should be override and set to true if the page uses a custom option. | |
2272 | * | |
2273 | * @return bool | |
2274 | */ | |
2275 | public function has_option() { | |
2276 | return false; | |
2277 | } | |
2278 | ||
2279 | /** | |
2280 | * Returns the maximum number of answers for this page given the maximum number | |
2281 | * of answers permitted by the lesson. | |
2282 | * | |
2283 | * @param int $default | |
2284 | * @return int | |
2285 | */ | |
2286 | public function max_answers($default) { | |
2287 | return $default; | |
2288 | } | |
2289 | ||
2290 | /** | |
2291 | * Returns the properties of this lesson page as an object | |
2292 | * @return stdClass; | |
2293 | */ | |
2294 | public function properties() { | |
2295 | $properties = clone($this->properties); | |
2296 | if ($this->answers === null) { | |
2297 | $this->get_answers(); | |
2298 | } | |
2299 | if (count($this->answers)>0) { | |
2300 | $count = 0; | |
2301 | foreach ($this->answers as $answer) { | |
2302 | $properties->{'answer_editor['.$count.']'} = array('text'=>$answer->answer, 'format'=>$answer->answerformat); | |
2303 | $properties->{'response_editor['.$count.']'} = array('text'=>$answer->response, 'format'=>$answer->responseformat); | |
2304 | $properties->{'jumpto['.$count.']'} = $answer->jumpto; | |
2305 | $properties->{'score['.$count.']'} = $answer->score; | |
2306 | $count++; | |
2307 | } | |
2308 | } | |
2309 | return $properties; | |
2310 | } | |
2311 | ||
2312 | /** | |
ff85f902 | 2313 | * Returns an array of options to display when choosing the jumpto for a page/answer |
1e7f8ea2 PS |
2314 | * @static |
2315 | * @param int $pageid | |
2316 | * @param lesson $lesson | |
2317 | * @return array | |
2318 | */ | |
2319 | public static function get_jumptooptions($pageid, lesson $lesson) { | |
2320 | global $DB; | |
2321 | $jump = array(); | |
2322 | $jump[0] = get_string("thispage", "lesson"); | |
2323 | $jump[LESSON_NEXTPAGE] = get_string("nextpage", "lesson"); | |
2324 | $jump[LESSON_PREVIOUSPAGE] = get_string("previouspage", "lesson"); | |
2325 | $jump[LESSON_EOL] = get_string("endoflesson", "lesson"); | |
2326 | ||
2327 | if ($pageid == 0) { | |
2328 | return $jump; | |
2329 | } | |
2330 | ||
2331 | $pages = $lesson->load_all_pages(); | |
2332 | if ($pages[$pageid]->qtype == LESSON_PAGE_BRANCHTABLE || $lesson->is_sub_page_of_type($pageid, array(LESSON_PAGE_BRANCHTABLE), array(LESSON_PAGE_ENDOFBRANCH, LESSON_PAGE_CLUSTER))) { | |
2333 | $jump[LESSON_UNSEENBRANCHPAGE] = get_string("unseenpageinbranch", "lesson"); | |
2334 | $jump[LESSON_RANDOMPAGE] = get_string("randompageinbranch", "lesson"); | |
2335 | } | |
2336 | if($pages[$pageid]->qtype == LESSON_PAGE_CLUSTER || $lesson->is_sub_page_of_type($pageid, array(LESSON_PAGE_CLUSTER), array(LESSON_PAGE_ENDOFCLUSTER))) { | |
2337 | $jump[LESSON_CLUSTERJUMP] = get_string("clusterjump", "lesson"); | |
2338 | } | |
2339 | if (!optional_param('firstpage', 0, PARAM_INT)) { | |
2340 | $apageid = $DB->get_field("lesson_pages", "id", array("lessonid" => $lesson->id, "prevpageid" => 0)); | |
2341 | while (true) { | |
2342 | if ($apageid) { | |
2343 | $title = $DB->get_field("lesson_pages", "title", array("id" => $apageid)); | |
2344 | $jump[$apageid] = strip_tags(format_string($title,true)); | |
2345 | $apageid = $DB->get_field("lesson_pages", "nextpageid", array("id" => $apageid)); | |
2346 | } else { | |
2347 | // last page reached | |
2348 | break; | |
2349 | } | |
2350 | } | |
2351 | } | |
2352 | return $jump; | |
2353 | } | |
2354 | /** | |
2355 | * Returns the contents field for the page properly formatted and with plugin | |
2356 | * file url's converted | |
2357 | * @return string | |
2358 | */ | |
2359 | public function get_contents() { | |
2360 | global $PAGE; | |
2361 | if (!empty($this->properties->contents)) { | |
2362 | if (!isset($this->properties->contentsformat)) { | |
2363 | $this->properties->contentsformat = FORMAT_HTML; | |
2364 | } | |
2365 | $context = get_context_instance(CONTEXT_MODULE, $PAGE->cm->id); | |
6999d9cf AD |
2366 | $contents = format_text($this->properties->contents); //format text so glossary autolinking happens |
2367 | return file_rewrite_pluginfile_urls($contents, 'pluginfile.php', $context->id, 'mod_lesson', 'page_contents', $this->properties->id); | |
1e7f8ea2 PS |
2368 | } else { |
2369 | return ''; | |
2370 | } | |
2371 | } | |
2372 | ||
2373 | /** | |
2374 | * Set to true if this page should display in the menu block | |
2375 | * @return bool | |
2376 | */ | |
2377 | protected function get_displayinmenublock() { | |
2378 | return false; | |
2379 | } | |
2380 | ||
2381 | /** | |
2382 | * Get the string that describes the options of this page type | |
2383 | * @return string | |
2384 | */ | |
2385 | public function option_description_string() { | |
2386 | return ''; | |
2387 | } | |
2388 | ||
2389 | /** | |
2390 | * Updates a table with the answers for this page | |
2391 | * @param html_table $table | |
2392 | * @return html_table | |
2393 | */ | |
2394 | public function display_answers(html_table $table) { | |
2395 | $answers = $this->get_answers(); | |
2396 | $i = 1; | |
2397 | foreach ($answers as $answer) { | |
2398 | $cells = array(); | |
2399 | $cells[] = "<span class=\"label\">".get_string("jump", "lesson")." $i<span>: "; | |
2400 | $cells[] = $this->get_jump_name($answer->jumpto); | |
2401 | $table->data[] = new html_table_row($cells); | |
2402 | if ($i === 1){ | |
2403 | $table->data[count($table->data)-1]->cells[0]->style = 'width:20%;'; | |
2404 | } | |
2405 | $i++; | |
2406 | } | |
2407 | return $table; | |
2408 | } | |
2409 | ||
2410 | /** | |
2411 | * Determines if this page should be grayed out on the management/report screens | |
2412 | * @return int 0 or 1 | |
2413 | */ | |
2414 | protected function get_grayout() { | |
2415 | return 0; | |
2416 | } | |
2417 | ||
2418 | /** | |
2419 | * Adds stats for this page to the &pagestats object. This should be defined | |
2420 | * for all page types that grade | |
2421 | * @param array $pagestats | |
2422 | * @param int $tries | |
2423 | * @return bool | |
2424 | */ | |
2425 | public function stats(array &$pagestats, $tries) { | |
2426 | return true; | |
2427 | } | |
2428 | ||
2429 | /** | |
2430 | * Formats the answers of this page for a report | |
2431 | * | |
2432 | * @param object $answerpage | |
2433 | * @param object $answerdata | |
2434 | * @param object $useranswer | |
2435 | * @param array $pagestats | |
2436 | * @param int $i Count of first level answers | |
2437 | * @param int $n Count of second level answers | |
2438 | * @return object The answer page for this | |
2439 | */ | |
2440 | public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) { | |
2441 | $answers = $this->get_answers(); | |
2442 | $formattextdefoptions = new stdClass; | |
2443 | $formattextdefoptions->para = false; //I'll use it widely in this page | |
2444 | foreach ($answers as $answer) { | |
2445 | $data = get_string('jumpsto', 'lesson', $this->get_jump_name($answer->jumpto)); | |
2446 | $answerdata->answers[] = array($data, ""); | |
2447 | $answerpage->answerdata = $answerdata; | |
2448 | } | |
2449 | return $answerpage; | |
2450 | } | |
2451 | ||
2452 | /** | |
2453 | * Gets an array of the jumps used by the answers of this page | |
2454 | * | |
2455 | * @return array | |
2456 | */ | |
2457 | public function get_jumps() { | |
2458 | global $DB; | |
2459 | $jumps = array(); | |
2460 | $params = array ("lessonid" => $this->lesson->id, "pageid" => $this->properties->id); | |
2461 | if ($answers = $this->get_answers()) { | |
2462 | foreach ($answers as $answer) { | |
2463 | $jumps[] = $this->get_jump_name($answer->jumpto); | |
2464 | } | |
2465 | } | |
2466 | return $jumps; | |
2467 | } | |
2468 | /** | |
2469 | * Informs whether this page type require manual grading or not | |
2470 | * @return bool | |
2471 | */ | |
2472 | public function requires_manual_grading() { | |
2473 | return false; | |
2474 | } | |
2475 | ||
2476 | /** | |
2477 | * A callback method that allows a page to override the next page a user will | |
2478 | * see during when this page is being completed. | |
2479 | * @return false|int | |
2480 | */ | |
2481 | public function override_next_page() { | |
2482 | return false; | |
2483 | } | |
2484 | ||
2485 | /** | |
2486 | * This method is used to determine if this page is a valid page | |
2487 | * | |
2488 | * @param array $validpages | |
2489 | * @param array $pageviews | |
2490 | * @return int The next page id to check | |
2491 | */ | |
2492 | public function valid_page_and_view(&$validpages, &$pageviews) { | |
2493 | $validpages[$this->properties->id] = 1; | |
2494 | return $this->properties->nextpageid; | |
2495 | } | |
2496 | } | |
2497 | ||
2498 | ||
2499 | ||
2500 | /** | |
2501 | * Class used to represent an answer to a page | |
2502 | * | |
2503 | * @property int $id The ID of this answer in the database | |
2504 | * @property int $lessonid The ID of the lesson this answer belongs to | |
2505 | * @property int $pageid The ID of the page this answer belongs to | |
2506 | * @property int $jumpto Identifies where the user goes upon completing a page with this answer | |
2507 | * @property int $grade The grade this answer is worth | |
2508 | * @property int $score The score this answer will give | |
2509 | * @property int $flags Used to store options for the answer | |
2510 | * @property int $timecreated A timestamp of when the answer was created | |
2511 | * @property int $timemodified A timestamp of when the answer was modified | |
2512 | * @property string $answer The answer itself | |
2513 | * @property string $response The response the user sees if selecting this answer | |
2514 | * | |
cc3dbaaa PS |
2515 | * @copyright 2009 Sam Hemelryk |
2516 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
1e7f8ea2 PS |
2517 | */ |
2518 | class lesson_page_answer extends lesson_base { | |
2519 | ||
2520 | /** | |
2521 | * Loads an page answer from the DB | |
2522 | * | |
2523 | * @param int $id | |
2524 | * @return lesson_page_answer | |
2525 | */ | |
2526 | public static function load($id) { | |
2527 | global $DB; | |
2528 | $answer = $DB->get_record("lesson_answers", array("id" => $id)); | |
2529 | return new lesson_page_answer($answer); | |
2530 | } | |
2531 | ||
2532 | /** | |
2533 | * Given an object of properties and a page created answer(s) and saves them | |
2534 | * in the database. | |
2535 | * | |
2536 | * @param stdClass $properties | |
2537 | * @param lesson_page $page | |
2538 | * @return array | |
2539 | */ | |
2540 | public static function create($properties, lesson_page $page) { | |
2541 | return $page->create_answers($properties); | |
2542 | } | |
2543 | ||
2544 | } | |
2545 | ||
2546 | /** | |
2547 | * A management class for page types | |
2548 | * | |
2549 | * This class is responsible for managing the different pages. A manager object can | |
2550 | * be retrieved by calling the following line of code: | |
2551 | * <code> | |
2552 | * $manager = lesson_page_type_manager::get($lesson); | |
2553 | * </code> | |
2554 | * The first time the page type manager is retrieved the it includes all of the | |
2555 | * different page types located in mod/lesson/pagetypes. | |
2556 | * | |
cc3dbaaa PS |
2557 | * @copyright 2009 Sam Hemelryk |
2558 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
1e7f8ea2 PS |
2559 | */ |
2560 | class lesson_page_type_manager { | |
2561 | ||
2562 | /** | |
2563 | * An array of different page type classes | |
2564 | * @var array | |
2565 | */ | |
2566 | protected $types = array(); | |
2567 | ||
2568 | /** | |
2569 | * Retrieves the lesson page type manager object | |
2570 | * | |
2571 | * If the object hasn't yet been created it is created here. | |
2572 | * | |
2573 | * @staticvar lesson_page_type_manager $pagetypemanager | |
2574 | * @param lesson $lesson | |
2575 | * @return lesson_page_type_manager | |
2576 | */ | |
2577 | public static function get(lesson $lesson) { | |
2578 | static $pagetypemanager; | |
2579 | if (!($pagetypemanager instanceof lesson_page_type_manager)) { | |
2580 | $pagetypemanager = new lesson_page_type_manager(); | |
2581 | $pagetypemanager->load_lesson_types($lesson); | |
2582 | } | |
2583 | return $pagetypemanager; | |
2584 | } | |
2585 | ||
2586 | /** | |
2587 | * Finds and loads all lesson page types in mod/lesson/pagetypes | |
2588 | * | |
2589 | * @param lesson $lesson | |
2590 | */ | |
2591 | public function load_lesson_types(lesson $lesson) { | |
2592 | global $CFG; | |
2593 | $basedir = $CFG->dirroot.'/mod/lesson/pagetypes/'; | |
2594 | $dir = dir($basedir); | |
2595 | while (false !== ($entry = $dir->read())) { | |
2596 | if (strpos($entry, '.')===0 || !preg_match('#^[a-zA-Z]+\.php#i', $entry)) { | |
2597 | continue; | |
2598 | } | |
2599 | require_once($basedir.$entry); | |
2600 | $class = 'lesson_page_type_'.strtok($entry,'.'); | |
2601 | if (class_exists($class)) { | |
2602 | $pagetype = new $class(new stdClass, $lesson); | |
2603 | $this->types[$pagetype->typeid] = $pagetype; | |
2604 | } | |
2605 | } | |
2606 | ||
2607 | } | |
2608 | ||
2609 | /** | |
2610 | * Returns an array of strings to describe the loaded page types | |
2611 | * | |
2612 | * @param int $type Can be used to return JUST the string for the requested type | |
2613 | * @return array | |
2614 | */ | |
2615 | public function get_page_type_strings($type=null, $special=true) { | |
2616 | $types = array(); | |
2617 | foreach ($this->types as $pagetype) { | |
2618 | if (($type===null || $pagetype->type===$type) && ($special===true || $pagetype->is_standard())) { | |
2619 | $types[$pagetype->typeid] = $pagetype->typestring; | |
2620 | } | |
2621 | } | |
2622 | return $types; | |
2623 | } | |
2624 | ||
2625 | /** | |
2626 | * Returns the basic string used to identify a page type provided with an id | |
2627 | * | |
2628 | * This string can be used to instantiate or identify the page type class. | |
2629 | * If the page type id is unknown then 'unknown' is returned | |
2630 | * | |
2631 | * @param int $id | |
2632 | * @return string | |
2633 | */ | |
2634 | public function get_page_type_idstring($id) { | |
2635 | foreach ($this->types as $pagetype) { | |
2636 | if ((int)$pagetype->typeid === (int)$id) { | |
2637 | return $pagetype->idstring; | |
2638 | } | |
2639 | } | |
2640 | return 'unknown'; | |
2641 | } | |
2642 | ||
2643 | /** | |
2644 | * Loads a page for the provided lesson given it's id | |
2645 | * | |
2646 | * This function loads a page from the lesson when given both the lesson it belongs | |
2647 | * to as well as the page's id. | |
2648 | * If the page doesn't exist an error is thrown | |
2649 | * | |
2650 | * @param int $pageid The id of the page to load | |
2651 | * @param lesson $lesson The lesson the page belongs to | |
2652 | * @return lesson_page A class that extends lesson_page | |
2653 | */ | |
2654 | public function load_page($pageid, lesson $lesson) { | |
2655 | global $DB; | |
2656 | if (!($page =$DB->get_record('lesson_pages', array('id'=>$pageid, 'lessonid'=>$lesson->id)))) { | |
2657 | print_error('cannotfindpages', 'lesson'); | |
2658 | } | |
2659 | $pagetype = get_class($this->types[$page->qtype]); | |
2660 | $page = new $pagetype($page, $lesson); | |
2661 | return $page; | |
2662 | } | |
2663 | ||
2664 | /** | |
2665 | * This function loads ALL pages that belong to the lesson. | |
2666 | * | |
2667 | * @param lesson $lesson | |
2668 | * @return array An array of lesson_page_type_* | |
2669 | */ | |
2670 | public function load_all_pages(lesson $lesson) { | |
2671 | global $DB; | |
2672 | if (!($pages =$DB->get_records('lesson_pages', array('lessonid'=>$lesson->id)))) { | |
2673 | print_error('cannotfindpages', 'lesson'); | |
2674 | } | |
2675 | foreach ($pages as $key=>$page) { | |
2676 | $pagetype = get_class($this->types[$page->qtype]); | |
2677 | $pages[$key] = new $pagetype($page, $lesson); | |
2678 | } | |
2679 | ||
2680 | $orderedpages = array(); | |
2681 | $lastpageid = 0; | |
2682 | ||
2683 | while (true) { | |
2684 | foreach ($pages as $page) { | |
2685 | if ((int)$page->prevpageid === (int)$lastpageid) { | |
2686 | $orderedpages[$page->id] = $page; | |
2687 | unset($pages[$page->id]); | |
2688 | $lastpageid = $page->id; | |
2689 | if ((int)$page->nextpageid===0) { | |
2690 | break 2; | |
2691 | } else { | |
2692 | break 1; | |
2693 | } | |
2694 | } | |
2695 | } | |
2696 | } | |
2697 | ||
2698 | return $orderedpages; | |
2699 | } | |
2700 | ||
2701 | /** | |
ff85f902 | 2702 | * Fetches an mform that can be used to create/edit an page |
1e7f8ea2 PS |
2703 | * |
2704 | * @param int $type The id for the page type | |
2705 | * @param array $arguments Any arguments to pass to the mform | |
2706 | * @return lesson_add_page_form_base | |
2707 | */ | |
2708 | public function get_page_form($type, $arguments) { | |
2709 | $class = 'lesson_add_page_form_'.$this->get_page_type_idstring($type); | |
2710 | if (!class_exists($class) || get_parent_class($class)!=='lesson_add_page_form_base') { | |
2711 | debugging('Lesson page type unknown class requested '.$class, DEBUG_DEVELOPER); | |
2712 | $class = 'lesson_add_page_form_selection'; | |
2713 | } else if ($class === 'lesson_add_page_form_unknown') { | |
2714 | $class = 'lesson_add_page_form_selection'; | |
2715 | } | |
2716 | return new $class(null, $arguments); | |
2717 | } | |
2718 | ||
2719 | /** | |
2720 | * Returns an array of links to use as add page links | |
2721 | * @param int $previd The id of the previous page | |
2722 | * @return array | |
2723 | */ | |
2724 | public function get_add_page_type_links($previd) { | |
2725 | global $OUTPUT; | |
2726 | ||
2727 | $links = array(); | |
2728 | ||
2729 | foreach ($this->types as $key=>$type) { | |
2730 | if ($link = $type->add_page_link($previd)) { | |
2731 | $links[$key] = $link; | |
2732 | } | |
2733 | } | |
2734 | ||
2735 | return $links; | |
2736 | } | |
2737 | } |