PageRenderTime 59ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/mod/lesson/locallib.php

http://github.com/moodle/moodle
PHP | 5316 lines | 3200 code | 523 blank | 1593 comment | 778 complexity | 507a1aa103f1ddeb90222f9e8511e21f MD5 | raw file
Possible License(s): MIT, AGPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, Apache-2.0, LGPL-2.1, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * Local library file for Lesson. These are non-standard functions that are used
  18. * only by Lesson.
  19. *
  20. * @package mod_lesson
  21. * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
  22. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or late
  23. **/
  24. /** Make sure this isn't being directly accessed */
  25. defined('MOODLE_INTERNAL') || die();
  26. /** Include the files that are required by this module */
  27. require_once($CFG->dirroot.'/course/moodleform_mod.php');
  28. require_once($CFG->dirroot . '/mod/lesson/lib.php');
  29. require_once($CFG->libdir . '/filelib.php');
  30. /** This page */
  31. define('LESSON_THISPAGE', 0);
  32. /** Next page -> any page not seen before */
  33. define("LESSON_UNSEENPAGE", 1);
  34. /** Next page -> any page not answered correctly */
  35. define("LESSON_UNANSWEREDPAGE", 2);
  36. /** Jump to Next Page */
  37. define("LESSON_NEXTPAGE", -1);
  38. /** End of Lesson */
  39. define("LESSON_EOL", -9);
  40. /** Jump to an unseen page within a branch and end of branch or end of lesson */
  41. define("LESSON_UNSEENBRANCHPAGE", -50);
  42. /** Jump to Previous Page */
  43. define("LESSON_PREVIOUSPAGE", -40);
  44. /** Jump to a random page within a branch and end of branch or end of lesson */
  45. define("LESSON_RANDOMPAGE", -60);
  46. /** Jump to a random Branch */
  47. define("LESSON_RANDOMBRANCH", -70);
  48. /** Cluster Jump */
  49. define("LESSON_CLUSTERJUMP", -80);
  50. /** Undefined */
  51. define("LESSON_UNDEFINED", -99);
  52. /** LESSON_MAX_EVENT_LENGTH = 432000 ; 5 days maximum */
  53. define("LESSON_MAX_EVENT_LENGTH", "432000");
  54. /** Answer format is HTML */
  55. define("LESSON_ANSWER_HTML", "HTML");
  56. /** Placeholder answer for all other answers. */
  57. define("LESSON_OTHER_ANSWERS", "@#wronganswer#@");
  58. //////////////////////////////////////////////////////////////////////////////////////
  59. /// Any other lesson functions go here. Each of them must have a name that
  60. /// starts with lesson_
  61. /**
  62. * Checks to see if a LESSON_CLUSTERJUMP or
  63. * a LESSON_UNSEENBRANCHPAGE is used in a lesson.
  64. *
  65. * This function is only executed when a teacher is
  66. * checking the navigation for a lesson.
  67. *
  68. * @param stdClass $lesson Id of the lesson that is to be checked.
  69. * @return boolean True or false.
  70. **/
  71. function lesson_display_teacher_warning($lesson) {
  72. global $DB;
  73. // get all of the lesson answers
  74. $params = array ("lessonid" => $lesson->id);
  75. if (!$lessonanswers = $DB->get_records_select("lesson_answers", "lessonid = :lessonid", $params)) {
  76. // no answers, then not using cluster or unseen
  77. return false;
  78. }
  79. // just check for the first one that fulfills the requirements
  80. foreach ($lessonanswers as $lessonanswer) {
  81. if ($lessonanswer->jumpto == LESSON_CLUSTERJUMP || $lessonanswer->jumpto == LESSON_UNSEENBRANCHPAGE) {
  82. return true;
  83. }
  84. }
  85. // if no answers use either of the two jumps
  86. return false;
  87. }
  88. /**
  89. * Interprets the LESSON_UNSEENBRANCHPAGE jump.
  90. *
  91. * will return the pageid of a random unseen page that is within a branch
  92. *
  93. * @param lesson $lesson
  94. * @param int $userid Id of the user.
  95. * @param int $pageid Id of the page from which we are jumping.
  96. * @return int Id of the next page.
  97. **/
  98. function lesson_unseen_question_jump($lesson, $user, $pageid) {
  99. global $DB;
  100. // get the number of retakes
  101. if (!$retakes = $DB->count_records("lesson_grades", array("lessonid"=>$lesson->id, "userid"=>$user))) {
  102. $retakes = 0;
  103. }
  104. // get all the lesson_attempts aka what the user has seen
  105. if ($viewedpages = $DB->get_records("lesson_attempts", array("lessonid"=>$lesson->id, "userid"=>$user, "retry"=>$retakes), "timeseen DESC")) {
  106. foreach($viewedpages as $viewed) {
  107. $seenpages[] = $viewed->pageid;
  108. }
  109. } else {
  110. $seenpages = array();
  111. }
  112. // get the lesson pages
  113. $lessonpages = $lesson->load_all_pages();
  114. if ($pageid == LESSON_UNSEENBRANCHPAGE) { // this only happens when a student leaves in the middle of an unseen question within a branch series
  115. $pageid = $seenpages[0]; // just change the pageid to the last page viewed inside the branch table
  116. }
  117. // go up the pages till branch table
  118. while ($pageid != 0) { // this condition should never be satisfied... only happens if there are no branch tables above this page
  119. if ($lessonpages[$pageid]->qtype == LESSON_PAGE_BRANCHTABLE) {
  120. break;
  121. }
  122. $pageid = $lessonpages[$pageid]->prevpageid;
  123. }
  124. $pagesinbranch = $lesson->get_sub_pages_of($pageid, array(LESSON_PAGE_BRANCHTABLE, LESSON_PAGE_ENDOFBRANCH));
  125. // this foreach loop stores all the pages that are within the branch table but are not in the $seenpages array
  126. $unseen = array();
  127. foreach($pagesinbranch as $page) {
  128. if (!in_array($page->id, $seenpages)) {
  129. $unseen[] = $page->id;
  130. }
  131. }
  132. if(count($unseen) == 0) {
  133. if(isset($pagesinbranch)) {
  134. $temp = end($pagesinbranch);
  135. $nextpage = $temp->nextpageid; // they have seen all the pages in the branch, so go to EOB/next branch table/EOL
  136. } else {
  137. // there are no pages inside the branch, so return the next page
  138. $nextpage = $lessonpages[$pageid]->nextpageid;
  139. }
  140. if ($nextpage == 0) {
  141. return LESSON_EOL;
  142. } else {
  143. return $nextpage;
  144. }
  145. } else {
  146. return $unseen[rand(0, count($unseen)-1)]; // returns a random page id for the next page
  147. }
  148. }
  149. /**
  150. * Handles the unseen branch table jump.
  151. *
  152. * @param lesson $lesson
  153. * @param int $userid User id.
  154. * @return int Will return the page id of a branch table or end of lesson
  155. **/
  156. function lesson_unseen_branch_jump($lesson, $userid) {
  157. global $DB;
  158. if (!$retakes = $DB->count_records("lesson_grades", array("lessonid"=>$lesson->id, "userid"=>$userid))) {
  159. $retakes = 0;
  160. }
  161. if (!$seenbranches = $lesson->get_content_pages_viewed($retakes, $userid, 'timeseen DESC')) {
  162. print_error('cannotfindrecords', 'lesson');
  163. }
  164. // get the lesson pages
  165. $lessonpages = $lesson->load_all_pages();
  166. // this loads all the viewed branch tables into $seen until it finds the branch table with the flag
  167. // which is the branch table that starts the unseenbranch function
  168. $seen = array();
  169. foreach ($seenbranches as $seenbranch) {
  170. if (!$seenbranch->flag) {
  171. $seen[$seenbranch->pageid] = $seenbranch->pageid;
  172. } else {
  173. $start = $seenbranch->pageid;
  174. break;
  175. }
  176. }
  177. // this function searches through the lesson pages to find all the branch tables
  178. // that follow the flagged branch table
  179. $pageid = $lessonpages[$start]->nextpageid; // move down from the flagged branch table
  180. $branchtables = array();
  181. while ($pageid != 0) { // grab all of the branch table till eol
  182. if ($lessonpages[$pageid]->qtype == LESSON_PAGE_BRANCHTABLE) {
  183. $branchtables[] = $lessonpages[$pageid]->id;
  184. }
  185. $pageid = $lessonpages[$pageid]->nextpageid;
  186. }
  187. $unseen = array();
  188. foreach ($branchtables as $branchtable) {
  189. // load all of the unseen branch tables into unseen
  190. if (!array_key_exists($branchtable, $seen)) {
  191. $unseen[] = $branchtable;
  192. }
  193. }
  194. if (count($unseen) > 0) {
  195. return $unseen[rand(0, count($unseen)-1)]; // returns a random page id for the next page
  196. } else {
  197. return LESSON_EOL; // has viewed all of the branch tables
  198. }
  199. }
  200. /**
  201. * Handles the random jump between a branch table and end of branch or end of lesson (LESSON_RANDOMPAGE).
  202. *
  203. * @param lesson $lesson
  204. * @param int $pageid The id of the page that we are jumping from (?)
  205. * @return int The pageid of a random page that is within a branch table
  206. **/
  207. function lesson_random_question_jump($lesson, $pageid) {
  208. global $DB;
  209. // get the lesson pages
  210. $params = array ("lessonid" => $lesson->id);
  211. if (!$lessonpages = $DB->get_records_select("lesson_pages", "lessonid = :lessonid", $params)) {
  212. print_error('cannotfindpages', 'lesson');
  213. }
  214. // go up the pages till branch table
  215. while ($pageid != 0) { // this condition should never be satisfied... only happens if there are no branch tables above this page
  216. if ($lessonpages[$pageid]->qtype == LESSON_PAGE_BRANCHTABLE) {
  217. break;
  218. }
  219. $pageid = $lessonpages[$pageid]->prevpageid;
  220. }
  221. // get the pages within the branch
  222. $pagesinbranch = $lesson->get_sub_pages_of($pageid, array(LESSON_PAGE_BRANCHTABLE, LESSON_PAGE_ENDOFBRANCH));
  223. if(count($pagesinbranch) == 0) {
  224. // there are no pages inside the branch, so return the next page
  225. return $lessonpages[$pageid]->nextpageid;
  226. } else {
  227. return $pagesinbranch[rand(0, count($pagesinbranch)-1)]->id; // returns a random page id for the next page
  228. }
  229. }
  230. /**
  231. * Calculates a user's grade for a lesson.
  232. *
  233. * @param object $lesson The lesson that the user is taking.
  234. * @param int $retries The attempt number.
  235. * @param int $userid Id of the user (optional, default current user).
  236. * @return object { nquestions => number of questions answered
  237. attempts => number of question attempts
  238. total => max points possible
  239. earned => points earned by student
  240. grade => calculated percentage grade
  241. nmanual => number of manually graded questions
  242. manualpoints => point value for manually graded questions }
  243. */
  244. function lesson_grade($lesson, $ntries, $userid = 0) {
  245. global $USER, $DB;
  246. if (empty($userid)) {
  247. $userid = $USER->id;
  248. }
  249. // Zero out everything
  250. $ncorrect = 0;
  251. $nviewed = 0;
  252. $score = 0;
  253. $nmanual = 0;
  254. $manualpoints = 0;
  255. $thegrade = 0;
  256. $nquestions = 0;
  257. $total = 0;
  258. $earned = 0;
  259. $params = array ("lessonid" => $lesson->id, "userid" => $userid, "retry" => $ntries);
  260. if ($useranswers = $DB->get_records_select("lesson_attempts", "lessonid = :lessonid AND
  261. userid = :userid AND retry = :retry", $params, "timeseen")) {
  262. // group each try with its page
  263. $attemptset = array();
  264. foreach ($useranswers as $useranswer) {
  265. $attemptset[$useranswer->pageid][] = $useranswer;
  266. }
  267. // Drop all attempts that go beyond max attempts for the lesson
  268. foreach ($attemptset as $key => $set) {
  269. $attemptset[$key] = array_slice($set, 0, $lesson->maxattempts);
  270. }
  271. // get only the pages and their answers that the user answered
  272. list($usql, $parameters) = $DB->get_in_or_equal(array_keys($attemptset));
  273. array_unshift($parameters, $lesson->id);
  274. $pages = $DB->get_records_select("lesson_pages", "lessonid = ? AND id $usql", $parameters);
  275. $answers = $DB->get_records_select("lesson_answers", "lessonid = ? AND pageid $usql", $parameters);
  276. // Number of pages answered
  277. $nquestions = count($pages);
  278. foreach ($attemptset as $attempts) {
  279. $page = lesson_page::load($pages[end($attempts)->pageid], $lesson);
  280. if ($lesson->custom) {
  281. $attempt = end($attempts);
  282. // If essay question, handle it, otherwise add to score
  283. if ($page->requires_manual_grading()) {
  284. $useranswerobj = unserialize($attempt->useranswer);
  285. if (isset($useranswerobj->score)) {
  286. $earned += $useranswerobj->score;
  287. }
  288. $nmanual++;
  289. $manualpoints += $answers[$attempt->answerid]->score;
  290. } else if (!empty($attempt->answerid)) {
  291. $earned += $page->earned_score($answers, $attempt);
  292. }
  293. } else {
  294. foreach ($attempts as $attempt) {
  295. $earned += $attempt->correct;
  296. }
  297. $attempt = end($attempts); // doesn't matter which one
  298. // If essay question, increase numbers
  299. if ($page->requires_manual_grading()) {
  300. $nmanual++;
  301. $manualpoints++;
  302. }
  303. }
  304. // Number of times answered
  305. $nviewed += count($attempts);
  306. }
  307. if ($lesson->custom) {
  308. $bestscores = array();
  309. // Find the highest possible score per page to get our total
  310. foreach ($answers as $answer) {
  311. if(!isset($bestscores[$answer->pageid])) {
  312. $bestscores[$answer->pageid] = $answer->score;
  313. } else if ($bestscores[$answer->pageid] < $answer->score) {
  314. $bestscores[$answer->pageid] = $answer->score;
  315. }
  316. }
  317. $total = array_sum($bestscores);
  318. } else {
  319. // Check to make sure the student has answered the minimum questions
  320. if ($lesson->minquestions and $nquestions < $lesson->minquestions) {
  321. // Nope, increase number viewed by the amount of unanswered questions
  322. $total = $nviewed + ($lesson->minquestions - $nquestions);
  323. } else {
  324. $total = $nviewed;
  325. }
  326. }
  327. }
  328. if ($total) { // not zero
  329. $thegrade = round(100 * $earned / $total, 5);
  330. }
  331. // Build the grade information object
  332. $gradeinfo = new stdClass;
  333. $gradeinfo->nquestions = $nquestions;
  334. $gradeinfo->attempts = $nviewed;
  335. $gradeinfo->total = $total;
  336. $gradeinfo->earned = $earned;
  337. $gradeinfo->grade = $thegrade;
  338. $gradeinfo->nmanual = $nmanual;
  339. $gradeinfo->manualpoints = $manualpoints;
  340. return $gradeinfo;
  341. }
  342. /**
  343. * Determines if a user can view the left menu. The determining factor
  344. * is whether a user has a grade greater than or equal to the lesson setting
  345. * of displayleftif
  346. *
  347. * @param object $lesson Lesson object of the current lesson
  348. * @return boolean 0 if the user cannot see, or $lesson->displayleft to keep displayleft unchanged
  349. **/
  350. function lesson_displayleftif($lesson) {
  351. global $CFG, $USER, $DB;
  352. if (!empty($lesson->displayleftif)) {
  353. // get the current user's max grade for this lesson
  354. $params = array ("userid" => $USER->id, "lessonid" => $lesson->id);
  355. 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)) {
  356. if ($maxgrade->maxgrade < $lesson->displayleftif) {
  357. return 0; // turn off the displayleft
  358. }
  359. } else {
  360. return 0; // no grades
  361. }
  362. }
  363. // if we get to here, keep the original state of displayleft lesson setting
  364. return $lesson->displayleft;
  365. }
  366. /**
  367. *
  368. * @param $cm
  369. * @param $lesson
  370. * @param $page
  371. * @return unknown_type
  372. */
  373. function lesson_add_fake_blocks($page, $cm, $lesson, $timer = null) {
  374. $bc = lesson_menu_block_contents($cm->id, $lesson);
  375. if (!empty($bc)) {
  376. $regions = $page->blocks->get_regions();
  377. $firstregion = reset($regions);
  378. $page->blocks->add_fake_block($bc, $firstregion);
  379. }
  380. $bc = lesson_mediafile_block_contents($cm->id, $lesson);
  381. if (!empty($bc)) {
  382. $page->blocks->add_fake_block($bc, $page->blocks->get_default_region());
  383. }
  384. if (!empty($timer)) {
  385. $bc = lesson_clock_block_contents($cm->id, $lesson, $timer, $page);
  386. if (!empty($bc)) {
  387. $page->blocks->add_fake_block($bc, $page->blocks->get_default_region());
  388. }
  389. }
  390. }
  391. /**
  392. * If there is a media file associated with this
  393. * lesson, return a block_contents that displays it.
  394. *
  395. * @param int $cmid Course Module ID for this lesson
  396. * @param object $lesson Full lesson record object
  397. * @return block_contents
  398. **/
  399. function lesson_mediafile_block_contents($cmid, $lesson) {
  400. global $OUTPUT;
  401. if (empty($lesson->mediafile)) {
  402. return null;
  403. }
  404. $options = array();
  405. $options['menubar'] = 0;
  406. $options['location'] = 0;
  407. $options['left'] = 5;
  408. $options['top'] = 5;
  409. $options['scrollbars'] = 1;
  410. $options['resizable'] = 1;
  411. $options['width'] = $lesson->mediawidth;
  412. $options['height'] = $lesson->mediaheight;
  413. $link = new moodle_url('/mod/lesson/mediafile.php?id='.$cmid);
  414. $action = new popup_action('click', $link, 'lessonmediafile', $options);
  415. $content = $OUTPUT->action_link($link, get_string('mediafilepopup', 'lesson'), $action, array('title'=>get_string('mediafilepopup', 'lesson')));
  416. $bc = new block_contents();
  417. $bc->title = get_string('linkedmedia', 'lesson');
  418. $bc->attributes['class'] = 'mediafile block';
  419. $bc->content = $content;
  420. return $bc;
  421. }
  422. /**
  423. * If a timed lesson and not a teacher, then
  424. * return a block_contents containing the clock.
  425. *
  426. * @param int $cmid Course Module ID for this lesson
  427. * @param object $lesson Full lesson record object
  428. * @param object $timer Full timer record object
  429. * @return block_contents
  430. **/
  431. function lesson_clock_block_contents($cmid, $lesson, $timer, $page) {
  432. // Display for timed lessons and for students only
  433. $context = context_module::instance($cmid);
  434. if ($lesson->timelimit == 0 || has_capability('mod/lesson:manage', $context)) {
  435. return null;
  436. }
  437. $content = '<div id="lesson-timer">';
  438. $content .= $lesson->time_remaining($timer->starttime);
  439. $content .= '</div>';
  440. $clocksettings = array('starttime' => $timer->starttime, 'servertime' => time(), 'testlength' => $lesson->timelimit);
  441. $page->requires->data_for_js('clocksettings', $clocksettings, true);
  442. $page->requires->strings_for_js(array('timeisup'), 'lesson');
  443. $page->requires->js('/mod/lesson/timer.js');
  444. $page->requires->js_init_call('show_clock');
  445. $bc = new block_contents();
  446. $bc->title = get_string('timeremaining', 'lesson');
  447. $bc->attributes['class'] = 'clock block';
  448. $bc->content = $content;
  449. return $bc;
  450. }
  451. /**
  452. * If left menu is turned on, then this will
  453. * print the menu in a block
  454. *
  455. * @param int $cmid Course Module ID for this lesson
  456. * @param lesson $lesson Full lesson record object
  457. * @return void
  458. **/
  459. function lesson_menu_block_contents($cmid, $lesson) {
  460. global $CFG, $DB;
  461. if (!$lesson->displayleft) {
  462. return null;
  463. }
  464. $pages = $lesson->load_all_pages();
  465. foreach ($pages as $page) {
  466. if ((int)$page->prevpageid === 0) {
  467. $pageid = $page->id;
  468. break;
  469. }
  470. }
  471. $currentpageid = optional_param('pageid', $pageid, PARAM_INT);
  472. if (!$pageid || !$pages) {
  473. return null;
  474. }
  475. $content = '<a href="#maincontent" class="accesshide">' .
  476. get_string('skip', 'lesson') .
  477. "</a>\n<div class=\"menuwrapper\">\n<ul>\n";
  478. while ($pageid != 0) {
  479. $page = $pages[$pageid];
  480. // Only process branch tables with display turned on
  481. if ($page->displayinmenublock && $page->display) {
  482. if ($page->id == $currentpageid) {
  483. $content .= '<li class="selected">'.format_string($page->title,true)."</li>\n";
  484. } else {
  485. $content .= "<li class=\"notselected\"><a href=\"$CFG->wwwroot/mod/lesson/view.php?id=$cmid&amp;pageid=$page->id\">".format_string($page->title,true)."</a></li>\n";
  486. }
  487. }
  488. $pageid = $page->nextpageid;
  489. }
  490. $content .= "</ul>\n</div>\n";
  491. $bc = new block_contents();
  492. $bc->title = get_string('lessonmenu', 'lesson');
  493. $bc->attributes['class'] = 'menu block';
  494. $bc->content = $content;
  495. return $bc;
  496. }
  497. /**
  498. * Adds header buttons to the page for the lesson
  499. *
  500. * @param object $cm
  501. * @param object $context
  502. * @param bool $extraeditbuttons
  503. * @param int $lessonpageid
  504. */
  505. function lesson_add_header_buttons($cm, $context, $extraeditbuttons=false, $lessonpageid=null) {
  506. global $CFG, $PAGE, $OUTPUT;
  507. if (has_capability('mod/lesson:edit', $context) && $extraeditbuttons) {
  508. if ($lessonpageid === null) {
  509. print_error('invalidpageid', 'lesson');
  510. }
  511. if (!empty($lessonpageid) && $lessonpageid != LESSON_EOL) {
  512. $url = new moodle_url('/mod/lesson/editpage.php', array(
  513. 'id' => $cm->id,
  514. 'pageid' => $lessonpageid,
  515. 'edit' => 1,
  516. 'returnto' => $PAGE->url->out_as_local_url(false)
  517. ));
  518. $PAGE->set_button($OUTPUT->single_button($url, get_string('editpagecontent', 'lesson')));
  519. }
  520. }
  521. }
  522. /**
  523. * This is a function used to detect media types and generate html code.
  524. *
  525. * @global object $CFG
  526. * @global object $PAGE
  527. * @param object $lesson
  528. * @param object $context
  529. * @return string $code the html code of media
  530. */
  531. function lesson_get_media_html($lesson, $context) {
  532. global $CFG, $PAGE, $OUTPUT;
  533. require_once("$CFG->libdir/resourcelib.php");
  534. // get the media file link
  535. if (strpos($lesson->mediafile, '://') !== false) {
  536. $url = new moodle_url($lesson->mediafile);
  537. } else {
  538. // the timemodified is used to prevent caching problems, instead of '/' we should better read from files table and use sortorder
  539. $url = moodle_url::make_pluginfile_url($context->id, 'mod_lesson', 'mediafile', $lesson->timemodified, '/', ltrim($lesson->mediafile, '/'));
  540. }
  541. $title = $lesson->mediafile;
  542. $clicktoopen = html_writer::link($url, get_string('download'));
  543. $mimetype = resourcelib_guess_url_mimetype($url);
  544. $extension = resourcelib_get_extension($url->out(false));
  545. $mediamanager = core_media_manager::instance($PAGE);
  546. $embedoptions = array(
  547. core_media_manager::OPTION_TRUSTED => true,
  548. core_media_manager::OPTION_BLOCK => true
  549. );
  550. // find the correct type and print it out
  551. if (in_array($mimetype, array('image/gif','image/jpeg','image/png'))) { // It's an image
  552. $code = resourcelib_embed_image($url, $title);
  553. } else if ($mediamanager->can_embed_url($url, $embedoptions)) {
  554. // Media (audio/video) file.
  555. $code = $mediamanager->embed_url($url, $title, 0, 0, $embedoptions);
  556. } else {
  557. // anything else - just try object tag enlarged as much as possible
  558. $code = resourcelib_embed_general($url, $title, $clicktoopen, $mimetype);
  559. }
  560. return $code;
  561. }
  562. /**
  563. * Logic to happen when a/some group(s) has/have been deleted in a course.
  564. *
  565. * @param int $courseid The course ID.
  566. * @param int $groupid The group id if it is known
  567. * @return void
  568. */
  569. function lesson_process_group_deleted_in_course($courseid, $groupid = null) {
  570. global $DB;
  571. $params = array('courseid' => $courseid);
  572. if ($groupid) {
  573. $params['groupid'] = $groupid;
  574. // We just update the group that was deleted.
  575. $sql = "SELECT o.id, o.lessonid
  576. FROM {lesson_overrides} o
  577. JOIN {lesson} lesson ON lesson.id = o.lessonid
  578. WHERE lesson.course = :courseid
  579. AND o.groupid = :groupid";
  580. } else {
  581. // No groupid, we update all orphaned group overrides for all lessons in course.
  582. $sql = "SELECT o.id, o.lessonid
  583. FROM {lesson_overrides} o
  584. JOIN {lesson} lesson ON lesson.id = o.lessonid
  585. LEFT JOIN {groups} grp ON grp.id = o.groupid
  586. WHERE lesson.course = :courseid
  587. AND o.groupid IS NOT NULL
  588. AND grp.id IS NULL";
  589. }
  590. $records = $DB->get_records_sql_menu($sql, $params);
  591. if (!$records) {
  592. return; // Nothing to do.
  593. }
  594. $DB->delete_records_list('lesson_overrides', 'id', array_keys($records));
  595. }
  596. /**
  597. * Return the overview report table and data.
  598. *
  599. * @param lesson $lesson lesson instance
  600. * @param mixed $currentgroup false if not group used, 0 for all groups, group id (int) to filter by that groups
  601. * @return mixed false if there is no information otherwise html_table and stdClass with the table and data
  602. * @since Moodle 3.3
  603. */
  604. function lesson_get_overview_report_table_and_data(lesson $lesson, $currentgroup) {
  605. global $DB, $CFG, $OUTPUT;
  606. require_once($CFG->dirroot . '/mod/lesson/pagetypes/branchtable.php');
  607. $context = $lesson->context;
  608. $cm = $lesson->cm;
  609. // Count the number of branch and question pages in this lesson.
  610. $branchcount = $DB->count_records('lesson_pages', array('lessonid' => $lesson->id, 'qtype' => LESSON_PAGE_BRANCHTABLE));
  611. $questioncount = ($DB->count_records('lesson_pages', array('lessonid' => $lesson->id)) - $branchcount);
  612. // Only load students if there attempts for this lesson.
  613. $attempts = $DB->record_exists('lesson_attempts', array('lessonid' => $lesson->id));
  614. $branches = $DB->record_exists('lesson_branch', array('lessonid' => $lesson->id));
  615. $timer = $DB->record_exists('lesson_timer', array('lessonid' => $lesson->id));
  616. if ($attempts or $branches or $timer) {
  617. list($esql, $params) = get_enrolled_sql($context, '', $currentgroup, true);
  618. list($sort, $sortparams) = users_order_by_sql('u');
  619. $extrafields = get_extra_user_fields($context);
  620. $params['a1lessonid'] = $lesson->id;
  621. $params['b1lessonid'] = $lesson->id;
  622. $params['c1lessonid'] = $lesson->id;
  623. $ufields = user_picture::fields('u', $extrafields);
  624. $sql = "SELECT DISTINCT $ufields
  625. FROM {user} u
  626. JOIN (
  627. SELECT userid, lessonid FROM {lesson_attempts} a1
  628. WHERE a1.lessonid = :a1lessonid
  629. UNION
  630. SELECT userid, lessonid FROM {lesson_branch} b1
  631. WHERE b1.lessonid = :b1lessonid
  632. UNION
  633. SELECT userid, lessonid FROM {lesson_timer} c1
  634. WHERE c1.lessonid = :c1lessonid
  635. ) a ON u.id = a.userid
  636. JOIN ($esql) ue ON ue.id = a.userid
  637. ORDER BY $sort";
  638. $students = $DB->get_recordset_sql($sql, $params);
  639. if (!$students->valid()) {
  640. $students->close();
  641. return array(false, false);
  642. }
  643. } else {
  644. return array(false, false);
  645. }
  646. if (! $grades = $DB->get_records('lesson_grades', array('lessonid' => $lesson->id), 'completed')) {
  647. $grades = array();
  648. }
  649. if (! $times = $DB->get_records('lesson_timer', array('lessonid' => $lesson->id), 'starttime')) {
  650. $times = array();
  651. }
  652. // Build an array for output.
  653. $studentdata = array();
  654. $attempts = $DB->get_recordset('lesson_attempts', array('lessonid' => $lesson->id), 'timeseen');
  655. foreach ($attempts as $attempt) {
  656. // if the user is not in the array or if the retry number is not in the sub array, add the data for that try.
  657. if (empty($studentdata[$attempt->userid]) || empty($studentdata[$attempt->userid][$attempt->retry])) {
  658. // restore/setup defaults
  659. $n = 0;
  660. $timestart = 0;
  661. $timeend = 0;
  662. $usergrade = null;
  663. $eol = 0;
  664. // search for the grade record for this try. if not there, the nulls defined above will be used.
  665. foreach($grades as $grade) {
  666. // check to see if the grade matches the correct user
  667. if ($grade->userid == $attempt->userid) {
  668. // see if n is = to the retry
  669. if ($n == $attempt->retry) {
  670. // get grade info
  671. $usergrade = round($grade->grade, 2); // round it here so we only have to do it once
  672. break;
  673. }
  674. $n++; // if not equal, then increment n
  675. }
  676. }
  677. $n = 0;
  678. // search for the time record for this try. if not there, the nulls defined above will be used.
  679. foreach($times as $time) {
  680. // check to see if the grade matches the correct user
  681. if ($time->userid == $attempt->userid) {
  682. // see if n is = to the retry
  683. if ($n == $attempt->retry) {
  684. // get grade info
  685. $timeend = $time->lessontime;
  686. $timestart = $time->starttime;
  687. $eol = $time->completed;
  688. break;
  689. }
  690. $n++; // if not equal, then increment n
  691. }
  692. }
  693. // build up the array.
  694. // this array represents each student and all of their tries at the lesson
  695. $studentdata[$attempt->userid][$attempt->retry] = array( "timestart" => $timestart,
  696. "timeend" => $timeend,
  697. "grade" => $usergrade,
  698. "end" => $eol,
  699. "try" => $attempt->retry,
  700. "userid" => $attempt->userid);
  701. }
  702. }
  703. $attempts->close();
  704. $branches = $DB->get_recordset('lesson_branch', array('lessonid' => $lesson->id), 'timeseen');
  705. foreach ($branches as $branch) {
  706. // If the user is not in the array or if the retry number is not in the sub array, add the data for that try.
  707. if (empty($studentdata[$branch->userid]) || empty($studentdata[$branch->userid][$branch->retry])) {
  708. // Restore/setup defaults.
  709. $n = 0;
  710. $timestart = 0;
  711. $timeend = 0;
  712. $usergrade = null;
  713. $eol = 0;
  714. // Search for the time record for this try. if not there, the nulls defined above will be used.
  715. foreach ($times as $time) {
  716. // Check to see if the grade matches the correct user.
  717. if ($time->userid == $branch->userid) {
  718. // See if n is = to the retry.
  719. if ($n == $branch->retry) {
  720. // Get grade info.
  721. $timeend = $time->lessontime;
  722. $timestart = $time->starttime;
  723. $eol = $time->completed;
  724. break;
  725. }
  726. $n++; // If not equal, then increment n.
  727. }
  728. }
  729. // Build up the array.
  730. // This array represents each student and all of their tries at the lesson.
  731. $studentdata[$branch->userid][$branch->retry] = array( "timestart" => $timestart,
  732. "timeend" => $timeend,
  733. "grade" => $usergrade,
  734. "end" => $eol,
  735. "try" => $branch->retry,
  736. "userid" => $branch->userid);
  737. }
  738. }
  739. $branches->close();
  740. // Need the same thing for timed entries that were not completed.
  741. foreach ($times as $time) {
  742. $endoflesson = $time->completed;
  743. // If the time start is the same with another record then we shouldn't be adding another item to this array.
  744. if (isset($studentdata[$time->userid])) {
  745. $foundmatch = false;
  746. $n = 0;
  747. foreach ($studentdata[$time->userid] as $key => $value) {
  748. if ($value['timestart'] == $time->starttime) {
  749. // Don't add this to the array.
  750. $foundmatch = true;
  751. break;
  752. }
  753. }
  754. $n = count($studentdata[$time->userid]) + 1;
  755. if (!$foundmatch) {
  756. // Add a record.
  757. $studentdata[$time->userid][] = array(
  758. "timestart" => $time->starttime,
  759. "timeend" => $time->lessontime,
  760. "grade" => null,
  761. "end" => $endoflesson,
  762. "try" => $n,
  763. "userid" => $time->userid
  764. );
  765. }
  766. } else {
  767. $studentdata[$time->userid][] = array(
  768. "timestart" => $time->starttime,
  769. "timeend" => $time->lessontime,
  770. "grade" => null,
  771. "end" => $endoflesson,
  772. "try" => 0,
  773. "userid" => $time->userid
  774. );
  775. }
  776. }
  777. // To store all the data to be returned by the function.
  778. $data = new stdClass();
  779. // Determine if lesson should have a score.
  780. if ($branchcount > 0 AND $questioncount == 0) {
  781. // This lesson only contains content pages and is not graded.
  782. $data->lessonscored = false;
  783. } else {
  784. // This lesson is graded.
  785. $data->lessonscored = true;
  786. }
  787. // set all the stats variables
  788. $data->numofattempts = 0;
  789. $data->avescore = 0;
  790. $data->avetime = 0;
  791. $data->highscore = null;
  792. $data->lowscore = null;
  793. $data->hightime = null;
  794. $data->lowtime = null;
  795. $data->students = array();
  796. $table = new html_table();
  797. $headers = [get_string('name')];
  798. foreach ($extrafields as $field) {
  799. $headers[] = get_user_field_name($field);
  800. }
  801. $caneditlesson = has_capability('mod/lesson:edit', $context);
  802. $attemptsheader = get_string('attempts', 'lesson');
  803. if ($caneditlesson) {
  804. $selectall = get_string('selectallattempts', 'lesson');
  805. $deselectall = get_string('deselectallattempts', 'lesson');
  806. // Build the select/deselect all control.
  807. $selectallid = 'selectall-attempts';
  808. $mastercheckbox = new \core\output\checkbox_toggleall('lesson-attempts', true, [
  809. 'id' => $selectallid,
  810. 'name' => $selectallid,
  811. 'value' => 1,
  812. 'label' => $selectall,
  813. 'selectall' => $selectall,
  814. 'deselectall' => $deselectall,
  815. 'labelclasses' => 'form-check-label'
  816. ]);
  817. $attemptsheader = $OUTPUT->render($mastercheckbox);
  818. }
  819. $headers [] = $attemptsheader;
  820. // Set up the table object.
  821. if ($data->lessonscored) {
  822. $headers [] = get_string('highscore', 'lesson');
  823. }
  824. $colcount = count($headers);
  825. $table->head = $headers;
  826. $table->align = [];
  827. $table->align = array_pad($table->align, $colcount, 'center');
  828. $table->align[$colcount - 1] = 'left';
  829. if ($data->lessonscored) {
  830. $table->align[$colcount - 2] = 'left';
  831. }
  832. $table->wrap = [];
  833. $table->wrap = array_pad($table->wrap, $colcount, 'nowrap');
  834. $table->attributes['class'] = 'table table-striped';
  835. // print out the $studentdata array
  836. // going through each student that has attempted the lesson, so, each student should have something to be displayed
  837. foreach ($students as $student) {
  838. // check to see if the student has attempts to print out
  839. if (array_key_exists($student->id, $studentdata)) {
  840. // set/reset some variables
  841. $attempts = array();
  842. $dataforstudent = new stdClass;
  843. $dataforstudent->attempts = array();
  844. // gather the data for each user attempt
  845. $bestgrade = 0;
  846. // $tries holds all the tries/retries a student has done
  847. $tries = $studentdata[$student->id];
  848. $studentname = fullname($student, true);
  849. foreach ($tries as $try) {
  850. $dataforstudent->attempts[] = $try;
  851. // Start to build up the checkbox and link.
  852. $attempturlparams = [
  853. 'id' => $cm->id,
  854. 'action' => 'reportdetail',
  855. 'userid' => $try['userid'],
  856. 'try' => $try['try'],
  857. ];
  858. if ($try["grade"] !== null) { // if null then not done yet
  859. // this is what the link does when the user has completed the try
  860. $timetotake = $try["timeend"] - $try["timestart"];
  861. if ($try["grade"] > $bestgrade) {
  862. $bestgrade = $try["grade"];
  863. }
  864. $attemptdata = (object)[
  865. 'grade' => $try["grade"],
  866. 'timestart' => userdate($try["timestart"]),
  867. 'duration' => format_time($timetotake),
  868. ];
  869. $attemptlinkcontents = get_string('attemptinfowithgrade', 'lesson', $attemptdata);
  870. } else {
  871. if ($try["end"]) {
  872. // User finished the lesson but has no grade. (Happens when there are only content pages).
  873. $timetotake = $try["timeend"] - $try["timestart"];
  874. $attemptdata = (object)[
  875. 'timestart' => userdate($try["timestart"]),
  876. 'duration' => format_time($timetotake),
  877. ];
  878. $attemptlinkcontents = get_string('attemptinfonograde', 'lesson', $attemptdata);
  879. } else {
  880. // This is what the link does/looks like when the user has not completed the attempt.
  881. if ($try['timestart'] !== 0) {
  882. // Teacher previews do not track time spent.
  883. $attemptlinkcontents = get_string("notcompletedwithdate", "lesson", userdate($try["timestart"]));
  884. } else {
  885. $attemptlinkcontents = get_string("notcompleted", "lesson");
  886. }
  887. $timetotake = null;
  888. }
  889. }
  890. $attempturl = new moodle_url('/mod/lesson/report.php', $attempturlparams);
  891. $attemptlink = html_writer::link($attempturl, $attemptlinkcontents, ['class' => 'lesson-attempt-link']);
  892. if ($caneditlesson) {
  893. $attemptid = 'attempt-' . $try['userid'] . '-' . $try['try'];
  894. $attemptname = 'attempts[' . $try['userid'] . '][' . $try['try'] . ']';
  895. $checkbox = new \core\output\checkbox_toggleall('lesson-attempts', false, [
  896. 'id' => $attemptid,
  897. 'name' => $attemptname,
  898. 'label' => $attemptlink
  899. ]);
  900. $attemptlink = $OUTPUT->render($checkbox);
  901. }
  902. // build up the attempts array
  903. $attempts[] = $attemptlink;
  904. // Run these lines for the stats only if the user finnished the lesson.
  905. if ($try["end"]) {
  906. // User has completed the lesson.
  907. $data->numofattempts++;
  908. $data->avetime += $timetotake;
  909. if ($timetotake > $data->hightime || $data->hightime == null) {
  910. $data->hightime = $timetotake;
  911. }
  912. if ($timetotake < $data->lowtime || $data->lowtime == null) {
  913. $data->lowtime = $timetotake;
  914. }
  915. if ($try["grade"] !== null) {
  916. // The lesson was scored.
  917. $data->avescore += $try["grade"];
  918. if ($try["grade"] > $data->highscore || $data->highscore === null) {
  919. $data->highscore = $try["grade"];
  920. }
  921. if ($try["grade"] < $data->lowscore || $data->lowscore === null) {
  922. $data->lowscore = $try["grade"];
  923. }
  924. }
  925. }
  926. }
  927. // get line breaks in after each attempt
  928. $attempts = implode("<br />\n", $attempts);
  929. $row = [$studentname];
  930. foreach ($extrafields as $field) {
  931. $row[] = $student->$field;
  932. }
  933. $row[] = $attempts;
  934. if ($data->lessonscored) {
  935. // Add the grade if the lesson is graded.
  936. $row[] = $bestgrade."%";
  937. }
  938. $table->data[] = $row;
  939. // Add the student data.
  940. $dataforstudent->id = $student->id;
  941. $dataforstudent->fullname = $studentname;
  942. $dataforstudent->bestgrade = $bestgrade;
  943. $data->students[] = $dataforstudent;
  944. }
  945. }
  946. $students->close();
  947. if ($data->numofattempts > 0) {
  948. $data->avescore = $data->avescore / $data->numofattempts;
  949. }
  950. return array($table, $data);
  951. }
  952. /**
  953. * Return information about one user attempt (including answers)
  954. * @param lesson $lesson lesson instance
  955. * @param int $userid the user id
  956. * @param int $attempt the attempt number
  957. * @return array the user answers (array) and user data stats (object)
  958. * @since Moodle 3.3
  959. */
  960. function lesson_get_user_detailed_report_data(lesson $lesson, $userid, $attempt) {
  961. global $DB;
  962. $context = $lesson->context;
  963. if (!empty($userid)) {
  964. // Apply overrides.
  965. $lesson->update_effective_access($userid);
  966. }
  967. $pageid = 0;
  968. $lessonpages = $lesson->load_all_pages();
  969. foreach ($lessonpages as $lessonpage) {
  970. if ($lessonpage->prevpageid == 0) {
  971. $pageid = $lessonpage->id;
  972. }
  973. }
  974. // now gather the stats into an object
  975. $firstpageid = $pageid;
  976. $pagestats = array();
  977. while ($pageid != 0) { // EOL
  978. $page = $lessonpages[$pageid];
  979. $params = array ("lessonid" => $lesson->id, "pageid" => $page->id);
  980. if ($allanswers = $DB->get_records_select("lesson_attempts", "lessonid = :lessonid AND pageid = :pageid", $params, "timeseen")) {
  981. // get them ready for processing
  982. $orderedanswers = array();
  983. foreach ($allanswers as $singleanswer) {
  984. // ordering them like this, will help to find the single attempt record that we want to keep.
  985. $orderedanswers[$singleanswer->userid][$singleanswer->retry][] = $singleanswer;
  986. }
  987. // this is foreach user and for each try for that user, keep one attempt record
  988. foreach ($orderedanswers as $orderedanswer) {
  989. foreach($orderedanswer as $tries) {
  990. $page->stats($pagestats, $tries);
  991. }
  992. }
  993. } else {
  994. // no one answered yet...
  995. }
  996. //unset($orderedanswers); initialized above now
  997. $pageid = $page->nextpageid;
  998. }
  999. $manager = lesson_page_type_manager::get($lesson);
  1000. $qtypes = $manager->get_page_type_strings();
  1001. $answerpages = array();
  1002. $answerpage = "";
  1003. $pageid = $firstpageid;
  1004. // cycle through all the pages
  1005. // foreach page, add to the $answerpages[] array all the data that is needed
  1006. // from the question, the users attempt, and the statistics
  1007. // grayout pages that the user did not answer and Branch, end of branch, cluster
  1008. // and end of cluster pages
  1009. while ($pageid != 0) { // EOL
  1010. $page = $lessonpages[$pageid];
  1011. $answerpage = new stdClass;
  1012. // Keep the original page object.
  1013. $answerpage->page = $page;
  1014. $data ='';
  1015. $answerdata = new stdClass;
  1016. // Set some defaults for the answer data.
  1017. $answerdata->score = null;
  1018. $answerdata->response = null;
  1019. $answerdata->responseformat = FORMAT_PLAIN;
  1020. $answerpage->title = format_string($page->title);
  1021. $options = new stdClass;
  1022. $options->noclean = true;
  1023. $options->overflowdiv = true;
  1024. $options->context = $context;
  1025. $answerpage->contents = format_text($page->contents, $page->contentsformat, $options);
  1026. $answerpage->qtype = $qtypes[$page->qtype].$page->option_description_string();
  1027. $answerpage->grayout = $page->grayout;
  1028. $answerpage->context = $context;
  1029. if (empty($userid)) {
  1030. // there is no userid, so set these vars and display stats.
  1031. $answerpage->grayout = 0;
  1032. $useranswer = null;
  1033. } elseif ($useranswers = $DB->get_records("lesson_attempts",array("lessonid"=>$lesson->id, "userid"=>$userid, "retry"=>$attempt,"pageid"=>$page->id), "timeseen")) {
  1034. // get the user's answer for this page
  1035. // need to find the right one
  1036. $i = 0;
  1037. foreach ($useranswers as $userattempt) {
  1038. $useranswer = $userattempt;
  1039. $i++;
  1040. if ($lesson->maxattempts == $i) {
  1041. break; // reached maxattempts, break out
  1042. }
  1043. }
  1044. } else {
  1045. // user did not answer this page, gray it out and set some nulls
  1046. $answerpage->grayout = 1;
  1047. $useranswer = null;
  1048. }
  1049. $i = 0;
  1050. $n = 0;
  1051. $answerpages[] = $page->report_answers(clone($answerpage), clone($answerdata), $useranswer, $pagestats, $i, $n);
  1052. $pageid = $page->nextpageid;
  1053. }
  1054. $userstats = new stdClass;
  1055. if (!empty($userid)) {
  1056. $params = array("lessonid"=>$lesson->id, "userid"=>$userid);
  1057. $alreadycompleted = true;
  1058. if (!$grades = $DB->get_records_select("lesson_grades", "lessonid = :lessonid and userid = :userid", $params, "completed", "*", $attempt, 1)) {
  1059. $userstats->grade = -1;
  1060. $userstats->completed = -1;
  1061. $alreadycompleted = false;
  1062. } else {
  1063. $userstats->grade = current($grades);
  1064. $userstats->completed = $userstats->grade->completed;
  1065. $userstats->grade = round($userstats->grade->grade, 2);
  1066. }
  1067. if (!$times = $lesson->get_user_timers($userid, 'starttime', '*', $attempt, 1)) {
  1068. $userstats->timetotake = -1;
  1069. $alreadycompleted = false;
  1070. } else {
  1071. $userstats->timetotake = current($times);
  1072. $userstats->timetotake = $userstats->timetotake->lessontime - $userstats->timetotake->starttime;
  1073. }
  1074. if ($alreadycompleted) {
  1075. $userstats->gradeinfo = lesson_grade($lesson, $attempt, $userid);
  1076. }
  1077. }
  1078. return array($answerpages, $userstats);
  1079. }
  1080. /**
  1081. * Return user's deadline for all lessons in a course, hereby taking into account group and user overrides.
  1082. *
  1083. * @param int $courseid the course id.
  1084. * @return object An object with of all lessonsids and close unixdates in this course,
  1085. * taking into account the most lenient overrides, if existing and 0 if no close date is set.
  1086. */
  1087. function lesson_get_user_deadline($courseid) {
  1088. global $DB, $USER;
  1089. // For teacher and manager/admins return lesson's deadline.
  1090. if (has_capability('moodle/course:update', context_course::instance($courseid))) {
  1091. $sql = "SELECT lesson.id, lesson.deadline AS userdeadline
  1092. FROM {lesson} lesson
  1093. WHERE lesson.course = :courseid";
  1094. $results = $DB->get_records_sql($sql, array('courseid' => $courseid));
  1095. return $results;
  1096. }
  1097. $sql = "SELECT a.id,
  1098. COALESCE(v.userclose, v.groupclose, a.deadline, 0) AS userdeadline
  1099. FROM (
  1100. SELECT lesson.id as lessonid,
  1101. MAX(leo.deadline) AS userclose, MAX(qgo.deadline) AS groupclose
  1102. FROM {lesson} lesson
  1103. LEFT JOIN {lesson_overrides} leo on lesson.id = leo.lessonid AND leo.userid = :userid
  1104. LEFT JOIN {groups_members} gm ON gm.userid = :useringroupid
  1105. LEFT JOIN {lesson_overrides} qgo on lesson.id = qgo.lessonid AND qgo.groupid = gm.groupid
  1106. WHERE lesson.course = :courseid
  1107. GROUP BY lesson.id
  1108. ) v
  1109. JOIN {lesson} a ON a.id = v.lessonid";
  1110. $results = $DB->get_records_sql($sql, array('userid' => $USER->id, 'useringroupid' => $USER->id, 'courseid' => $courseid));
  1111. return $results;
  1112. }
  1113. /**
  1114. * Abstract class that page type's MUST inherit from.
  1115. *
  1116. * This is the abstract class that ALL add page type forms must extend.
  1117. * You will notice that all but two of the methods this class contains are final.
  1118. * Essentially the only thing that extending classes can do is extend custom_definition.
  1119. * OR if it has a special requirement on creation it can extend construction_override
  1120. *
  1121. * @abstract
  1122. * @copyright 2009 Sam Hemelryk
  1123. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1124. */
  1125. abstract class lesson_add_page_form_base extends moodleform {
  1126. /**
  1127. * This is the classic define that is used to identify this pagetype.
  1128. * Will be one of LESSON_*
  1129. * @var int
  1130. */
  1131. public $qtype;
  1132. /**
  1133. * The simple string that describes the page type e.g. truefalse, multichoice
  1134. * @var string
  1135. */

Large files files are truncated, but you can click here to view the full file