2009-09-24 08:05:55 +00:00
< ? php
2009-12-16 02:00:48 +00:00
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
2006-09-30 19:49:40 +00:00
/**
* Local library file for Lesson . These are non - standard functions that are used
* only by Lesson .
*
2010-07-25 10:54:39 +00:00
* @ package mod
* @ subpackage lesson
2009-12-16 02:00:48 +00:00
* @ copyright 1999 onwards Martin Dougiamas { @ link http :// moodle . com }
2010-07-25 10:54:39 +00:00
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or late
2006-09-30 19:49:40 +00:00
**/
2006-03-17 03:31:19 +00:00
2009-12-16 02:00:48 +00:00
/** Make sure this isn't being directly accessed */
2010-07-24 18:56:56 +00:00
defined ( 'MOODLE_INTERNAL' ) || die ();
2009-12-16 02:00:48 +00:00
/** Include the files that are required by this module */
2010-07-24 18:56:56 +00:00
require_once ( $CFG -> dirroot . '/course/moodleform_mod.php' );
2009-12-16 02:00:48 +00:00
require_once ( $CFG -> dirroot . '/mod/lesson/lib.php' );
2011-01-23 18:34:41 +01:00
require_once ( $CFG -> libdir . '/filelib.php' );
2009-12-16 02:00:48 +00:00
2010-07-28 13:45:01 +00:00
/** This page */
define ( 'LESSON_THISPAGE' , 0 );
2009-12-16 02:00:48 +00:00
/** Next page -> any page not seen before */
define ( " LESSON_UNSEENPAGE " , 1 );
/** Next page -> any page not answered correctly */
define ( " LESSON_UNANSWEREDPAGE " , 2 );
/** Jump to Next Page */
define ( " LESSON_NEXTPAGE " , - 1 );
/** End of Lesson */
define ( " LESSON_EOL " , - 9 );
/** Jump to an unseen page within a branch and end of branch or end of lesson */
define ( " LESSON_UNSEENBRANCHPAGE " , - 50 );
/** Jump to Previous Page */
define ( " LESSON_PREVIOUSPAGE " , - 40 );
/** Jump to a random page within a branch and end of branch or end of lesson */
define ( " LESSON_RANDOMPAGE " , - 60 );
/** Jump to a random Branch */
define ( " LESSON_RANDOMBRANCH " , - 70 );
/** Cluster Jump */
define ( " LESSON_CLUSTERJUMP " , - 80 );
/** Undefined */
define ( " LESSON_UNDEFINED " , - 99 );
2004-09-15 20:32:24 +00:00
2010-07-24 18:56:56 +00:00
/** LESSON_MAX_EVENT_LENGTH = 432000 ; 5 days maximum */
define ( " LESSON_MAX_EVENT_LENGTH " , " 432000 " );
2004-09-15 20:32:24 +00:00
//////////////////////////////////////////////////////////////////////////////////////
2009-11-01 15:03:10 +00:00
/// Any other lesson functions go here. Each of them must have a name that
2004-09-15 20:32:24 +00:00
/// starts with lesson_
2006-03-17 03:31:19 +00:00
/**
2009-11-01 15:03:10 +00:00
* Checks to see if a LESSON_CLUSTERJUMP or
2006-03-17 03:31:19 +00:00
* a LESSON_UNSEENBRANCHPAGE is used in a lesson .
*
2009-11-01 15:03:10 +00:00
* This function is only executed when a teacher is
2006-03-17 03:31:19 +00:00
* checking the navigation for a lesson .
*
2010-09-18 13:04:10 +00:00
* @ param stdClass $lesson Id of the lesson that is to be checked .
2006-03-17 03:31:19 +00:00
* @ return boolean True or false .
**/
2006-03-17 03:37:31 +00:00
function lesson_display_teacher_warning ( $lesson ) {
2008-06-06 04:03:40 +00:00
global $DB ;
2009-11-01 15:03:10 +00:00
2005-03-05 00:09:39 +00:00
// get all of the lesson answers
2009-12-16 02:00:48 +00:00
$params = array ( " lessonid " => $lesson -> id );
2008-06-06 04:03:40 +00:00
if ( ! $lessonanswers = $DB -> get_records_select ( " lesson_answers " , " lessonid = :lessonid " , $params )) {
2010-09-18 13:27:21 +00:00
// no answers, then not using cluster or unseen
2005-03-05 00:09:39 +00:00
return false ;
}
// just check for the first one that fulfills the requirements
foreach ( $lessonanswers as $lessonanswer ) {
if ( $lessonanswer -> jumpto == LESSON_CLUSTERJUMP || $lessonanswer -> jumpto == LESSON_UNSEENBRANCHPAGE ) {
return true ;
}
}
2009-11-01 15:03:10 +00:00
2005-03-05 00:09:39 +00:00
// if no answers use either of the two jumps
return false ;
2004-09-15 20:32:24 +00:00
}
2006-03-17 03:31:19 +00:00
/**
* Interprets the LESSON_UNSEENBRANCHPAGE jump .
2009-11-01 15:03:10 +00:00
*
2006-03-17 03:31:19 +00:00
* will return the pageid of a random unseen page that is within a branch
*
2009-12-16 02:00:48 +00:00
* @ param lesson $lesson
2006-12-30 17:29:44 +00:00
* @ param int $userid Id of the user .
2006-03-17 03:31:19 +00:00
* @ param int $pageid Id of the page from which we are jumping .
* @ return int Id of the next page .
**/
2004-09-15 20:32:24 +00:00
function lesson_unseen_question_jump ( $lesson , $user , $pageid ) {
2008-06-06 04:03:40 +00:00
global $DB ;
2009-11-01 15:03:10 +00:00
2005-03-05 00:09:39 +00:00
// get the number of retakes
2009-12-16 02:00:48 +00:00
if ( ! $retakes = $DB -> count_records ( " lesson_grades " , array ( " lessonid " => $lesson -> id , " userid " => $user ))) {
2005-03-05 00:09:39 +00:00
$retakes = 0 ;
}
// get all the lesson_attempts aka what the user has seen
2009-12-16 02:00:48 +00:00
if ( $viewedpages = $DB -> get_records ( " lesson_attempts " , array ( " lessonid " => $lesson -> id , " userid " => $user , " retry " => $retakes ), " timeseen DESC " )) {
2005-03-05 00:09:39 +00:00
foreach ( $viewedpages as $viewed ) {
$seenpages [] = $viewed -> pageid ;
}
} else {
$seenpages = array ();
}
// get the lesson pages
2009-12-16 02:00:48 +00:00
$lessonpages = $lesson -> load_all_pages ();
2009-11-01 15:03:10 +00:00
2005-03-05 00:09:39 +00:00
if ( $pageid == LESSON_UNSEENBRANCHPAGE ) { // this only happens when a student leaves in the middle of an unseen question within a branch series
$pageid = $seenpages [ 0 ]; // just change the pageid to the last page viewed inside the branch table
}
// go up the pages till branch table
while ( $pageid != 0 ) { // this condition should never be satisfied... only happens if there are no branch tables above this page
2009-12-16 02:00:48 +00:00
if ( $lessonpages [ $pageid ] -> qtype == LESSON_PAGE_BRANCHTABLE ) {
2005-03-05 00:09:39 +00:00
break ;
}
$pageid = $lessonpages [ $pageid ] -> prevpageid ;
}
2009-11-01 15:03:10 +00:00
2010-08-11 08:39:11 +00:00
$pagesinbranch = $lesson -> get_sub_pages_of ( $pageid , array ( LESSON_PAGE_BRANCHTABLE , LESSON_PAGE_ENDOFBRANCH ));
2009-11-01 15:03:10 +00:00
2005-03-05 00:09:39 +00:00
// this foreach loop stores all the pages that are within the branch table but are not in the $seenpages array
$unseen = array ();
2009-11-01 15:03:10 +00:00
foreach ( $pagesinbranch as $page ) {
2005-03-05 00:09:39 +00:00
if ( ! in_array ( $page -> id , $seenpages )) {
$unseen [] = $page -> id ;
}
}
if ( count ( $unseen ) == 0 ) {
if ( isset ( $pagesinbranch )) {
$temp = end ( $pagesinbranch );
$nextpage = $temp -> nextpageid ; // they have seen all the pages in the branch, so go to EOB/next branch table/EOL
} else {
// there are no pages inside the branch, so return the next page
$nextpage = $lessonpages [ $pageid ] -> nextpageid ;
}
if ( $nextpage == 0 ) {
return LESSON_EOL ;
} else {
return $nextpage ;
}
} else {
return $unseen [ rand ( 0 , count ( $unseen ) - 1 )]; // returns a random page id for the next page
}
2004-09-15 20:32:24 +00:00
}
2006-03-17 03:31:19 +00:00
/**
* Handles the unseen branch table jump .
*
2009-12-16 02:00:48 +00:00
* @ param lesson $lesson
2006-12-30 17:29:44 +00:00
* @ param int $userid User id .
2006-03-17 03:31:19 +00:00
* @ return int Will return the page id of a branch table or end of lesson
**/
2009-12-16 02:00:48 +00:00
function lesson_unseen_branch_jump ( $lesson , $userid ) {
2008-06-06 04:03:40 +00:00
global $DB ;
2009-11-01 15:03:10 +00:00
2009-12-16 02:00:48 +00:00
if ( ! $retakes = $DB -> count_records ( " lesson_grades " , array ( " lessonid " => $lesson -> id , " userid " => $userid ))) {
2005-03-05 00:09:39 +00:00
$retakes = 0 ;
}
2009-12-16 02:00:48 +00:00
$params = array ( " lessonid " => $lesson -> id , " userid " => $userid , " retry " => $retakes );
2008-06-06 04:03:40 +00:00
if ( ! $seenbranches = $DB -> get_records_select ( " lesson_branch " , " lessonid = :lessonid AND userid = :userid AND retry = :retry " , $params ,
2005-03-05 00:09:39 +00:00
" timeseen DESC " )) {
2008-06-13 05:12:50 +00:00
print_error ( 'cannotfindrecords' , 'lesson' );
2005-03-05 00:09:39 +00:00
}
// get the lesson pages
2009-12-16 02:00:48 +00:00
$lessonpages = $lesson -> load_all_pages ();
2009-11-01 15:03:10 +00:00
2010-07-24 19:03:37 +00:00
// this loads all the viewed branch tables into $seen until it finds the branch table with the flag
2005-03-05 00:09:39 +00:00
// which is the branch table that starts the unseenbranch function
2009-11-01 15:03:10 +00:00
$seen = array ();
2005-03-05 00:09:39 +00:00
foreach ( $seenbranches as $seenbranch ) {
if ( ! $seenbranch -> flag ) {
$seen [ $seenbranch -> pageid ] = $seenbranch -> pageid ;
} else {
$start = $seenbranch -> pageid ;
break ;
}
}
// this function searches through the lesson pages to find all the branch tables
// that follow the flagged branch table
$pageid = $lessonpages [ $start ] -> nextpageid ; // move down from the flagged branch table
2012-03-23 15:36:39 +08:00
$branchtables = array ();
2005-03-05 00:09:39 +00:00
while ( $pageid != 0 ) { // grab all of the branch table till eol
2009-12-16 02:00:48 +00:00
if ( $lessonpages [ $pageid ] -> qtype == LESSON_PAGE_BRANCHTABLE ) {
2005-03-05 00:09:39 +00:00
$branchtables [] = $lessonpages [ $pageid ] -> id ;
}
$pageid = $lessonpages [ $pageid ] -> nextpageid ;
}
$unseen = array ();
foreach ( $branchtables as $branchtable ) {
// load all of the unseen branch tables into unseen
if ( ! array_key_exists ( $branchtable , $seen )) {
$unseen [] = $branchtable ;
}
}
if ( count ( $unseen ) > 0 ) {
return $unseen [ rand ( 0 , count ( $unseen ) - 1 )]; // returns a random page id for the next page
} else {
return LESSON_EOL ; // has viewed all of the branch tables
}
2004-09-15 20:32:24 +00:00
}
2006-03-17 03:31:19 +00:00
/**
* Handles the random jump between a branch table and end of branch or end of lesson ( LESSON_RANDOMPAGE ) .
2009-11-01 15:03:10 +00:00
*
2009-12-16 02:00:48 +00:00
* @ param lesson $lesson
2006-03-17 03:31:19 +00:00
* @ param int $pageid The id of the page that we are jumping from ( ? )
* @ return int The pageid of a random page that is within a branch table
**/
2009-12-16 02:00:48 +00:00
function lesson_random_question_jump ( $lesson , $pageid ) {
2008-06-06 04:03:40 +00:00
global $DB ;
2009-11-01 15:03:10 +00:00
2005-03-05 00:09:39 +00:00
// get the lesson pages
2009-12-16 02:00:48 +00:00
$params = array ( " lessonid " => $lesson -> id );
2008-06-06 04:03:40 +00:00
if ( ! $lessonpages = $DB -> get_records_select ( " lesson_pages " , " lessonid = :lessonid " , $params )) {
2008-06-13 05:12:50 +00:00
print_error ( 'cannotfindpages' , 'lesson' );
2005-03-05 00:09:39 +00:00
}
// go up the pages till branch table
while ( $pageid != 0 ) { // this condition should never be satisfied... only happens if there are no branch tables above this page
2009-12-16 02:00:48 +00:00
if ( $lessonpages [ $pageid ] -> qtype == LESSON_PAGE_BRANCHTABLE ) {
2005-03-05 00:09:39 +00:00
break ;
}
$pageid = $lessonpages [ $pageid ] -> prevpageid ;
}
2009-11-01 15:03:10 +00:00
// get the pages within the branch
2010-08-11 08:39:11 +00:00
$pagesinbranch = $lesson -> get_sub_pages_of ( $pageid , array ( LESSON_PAGE_BRANCHTABLE , LESSON_PAGE_ENDOFBRANCH ));
2009-11-01 15:03:10 +00:00
2005-03-05 00:09:39 +00:00
if ( count ( $pagesinbranch ) == 0 ) {
// there are no pages inside the branch, so return the next page
return $lessonpages [ $pageid ] -> nextpageid ;
} else {
return $pagesinbranch [ rand ( 0 , count ( $pagesinbranch ) - 1 )] -> id ; // returns a random page id for the next page
}
2004-09-15 20:32:24 +00:00
}
2006-03-17 03:31:19 +00:00
/**
* Calculates a user ' s grade for a lesson .
*
* @ param object $lesson The lesson that the user is taking .
* @ param int $retries The attempt number .
2010-07-24 19:03:37 +00:00
* @ param int $userid Id of the user ( optional , default current user ) .
2006-05-19 01:05:13 +00:00
* @ return object { nquestions => number of questions answered
attempts => number of question attempts
total => max points possible
earned => points earned by student
grade => calculated percentage grade
nmanual => number of manually graded questions
manualpoints => point value for manually graded questions }
2006-03-17 03:31:19 +00:00
*/
2009-11-01 15:03:10 +00:00
function lesson_grade ( $lesson , $ntries , $userid = 0 ) {
2008-06-06 04:03:40 +00:00
global $USER , $DB ;
2005-03-05 00:09:39 +00:00
2006-05-19 01:05:13 +00:00
if ( empty ( $userid )) {
$userid = $USER -> id ;
}
2009-11-01 15:03:10 +00:00
2006-05-19 01:05:13 +00:00
// Zero out everything
$ncorrect = 0 ;
$nviewed = 0 ;
$score = 0 ;
$nmanual = 0 ;
$manualpoints = 0 ;
$thegrade = 0 ;
$nquestions = 0 ;
$total = 0 ;
$earned = 0 ;
2008-06-06 04:03:40 +00:00
$params = array ( " lessonid " => $lesson -> id , " userid " => $userid , " retry " => $ntries );
2009-11-01 15:03:10 +00:00
if ( $useranswers = $DB -> get_records_select ( " lesson_attempts " , " lessonid = :lessonid AND
2008-06-06 04:03:40 +00:00
userid = : userid AND retry = : retry " , $params , " timeseen " )) {
2006-05-19 01:05:13 +00:00
// group each try with its page
$attemptset = array ();
foreach ( $useranswers as $useranswer ) {
2009-11-01 15:03:10 +00:00
$attemptset [ $useranswer -> pageid ][] = $useranswer ;
2005-03-05 00:09:39 +00:00
}
2009-11-01 15:03:10 +00:00
2006-05-19 01:05:13 +00:00
// Drop all attempts that go beyond max attempts for the lesson
foreach ( $attemptset as $key => $set ) {
$attemptset [ $key ] = array_slice ( $set , 0 , $lesson -> maxattempts );
}
2009-11-01 15:03:10 +00:00
2006-05-19 01:05:13 +00:00
// get only the pages and their answers that the user answered
2008-06-06 04:03:40 +00:00
list ( $usql , $parameters ) = $DB -> get_in_or_equal ( array_keys ( $attemptset ));
2009-12-16 02:00:48 +00:00
array_unshift ( $parameters , $lesson -> id );
$pages = $DB -> get_records_select ( " lesson_pages " , " lessonid = ? AND id $usql " , $parameters );
$answers = $DB -> get_records_select ( " lesson_answers " , " lessonid = ? AND pageid $usql " , $parameters );
2009-11-01 15:03:10 +00:00
2006-05-19 01:05:13 +00:00
// Number of pages answered
$nquestions = count ( $pages );
foreach ( $attemptset as $attempts ) {
2009-12-16 02:00:48 +00:00
$page = lesson_page :: load ( $pages [ end ( $attempts ) -> pageid ], $lesson );
2006-05-19 01:05:13 +00:00
if ( $lesson -> custom ) {
$attempt = end ( $attempts );
// If essay question, handle it, otherwise add to score
2009-12-16 02:00:48 +00:00
if ( $page -> requires_manual_grading ()) {
2011-04-29 18:20:19 +08:00
$useranswerobj = unserialize ( $attempt -> useranswer );
if ( isset ( $useranswerobj -> score )) {
$earned += $useranswerobj -> score ;
}
2006-05-19 01:05:13 +00:00
$nmanual ++ ;
$manualpoints += $answers [ $attempt -> answerid ] -> score ;
2009-03-17 16:27:40 +00:00
} else if ( ! empty ( $attempt -> answerid )) {
2009-12-16 02:00:48 +00:00
$earned += $page -> earned_score ( $answers , $attempt );
2006-05-19 01:05:13 +00:00
}
} else {
foreach ( $attempts as $attempt ) {
$earned += $attempt -> correct ;
}
$attempt = end ( $attempts ); // doesn't matter which one
// If essay question, increase numbers
2009-12-16 02:00:48 +00:00
if ( $page -> requires_manual_grading ()) {
2006-05-19 01:05:13 +00:00
$nmanual ++ ;
$manualpoints ++ ;
2005-03-05 00:09:39 +00:00
}
}
2006-05-19 01:05:13 +00:00
// Number of times answered
$nviewed += count ( $attempts );
}
2009-11-01 15:03:10 +00:00
2006-05-19 01:05:13 +00:00
if ( $lesson -> custom ) {
2005-03-05 00:09:39 +00:00
$bestscores = array ();
2006-05-19 01:05:13 +00:00
// Find the highest possible score per page to get our total
foreach ( $answers as $answer ) {
2006-07-10 21:52:12 +00:00
if ( ! isset ( $bestscores [ $answer -> pageid ])) {
2006-05-19 01:05:13 +00:00
$bestscores [ $answer -> pageid ] = $answer -> score ;
2006-07-10 21:52:12 +00:00
} else if ( $bestscores [ $answer -> pageid ] < $answer -> score ) {
2006-05-19 01:05:13 +00:00
$bestscores [ $answer -> pageid ] = $answer -> score ;
2005-03-05 00:09:39 +00:00
}
}
2006-05-19 01:05:13 +00:00
$total = array_sum ( $bestscores );
} else {
// Check to make sure the student has answered the minimum questions
if ( $lesson -> minquestions and $nquestions < $lesson -> minquestions ) {
// Nope, increase number viewed by the amount of unanswered questions
$total = $nviewed + ( $lesson -> minquestions - $nquestions );
} else {
$total = $nviewed ;
}
2005-03-05 00:09:39 +00:00
}
2006-05-19 01:05:13 +00:00
}
2009-11-01 15:03:10 +00:00
2006-05-19 01:05:13 +00:00
if ( $total ) { // not zero
$thegrade = round ( 100 * $earned / $total , 5 );
}
2009-11-01 15:03:10 +00:00
2006-05-19 01:05:13 +00:00
// Build the grade information object
$gradeinfo = new stdClass ;
$gradeinfo -> nquestions = $nquestions ;
$gradeinfo -> attempts = $nviewed ;
$gradeinfo -> total = $total ;
$gradeinfo -> earned = $earned ;
$gradeinfo -> grade = $thegrade ;
$gradeinfo -> nmanual = $nmanual ;
$gradeinfo -> manualpoints = $manualpoints ;
2009-11-01 15:03:10 +00:00
2006-05-19 01:05:13 +00:00
return $gradeinfo ;
}
2005-10-24 23:06:43 +00:00
/**
* Determines if a user can view the left menu . The determining factor
* is whether a user has a grade greater than or equal to the lesson setting
* of displayleftif
*
* @ param object $lesson Lesson object of the current lesson
* @ return boolean 0 if the user cannot see , or $lesson -> displayleft to keep displayleft unchanged
**/
function lesson_displayleftif ( $lesson ) {
2008-06-06 04:03:40 +00:00
global $CFG , $USER , $DB ;
2009-11-01 15:03:10 +00:00
2005-10-24 23:06:43 +00:00
if ( ! empty ( $lesson -> displayleftif )) {
// get the current user's max grade for this lesson
2008-06-06 04:03:40 +00:00
$params = array ( " userid " => $USER -> id , " lessonid " => $lesson -> id );
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 )) {
2005-10-24 23:06:43 +00:00
if ( $maxgrade -> maxgrade < $lesson -> displayleftif ) {
return 0 ; // turn off the displayleft
}
} else {
return 0 ; // no grades
}
}
2009-11-01 15:03:10 +00:00
2005-10-24 23:06:43 +00:00
// if we get to here, keep the original state of displayleft lesson setting
return $lesson -> displayleft ;
}
2004-09-15 20:32:24 +00:00
2009-07-23 10:01:19 +00:00
/**
2009-11-01 15:03:10 +00:00
*
2009-07-23 10:01:19 +00:00
* @ param $cm
* @ param $lesson
* @ param $page
* @ return unknown_type
*/
2010-12-13 13:23:49 +00:00
function lesson_add_fake_blocks ( $page , $cm , $lesson , $timer = null ) {
2009-07-23 10:01:19 +00:00
$bc = lesson_menu_block_contents ( $cm -> id , $lesson );
if ( ! empty ( $bc )) {
$regions = $page -> blocks -> get_regions ();
$firstregion = reset ( $regions );
2010-12-13 13:23:49 +00:00
$page -> blocks -> add_fake_block ( $bc , $firstregion );
2009-07-23 10:01:19 +00:00
}
$bc = lesson_mediafile_block_contents ( $cm -> id , $lesson );
if ( ! empty ( $bc )) {
2010-12-13 13:23:49 +00:00
$page -> blocks -> add_fake_block ( $bc , $page -> blocks -> get_default_region ());
2009-07-23 10:01:19 +00:00
}
if ( ! empty ( $timer )) {
$bc = lesson_clock_block_contents ( $cm -> id , $lesson , $timer , $page );
if ( ! empty ( $bc )) {
2010-12-13 13:23:49 +00:00
$page -> blocks -> add_fake_block ( $bc , $page -> blocks -> get_default_region ());
2009-07-23 10:01:19 +00:00
}
}
}
2006-12-30 17:29:44 +00:00
/**
2009-11-01 15:03:10 +00:00
* If there is a media file associated with this
2009-07-23 10:01:19 +00:00
* lesson , return a block_contents that displays it .
2006-12-30 17:29:44 +00:00
*
* @ param int $cmid Course Module ID for this lesson
* @ param object $lesson Full lesson record object
2009-07-23 10:01:19 +00:00
* @ return block_contents
2006-12-30 17:29:44 +00:00
**/
2009-07-23 10:01:19 +00:00
function lesson_mediafile_block_contents ( $cmid , $lesson ) {
2009-08-18 05:15:52 +00:00
global $OUTPUT ;
2010-08-12 16:05:41 +00:00
if ( empty ( $lesson -> mediafile )) {
2009-07-23 10:01:19 +00:00
return null ;
2006-12-30 17:29:44 +00:00
}
2009-07-23 10:01:19 +00:00
2009-12-16 02:00:48 +00:00
$options = array ();
$options [ 'menubar' ] = 0 ;
$options [ 'location' ] = 0 ;
$options [ 'left' ] = 5 ;
$options [ 'top' ] = 5 ;
$options [ 'scrollbars' ] = 1 ;
$options [ 'resizable' ] = 1 ;
$options [ 'width' ] = $lesson -> mediawidth ;
$options [ 'height' ] = $lesson -> mediaheight ;
2009-07-23 10:01:19 +00:00
2010-02-11 18:50:55 +00:00
$link = new moodle_url ( '/mod/lesson/mediafile.php?id=' . $cmid );
$action = new popup_action ( 'click' , $link , 'lessonmediafile' , $options );
$content = $OUTPUT -> action_link ( $link , get_string ( 'mediafilepopup' , 'lesson' ), $action , array ( 'title' => get_string ( 'mediafilepopup' , 'lesson' )));
2009-11-01 15:03:10 +00:00
2009-07-23 10:01:19 +00:00
$bc = new block_contents ();
$bc -> title = get_string ( 'linkedmedia' , 'lesson' );
2010-03-18 16:49:03 +00:00
$bc -> attributes [ 'class' ] = 'mediafile' ;
2009-07-23 10:01:19 +00:00
$bc -> content = $content ;
return $bc ;
2006-12-30 17:29:44 +00:00
}
/**
* If a timed lesson and not a teacher , then
2009-07-23 10:01:19 +00:00
* return a block_contents containing the clock .
2006-12-30 17:29:44 +00:00
*
* @ param int $cmid Course Module ID for this lesson
* @ param object $lesson Full lesson record object
* @ param object $timer Full timer record object
2009-07-23 10:01:19 +00:00
* @ return block_contents
2006-12-30 17:29:44 +00:00
**/
2009-07-23 10:01:19 +00:00
function lesson_clock_block_contents ( $cmid , $lesson , $timer , $page ) {
// Display for timed lessons and for students only
2012-07-27 13:26:35 +08:00
$context = context_module :: instance ( $cmid );
2009-07-23 10:01:19 +00:00
if ( ! $lesson -> timed || has_capability ( 'mod/lesson:manage' , $context )) {
return null ;
}
2006-12-30 17:29:44 +00:00
2009-07-23 10:01:19 +00:00
$content = '<div class="jshidewhenenabled">' ;
2009-12-16 02:00:48 +00:00
$content .= $lesson -> time_remaining ( $timer -> starttime );
2009-07-23 10:01:19 +00:00
$content .= '</div>' ;
2009-06-25 06:29:41 +00:00
2009-07-23 10:01:19 +00:00
$clocksettings = array ( 'starttime' => $timer -> starttime , 'servertime' => time (), 'testlength' => ( $lesson -> maxtime * 60 ));
2010-01-20 20:01:24 +00:00
$page -> requires -> data_for_js ( 'clocksettings' , $clocksettings );
$page -> requires -> js ( '/mod/lesson/timer.js' );
$page -> requires -> js_function_call ( 'show_clock' );
2009-06-25 06:29:41 +00:00
2009-07-23 10:01:19 +00:00
$bc = new block_contents ();
$bc -> title = get_string ( 'timeremaining' , 'lesson' );
2010-05-06 06:16:07 +00:00
$bc -> attributes [ 'class' ] = 'clock block' ;
2009-07-23 10:01:19 +00:00
$bc -> content = $content ;
return $bc ;
2006-12-30 17:29:44 +00:00
}
/**
* If left menu is turned on , then this will
* print the menu in a block
*
* @ param int $cmid Course Module ID for this lesson
2009-12-16 02:00:48 +00:00
* @ param lesson $lesson Full lesson record object
2006-12-30 17:29:44 +00:00
* @ return void
**/
2009-07-23 10:01:19 +00:00
function lesson_menu_block_contents ( $cmid , $lesson ) {
2008-06-06 04:03:40 +00:00
global $CFG , $DB ;
2006-12-30 17:29:44 +00:00
2009-07-23 10:01:19 +00:00
if ( ! $lesson -> displayleft ) {
return null ;
}
2006-12-30 17:29:44 +00:00
2009-12-16 02:00:48 +00:00
$pages = $lesson -> load_all_pages ();
foreach ( $pages as $page ) {
if (( int ) $page -> prevpageid === 0 ) {
$pageid = $page -> id ;
break ;
}
}
2009-07-23 10:01:19 +00:00
$currentpageid = optional_param ( 'pageid' , $pageid , PARAM_INT );
2006-12-30 17:29:44 +00:00
2009-07-23 10:01:19 +00:00
if ( ! $pageid || ! $pages ) {
return null ;
2006-12-30 17:29:44 +00:00
}
2009-07-23 10:01:19 +00:00
$content = '<a href="#maincontent" class="skip">' . get_string ( 'skip' , 'lesson' ) . " </a> \n <div class= \" menuwrapper \" > \n <ul> \n " ;
2007-03-24 23:07:59 +00:00
2009-07-23 10:01:19 +00:00
while ( $pageid != 0 ) {
$page = $pages [ $pageid ];
// Only process branch tables with display turned on
2009-12-16 02:00:48 +00:00
if ( $page -> displayinmenublock && $page -> display ) {
2009-11-01 15:03:10 +00:00
if ( $page -> id == $currentpageid ) {
2009-07-23 10:01:19 +00:00
$content .= '<li class="selected">' . format_string ( $page -> title , true ) . " </li> \n " ;
} else {
$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 " ;
}
2009-11-01 15:03:10 +00:00
2007-03-24 23:07:59 +00:00
}
2009-07-23 10:01:19 +00:00
$pageid = $page -> nextpageid ;
2007-03-24 23:07:59 +00:00
}
2009-07-23 10:01:19 +00:00
$content .= " </ul> \n </div> \n " ;
2007-03-24 23:07:59 +00:00
2009-07-23 10:01:19 +00:00
$bc = new block_contents ();
$bc -> title = get_string ( 'lessonmenu' , 'lesson' );
2010-05-06 06:16:07 +00:00
$bc -> attributes [ 'class' ] = 'menu block' ;
2009-07-23 10:01:19 +00:00
$bc -> content = $content ;
2007-03-24 23:07:59 +00:00
2009-07-23 10:01:19 +00:00
return $bc ;
2009-12-18 03:50:38 +00:00
}
/**
* Adds header buttons to the page for the lesson
*
* @ param object $cm
* @ param object $context
* @ param bool $extraeditbuttons
* @ param int $lessonpageid
*/
function lesson_add_header_buttons ( $cm , $context , $extraeditbuttons = false , $lessonpageid = null ) {
global $CFG , $PAGE , $OUTPUT ;
2009-12-23 01:57:55 +00:00
if ( has_capability ( 'mod/lesson:edit' , $context ) && $extraeditbuttons ) {
if ( $lessonpageid === null ) {
print_error ( 'invalidpageid' , 'lesson' );
}
if ( ! empty ( $lessonpageid ) && $lessonpageid != LESSON_EOL ) {
2010-07-25 10:10:49 +00:00
$url = new moodle_url ( '/mod/lesson/editpage.php' , array ( 'id' => $cm -> id , 'pageid' => $lessonpageid , 'edit' => 1 ));
2010-01-03 15:46:14 +00:00
$PAGE -> set_button ( $OUTPUT -> single_button ( $url , get_string ( 'editpagecontent' , 'lesson' )));
2009-12-18 03:50:38 +00:00
}
}
2010-01-03 15:46:14 +00:00
}
MDL-16089 some resource lib and lang pack cleanup
AMOS START
MOV [displayauto,mod_resource],[resourcedisplayauto,core]
MOV [displaydownload,mod_resource],[resourcedisplaydownload,core]
MOV [displayembed,mod_resource],[resourcedisplayembed,core]
MOV [displayframe,mod_resource],[resourcedisplayframe,core]
MOV [displaynew,mod_resource],[resourcedisplaynew,core]
MOV [displayopen,mod_resource],[resourcedisplayopen,core]
MOV [displaypopup,mod_resource],[resourcedisplaypopup,core]
AMOS END
2010-05-22 13:54:41 +00:00
/**
* This is a function used to detect media types and generate html code .
*
* @ global object $CFG
* @ global object $PAGE
* @ param object $lesson
* @ param object $context
* @ return string $code the html code of media
*/
function lesson_get_media_html ( $lesson , $context ) {
global $CFG , $PAGE , $OUTPUT ;
require_once ( " $CFG->libdir /resourcelib.php " );
2010-07-03 13:37:13 +00:00
// get the media file link
2010-08-12 16:05:41 +00:00
if ( strpos ( $lesson -> mediafile , '://' ) !== false ) {
$url = new moodle_url ( $lesson -> mediafile );
} else {
// the timemodified is used to prevent caching problems, instead of '/' we should better read from files table and use sortorder
$url = moodle_url :: make_pluginfile_url ( $context -> id , 'mod_lesson' , 'mediafile' , $lesson -> timemodified , '/' , ltrim ( $lesson -> mediafile , '/' ));
}
MDL-16089 some resource lib and lang pack cleanup
AMOS START
MOV [displayauto,mod_resource],[resourcedisplayauto,core]
MOV [displaydownload,mod_resource],[resourcedisplaydownload,core]
MOV [displayembed,mod_resource],[resourcedisplayembed,core]
MOV [displayframe,mod_resource],[resourcedisplayframe,core]
MOV [displaynew,mod_resource],[resourcedisplaynew,core]
MOV [displayopen,mod_resource],[resourcedisplayopen,core]
MOV [displaypopup,mod_resource],[resourcedisplaypopup,core]
AMOS END
2010-05-22 13:54:41 +00:00
$title = $lesson -> mediafile ;
2010-08-12 16:05:41 +00:00
$clicktoopen = html_writer :: link ( $url , get_string ( 'download' ));
MDL-16089 some resource lib and lang pack cleanup
AMOS START
MOV [displayauto,mod_resource],[resourcedisplayauto,core]
MOV [displaydownload,mod_resource],[resourcedisplaydownload,core]
MOV [displayembed,mod_resource],[resourcedisplayembed,core]
MOV [displayframe,mod_resource],[resourcedisplayframe,core]
MOV [displaynew,mod_resource],[resourcedisplaynew,core]
MOV [displayopen,mod_resource],[resourcedisplayopen,core]
MOV [displaypopup,mod_resource],[resourcedisplaypopup,core]
AMOS END
2010-05-22 13:54:41 +00:00
$mimetype = resourcelib_guess_url_mimetype ( $url );
2011-03-12 17:42:52 +01:00
$extension = resourcelib_get_extension ( $url -> out ( false ));
2011-12-13 17:21:06 +00:00
$mediarenderer = $PAGE -> get_renderer ( 'core' , 'media' );
$embedoptions = array (
core_media :: OPTION_TRUSTED => true ,
core_media :: OPTION_BLOCK => true
);
MDL-16089 some resource lib and lang pack cleanup
AMOS START
MOV [displayauto,mod_resource],[resourcedisplayauto,core]
MOV [displaydownload,mod_resource],[resourcedisplaydownload,core]
MOV [displayembed,mod_resource],[resourcedisplayembed,core]
MOV [displayframe,mod_resource],[resourcedisplayframe,core]
MOV [displaynew,mod_resource],[resourcedisplaynew,core]
MOV [displayopen,mod_resource],[resourcedisplayopen,core]
MOV [displaypopup,mod_resource],[resourcedisplaypopup,core]
AMOS END
2010-05-22 13:54:41 +00:00
// find the correct type and print it out
if ( in_array ( $mimetype , array ( 'image/gif' , 'image/jpeg' , 'image/png' ))) { // It's an image
$code = resourcelib_embed_image ( $url , $title );
2011-12-13 17:21:06 +00:00
} else if ( $mediarenderer -> can_embed_url ( $url , $embedoptions )) {
// Media (audio/video) file.
$code = $mediarenderer -> embed_url ( $url , $title , 0 , 0 , $embedoptions );
MDL-16089 some resource lib and lang pack cleanup
AMOS START
MOV [displayauto,mod_resource],[resourcedisplayauto,core]
MOV [displaydownload,mod_resource],[resourcedisplaydownload,core]
MOV [displayembed,mod_resource],[resourcedisplayembed,core]
MOV [displayframe,mod_resource],[resourcedisplayframe,core]
MOV [displaynew,mod_resource],[resourcedisplaynew,core]
MOV [displayopen,mod_resource],[resourcedisplayopen,core]
MOV [displaypopup,mod_resource],[resourcedisplaypopup,core]
AMOS END
2010-05-22 13:54:41 +00:00
} else {
// anything else - just try object tag enlarged as much as possible
$code = resourcelib_embed_general ( $url , $title , $clicktoopen , $mimetype );
}
return $code ;
}
2010-07-24 18:56:56 +00:00
/**
* Abstract class that page type ' s MUST inherit from .
*
* This is the abstract class that ALL add page type forms must extend .
* You will notice that all but two of the methods this class contains are final .
* Essentially the only thing that extending classes can do is extend custom_definition .
* OR if it has a special requirement on creation it can extend construction_override
*
* @ abstract
2010-07-25 10:54:39 +00:00
* @ copyright 2009 Sam Hemelryk
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
2010-07-24 18:56:56 +00:00
*/
abstract class lesson_add_page_form_base extends moodleform {
/**
* This is the classic define that is used to identify this pagetype .
* Will be one of LESSON_ *
* @ var int
*/
public $qtype ;
/**
* The simple string that describes the page type e . g . truefalse , multichoice
* @ var string
*/
public $qtypestring ;
/**
* An array of options used in the htmleditor
* @ var array
*/
protected $editoroptions = array ();
/**
* True if this is a standard page of false if it does something special .
* Questions are standard pages , branch tables are not
* @ var bool
*/
protected $standard = true ;
/**
* Each page type can and should override this to add any custom elements to
* the basic form that they want
*/
public function custom_definition () {}
/**
* Used to determine if this is a standard page or a special page
* @ return bool
*/
public final function is_standard () {
return ( bool ) $this -> standard ;
}
/**
* Add the required basic elements to the form .
*
* This method adds the basic elements to the form including title and contents
* and then calls custom_definition ();
*/
public final function definition () {
$mform = $this -> _form ;
$editoroptions = $this -> _customdata [ 'editoroptions' ];
2013-05-02 16:18:44 +08:00
$mform -> addElement ( 'header' , 'qtypeheading' , get_string ( 'createaquestionpage' , 'lesson' , get_string ( $this -> qtypestring , 'lesson' )));
2010-07-24 18:56:56 +00:00
$mform -> addElement ( 'hidden' , 'id' );
$mform -> setType ( 'id' , PARAM_INT );
$mform -> addElement ( 'hidden' , 'pageid' );
$mform -> setType ( 'pageid' , PARAM_INT );
if ( $this -> standard === true ) {
$mform -> addElement ( 'hidden' , 'qtype' );
2011-10-21 12:38:29 +08:00
$mform -> setType ( 'qtype' , PARAM_INT );
2010-07-24 18:56:56 +00:00
2010-07-25 09:11:17 +00:00
$mform -> addElement ( 'text' , 'title' , get_string ( 'pagetitle' , 'lesson' ), array ( 'size' => 70 ));
2010-07-24 18:56:56 +00:00
$mform -> setType ( 'title' , PARAM_TEXT );
2010-07-25 09:11:17 +00:00
$mform -> addRule ( 'title' , get_string ( 'required' ), 'required' , null , 'client' );
2010-07-24 18:56:56 +00:00
$this -> editoroptions = array ( 'noclean' => true , 'maxfiles' => EDITOR_UNLIMITED_FILES , 'maxbytes' => $this -> _customdata [ 'maxbytes' ]);
2010-07-25 09:11:17 +00:00
$mform -> addElement ( 'editor' , 'contents_editor' , get_string ( 'pagecontents' , 'lesson' ), null , $this -> editoroptions );
$mform -> setType ( 'contents_editor' , PARAM_RAW );
$mform -> addRule ( 'contents_editor' , get_string ( 'required' ), 'required' , null , 'client' );
2010-07-24 18:56:56 +00:00
}
$this -> custom_definition ();
if ( $this -> _customdata [ 'edit' ] === true ) {
$mform -> addElement ( 'hidden' , 'edit' , 1 );
2013-05-01 11:39:31 +08:00
$mform -> setType ( 'edit' , PARAM_BOOL );
2010-07-25 09:11:17 +00:00
$this -> add_action_buttons ( get_string ( 'cancel' ), get_string ( 'savepage' , 'lesson' ));
2012-05-09 14:19:49 +08:00
} else if ( $this -> qtype === 'questiontype' ) {
2010-07-25 09:11:17 +00:00
$this -> add_action_buttons ( get_string ( 'cancel' ), get_string ( 'addaquestionpage' , 'lesson' ));
2012-05-09 14:19:49 +08:00
} else {
$this -> add_action_buttons ( get_string ( 'cancel' ), get_string ( 'savepage' , 'lesson' ));
2010-07-24 18:56:56 +00:00
}
}
/**
* Convenience function : Adds a jumpto select element
*
* @ param string $name
* @ param string | null $label
* @ param int $selected The page to select by default
*/
protected final function add_jumpto ( $name , $label = null , $selected = LESSON_NEXTPAGE ) {
$title = get_string ( " jump " , " lesson " );
if ( $label === null ) {
$label = $title ;
}
if ( is_int ( $name )) {
$name = " jumpto[ $name ] " ;
}
$this -> _form -> addElement ( 'select' , $name , $label , $this -> _customdata [ 'jumpto' ]);
$this -> _form -> setDefault ( $name , $selected );
$this -> _form -> addHelpButton ( $name , 'jumps' , 'lesson' );
}
/**
* Convenience function : Adds a score input element
*
* @ param string $name
* @ param string | null $label
* @ param mixed $value The default value
*/
protected final function add_score ( $name , $label = null , $value = null ) {
if ( $label === null ) {
$label = get_string ( " score " , " lesson " );
}
2013-05-01 11:39:31 +08:00
$elname = $name ;
2010-07-24 18:56:56 +00:00
if ( is_int ( $name )) {
$name = " score[ $name ] " ;
2013-05-01 11:39:31 +08:00
$elname = 'score' ;
2010-07-24 18:56:56 +00:00
}
$this -> _form -> addElement ( 'text' , $name , $label , array ( 'size' => 5 ));
2013-05-01 11:39:31 +08:00
// Temporary fix until MDL-38885 gets integrated.
$this -> _form -> setType ( $elname , PARAM_INT );
2010-07-24 18:56:56 +00:00
if ( $value !== null ) {
$this -> _form -> setDefault ( $name , $value );
}
}
/**
* Convenience function : Adds an answer editor
*
* @ param int $count The count of the element to add
2013-03-25 14:07:18 +08:00
* @ param string $label , null means default
2010-07-24 20:21:19 +00:00
* @ param bool $required
* @ return void
2010-07-24 18:56:56 +00:00
*/
2013-03-25 14:07:18 +08:00
protected final function add_answer ( $count , $label = null , $required = false ) {
if ( $label === null ) {
2010-07-24 20:21:19 +00:00
$label = get_string ( 'answer' , 'lesson' );
}
2010-07-28 13:23:51 +00:00
$this -> _form -> addElement ( 'editor' , 'answer_editor[' . $count . ']' , $label , array ( 'rows' => '4' , 'columns' => '80' ), array ( 'noclean' => true ));
2010-07-24 18:56:56 +00:00
$this -> _form -> setDefault ( 'answer_editor[' . $count . ']' , array ( 'text' => '' , 'format' => FORMAT_MOODLE ));
2010-07-24 20:21:19 +00:00
if ( $required ) {
$this -> _form -> addRule ( 'answer_editor[' . $count . ']' , get_string ( 'required' ), 'required' , null , 'client' );
}
2010-07-24 18:56:56 +00:00
}
/**
* Convenience function : Adds an response editor
*
* @ param int $count The count of the element to add
2013-03-25 14:07:18 +08:00
* @ param string $label , null means default
2010-07-24 20:21:19 +00:00
* @ param bool $required
* @ return void
2010-07-24 18:56:56 +00:00
*/
2013-03-25 14:07:18 +08:00
protected final function add_response ( $count , $label = null , $required = false ) {
if ( $label === null ) {
2010-07-24 20:21:19 +00:00
$label = get_string ( 'response' , 'lesson' );
}
2010-07-28 13:23:51 +00:00
$this -> _form -> addElement ( 'editor' , 'response_editor[' . $count . ']' , $label , array ( 'rows' => '4' , 'columns' => '80' ), array ( 'noclean' => true ));
2010-07-24 18:56:56 +00:00
$this -> _form -> setDefault ( 'response_editor[' . $count . ']' , array ( 'text' => '' , 'format' => FORMAT_MOODLE ));
2010-07-24 20:21:19 +00:00
if ( $required ) {
$this -> _form -> addRule ( 'response_editor[' . $count . ']' , get_string ( 'required' ), 'required' , null , 'client' );
}
2010-07-24 18:56:56 +00:00
}
/**
* A function that gets called upon init of this object by the calling script .
*
* This can be used to process an immediate action if required . Currently it
* is only used in special cases by non - standard page types .
*
* @ return bool
*/
2012-03-18 18:36:04 +01:00
public function construction_override ( $pageid , lesson $lesson ) {
2010-07-24 18:56:56 +00:00
return true ;
}
}
/**
* Class representation of a lesson
*
* This class is used the interact with , and manage a lesson once instantiated .
* If you need to fetch a lesson object you can do so by calling
*
* < code >
* lesson :: load ( $lessonid );
* // or
* $lessonrecord = $DB -> get_record ( 'lesson' , $lessonid );
* $lesson = new lesson ( $lessonrecord );
* </ code >
*
* The class itself extends lesson_base as all classes within the lesson module should
*
* These properties are from the database
* @ property int $id The id of this lesson
* @ property int $course The ID of the course this lesson belongs to
* @ property string $name The name of this lesson
* @ property int $practice Flag to toggle this as a practice lesson
* @ property int $modattempts Toggle to allow the user to go back and review answers
* @ property int $usepassword Toggle the use of a password for entry
* @ property string $password The password to require users to enter
2010-07-24 19:03:37 +00:00
* @ property int $dependency ID of another lesson this lesson is dependent on
2010-07-24 18:56:56 +00:00
* @ property string $conditions Conditions of the lesson dependency
* @ property int $grade The maximum grade a user can achieve ( % )
* @ property int $custom Toggle custom scoring on or off
* @ property int $ongoing Toggle display of an ongoing score
* @ property int $usemaxgrade How retakes are handled ( max = 1 , mean = 0 )
* @ property int $maxanswers The max number of answers or branches
* @ property int $maxattempts The maximum number of attempts a user can record
* @ property int $review Toggle use or wrong answer review button
* @ property int $nextpagedefault Override the default next page
* @ property int $feedback Toggles display of default feedback
* @ property int $minquestions Sets a minimum value of pages seen when calculating grades
* @ property int $maxpages Maximum number of pages this lesson can contain
* @ property int $retake Flag to allow users to retake a lesson
* @ property int $activitylink Relate this lesson to another lesson
* @ property string $mediafile File to pop up to or webpage to display
* @ property int $mediaheight Sets the height of the media file popup
* @ property int $mediawidth Sets the width of the media file popup
* @ property int $mediaclose Toggle display of a media close button
* @ property int $slideshow Flag for whether branch pages should be shown as slideshows
* @ property int $width Width of slideshow
* @ property int $height Height of slideshow
* @ property string $bgcolor Background colour of slideshow
2010-07-24 19:03:37 +00:00
* @ property int $displayleft Display a left menu
2010-07-24 18:56:56 +00:00
* @ property int $displayleftif Sets the condition on which the left menu is displayed
* @ property int $progressbar Flag to toggle display of a lesson progress bar
* @ property int $highscores Flag to toggle collection of high scores
* @ property int $maxhighscores Number of high scores to limit to
* @ property int $available Timestamp of when this lesson becomes available
* @ property int $deadline Timestamp of when this lesson is no longer available
* @ property int $timemodified Timestamp when lesson was last modified
*
* These properties are calculated
* @ property int $firstpageid Id of the first page of this lesson ( prevpageid = 0 )
* @ property int $lastpageid Id of the last page of this lesson ( nextpageid = 0 )
*
2010-07-25 10:54:39 +00:00
* @ copyright 2009 Sam Hemelryk
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
2010-07-24 18:56:56 +00:00
*/
class lesson extends lesson_base {
/**
* The id of the first page ( where prevpageid = 0 ) gets set and retrieved by
* { @ see get_firstpageid ()} by directly calling < code > $lesson -> firstpageid ; </ code >
* @ var int
*/
protected $firstpageid = null ;
/**
* The id of the last page ( where nextpageid = 0 ) gets set and retrieved by
* { @ see get_lastpageid ()} by directly calling < code > $lesson -> lastpageid ; </ code >
* @ var int
*/
protected $lastpageid = null ;
/**
* An array used to cache the pages associated with this lesson after the first
* time they have been loaded .
* A note to developers : If you are going to be working with MORE than one or
* two pages from a lesson you should probably call { @ see $lesson -> load_all_pages ()}
* in order to save excess database queries .
* @ var array An array of lesson_page objects
*/
protected $pages = array ();
/**
* Flag that gets set to true once all of the pages associated with the lesson
* have been loaded .
* @ var bool
*/
protected $loadedallpages = false ;
/**
* Simply generates a lesson object given an array / object of properties
* Overrides { @ see lesson_base -> create ()}
* @ static
* @ param object | array $properties
* @ return lesson
*/
public static function create ( $properties ) {
return new lesson ( $properties );
}
/**
* Generates a lesson object from the database given its id
* @ static
* @ param int $lessonid
* @ return lesson
*/
public static function load ( $lessonid ) {
2010-09-18 13:04:10 +00:00
global $DB ;
2010-07-24 18:56:56 +00:00
if ( ! $lesson = $DB -> get_record ( 'lesson' , array ( 'id' => $lessonid ))) {
print_error ( 'invalidcoursemodule' );
}
return new lesson ( $lesson );
}
/**
* Deletes this lesson from the database
*/
public function delete () {
global $CFG , $DB ;
require_once ( $CFG -> libdir . '/gradelib.php' );
require_once ( $CFG -> dirroot . '/calendar/lib.php' );
2012-11-15 09:51:26 +08:00
$DB -> delete_records ( " lesson " , array ( " id " => $this -> properties -> id ));
2010-07-24 18:56:56 +00:00
$DB -> delete_records ( " lesson_pages " , array ( " lessonid " => $this -> properties -> id ));
$DB -> delete_records ( " lesson_answers " , array ( " lessonid " => $this -> properties -> id ));
$DB -> delete_records ( " lesson_attempts " , array ( " lessonid " => $this -> properties -> id ));
$DB -> delete_records ( " lesson_grades " , array ( " lessonid " => $this -> properties -> id ));
$DB -> delete_records ( " lesson_timer " , array ( " lessonid " => $this -> properties -> id ));
$DB -> delete_records ( " lesson_branch " , array ( " lessonid " => $this -> properties -> id ));
$DB -> delete_records ( " lesson_high_scores " , array ( " lessonid " => $this -> properties -> id ));
if ( $events = $DB -> get_records ( 'event' , array ( " modulename " => 'lesson' , " instance " => $this -> properties -> id ))) {
foreach ( $events as $event ) {
$event = calendar_event :: load ( $event );
$event -> delete ();
}
}
2013-03-25 14:07:18 +08:00
grade_update ( 'mod/lesson' , $this -> properties -> course , 'mod' , 'lesson' , $this -> properties -> id , 0 , null , array ( 'deleted' => 1 ));
2010-07-24 18:56:56 +00:00
return true ;
}
/**
* Fetches messages from the session that may have been set in previous page
* actions .
*
* < code >
* // Do not call this method directly instead use
* $lesson -> messages ;
* </ code >
*
* @ return array
*/
protected function get_messages () {
global $SESSION ;
$messages = array ();
if ( ! empty ( $SESSION -> lesson_messages ) && is_array ( $SESSION -> lesson_messages ) && array_key_exists ( $this -> properties -> id , $SESSION -> lesson_messages )) {
$messages = $SESSION -> lesson_messages [ $this -> properties -> id ];
unset ( $SESSION -> lesson_messages [ $this -> properties -> id ]);
}
return $messages ;
}
/**
* Get all of the attempts for the current user .
*
* @ param int $retries
* @ param bool $correct Optional : only fetch correct attempts
* @ param int $pageid Optional : only fetch attempts at the given page
* @ param int $userid Optional : defaults to the current user if not set
* @ return array | false
*/
public function get_attempts ( $retries , $correct = false , $pageid = null , $userid = null ) {
global $USER , $DB ;
$params = array ( " lessonid " => $this -> properties -> id , " userid " => $userid , " retry " => $retries );
if ( $correct ) {
$params [ 'correct' ] = 1 ;
}
if ( $pageid !== null ) {
$params [ 'pageid' ] = $pageid ;
}
if ( $userid === null ) {
$params [ 'userid' ] = $USER -> id ;
}
2010-08-04 02:26:46 +00:00
return $DB -> get_records ( 'lesson_attempts' , $params , 'timeseen ASC' );
2010-07-24 18:56:56 +00:00
}
/**
* Returns the first page for the lesson or false if there isn ' t one .
*
* This method should be called via the magic method __get ();
* < code >
* $firstpage = $lesson -> firstpage ;
* </ code >
*
* @ return lesson_page | bool Returns the lesson_page specialised object or false
*/
protected function get_firstpage () {
$pages = $this -> load_all_pages ();
if ( count ( $pages ) > 0 ) {
foreach ( $pages as $page ) {
if (( int ) $page -> prevpageid === 0 ) {
return $page ;
}
}
}
return false ;
}
/**
* Returns the last page for the lesson or false if there isn ' t one .
*
* This method should be called via the magic method __get ();
* < code >
* $lastpage = $lesson -> lastpage ;
* </ code >
*
* @ return lesson_page | bool Returns the lesson_page specialised object or false
*/
protected function get_lastpage () {
$pages = $this -> load_all_pages ();
if ( count ( $pages ) > 0 ) {
foreach ( $pages as $page ) {
if (( int ) $page -> nextpageid === 0 ) {
return $page ;
}
}
}
return false ;
}
/**
* Returns the id of the first page of this lesson . ( prevpageid = 0 )
* @ return int
*/
protected function get_firstpageid () {
global $DB ;
if ( $this -> firstpageid == null ) {
if ( ! $this -> loadedallpages ) {
$firstpageid = $DB -> get_field ( 'lesson_pages' , 'id' , array ( 'lessonid' => $this -> properties -> id , 'prevpageid' => 0 ));
if ( ! $firstpageid ) {
print_error ( 'cannotfindfirstpage' , 'lesson' );
}
$this -> firstpageid = $firstpageid ;
} else {
$firstpage = $this -> get_firstpage ();
$this -> firstpageid = $firstpage -> id ;
}
}
return $this -> firstpageid ;
}
/**
* Returns the id of the last page of this lesson . ( nextpageid = 0 )
* @ return int
*/
public function get_lastpageid () {
global $DB ;
if ( $this -> lastpageid == null ) {
if ( ! $this -> loadedallpages ) {
$lastpageid = $DB -> get_field ( 'lesson_pages' , 'id' , array ( 'lessonid' => $this -> properties -> id , 'nextpageid' => 0 ));
if ( ! $lastpageid ) {
print_error ( 'cannotfindlastpage' , 'lesson' );
}
$this -> lastpageid = $lastpageid ;
} else {
$lastpageid = $this -> get_lastpage ();
$this -> lastpageid = $lastpageid -> id ;
}
}
return $this -> lastpageid ;
}
/**
2011-09-26 16:41:49 +08:00
* Gets the next page id to display after the one that is provided .
2010-07-24 18:56:56 +00:00
* @ param int $nextpageid
* @ return bool
*/
public function get_next_page ( $nextpageid ) {
2010-09-18 13:04:10 +00:00
global $USER , $DB ;
2010-07-24 18:56:56 +00:00
$allpages = $this -> load_all_pages ();
if ( $this -> properties -> nextpagedefault ) {
// in Flash Card mode...first get number of retakes
2011-09-26 16:41:49 +08:00
$nretakes = $DB -> count_records ( " lesson_grades " , array ( " lessonid " => $this -> properties -> id , " userid " => $USER -> id ));
2010-07-24 18:56:56 +00:00
shuffle ( $allpages );
$found = false ;
if ( $this -> properties -> nextpagedefault == LESSON_UNSEENPAGE ) {
foreach ( $allpages as $nextpage ) {
2011-09-26 16:41:49 +08:00
if ( ! $DB -> count_records ( " lesson_attempts " , array ( " pageid " => $nextpage -> id , " userid " => $USER -> id , " retry " => $nretakes ))) {
2010-07-24 18:56:56 +00:00
$found = true ;
break ;
}
}
} elseif ( $this -> properties -> nextpagedefault == LESSON_UNANSWEREDPAGE ) {
foreach ( $allpages as $nextpage ) {
2011-09-26 16:41:49 +08:00
if ( ! $DB -> count_records ( " lesson_attempts " , array ( 'pageid' => $nextpage -> id , 'userid' => $USER -> id , 'correct' => 1 , 'retry' => $nretakes ))) {
2010-07-24 18:56:56 +00:00
$found = true ;
break ;
}
}
}
if ( $found ) {
if ( $this -> properties -> maxpages ) {
// check number of pages viewed (in the lesson)
2011-09-26 16:41:49 +08:00
if ( $DB -> count_records ( " lesson_attempts " , array ( " lessonid " => $this -> properties -> id , " userid " => $USER -> id , " retry " => $nretakes )) >= $this -> properties -> maxpages ) {
return LESSON_EOL ;
2010-07-24 18:56:56 +00:00
}
}
2011-09-26 16:41:49 +08:00
return $nextpage -> id ;
2010-07-24 18:56:56 +00:00
}
}
// In a normal lesson mode
foreach ( $allpages as $nextpage ) {
2011-09-26 16:41:49 +08:00
if (( int ) $nextpage -> id === ( int ) $nextpageid ) {
return $nextpage -> id ;
2010-07-24 18:56:56 +00:00
}
}
2011-09-26 16:41:49 +08:00
return LESSON_EOL ;
2010-07-24 18:56:56 +00:00
}
/**
* Sets a message against the session for this lesson that will displayed next
* time the lesson processes messages
*
* @ param string $message
* @ param string $class
* @ param string $align
* @ return bool
*/
public function add_message ( $message , $class = " notifyproblem " , $align = 'center' ) {
global $SESSION ;
if ( empty ( $SESSION -> lesson_messages ) || ! is_array ( $SESSION -> lesson_messages )) {
$SESSION -> lesson_messages = array ();
$SESSION -> lesson_messages [ $this -> properties -> id ] = array ();
} else if ( ! array_key_exists ( $this -> properties -> id , $SESSION -> lesson_messages )) {
$SESSION -> lesson_messages [ $this -> properties -> id ] = array ();
}
$SESSION -> lesson_messages [ $this -> properties -> id ][] = array ( $message , $class , $align );
return true ;
}
/**
* Check if the lesson is accessible at the present time
* @ return bool True if the lesson is accessible , false otherwise
*/
public function is_accessible () {
$available = $this -> properties -> available ;
$deadline = $this -> properties -> deadline ;
return (( $available == 0 || time () >= $available ) && ( $deadline == 0 || time () < $deadline ));
}
/**
* Starts the lesson time for the current user
* @ return bool Returns true
*/
public function start_timer () {
global $USER , $DB ;
$USER -> startlesson [ $this -> properties -> id ] = true ;
$startlesson = new stdClass ;
$startlesson -> lessonid = $this -> properties -> id ;
$startlesson -> userid = $USER -> id ;
$startlesson -> starttime = time ();
$startlesson -> lessontime = time ();
$DB -> insert_record ( 'lesson_timer' , $startlesson );
if ( $this -> properties -> timed ) {
$this -> add_message ( get_string ( 'maxtimewarning' , 'lesson' , $this -> properties -> maxtime ), 'center' );
}
return true ;
}
/**
* Updates the timer to the current time and returns the new timer object
* @ param bool $restart If set to true the timer is restarted
* @ param bool $continue If set to true AND $restart = true then the timer
* will continue from a previous attempt
* @ return stdClass The new timer
*/
public function update_timer ( $restart = false , $continue = false ) {
global $USER , $DB ;
// clock code
// get time information for this user
if ( ! $timer = $DB -> get_records ( 'lesson_timer' , array ( " lessonid " => $this -> properties -> id , " userid " => $USER -> id ), 'starttime DESC' , '*' , 0 , 1 )) {
print_error ( 'cannotfindtimer' , 'lesson' );
} else {
$timer = current ( $timer ); // this will get the latest start time record
}
if ( $restart ) {
if ( $continue ) {
// continue a previous test, need to update the clock (think this option is disabled atm)
$timer -> starttime = time () - ( $timer -> lessontime - $timer -> starttime );
} else {
// starting over, so reset the clock
$timer -> starttime = time ();
}
}
$timer -> lessontime = time ();
$DB -> update_record ( 'lesson_timer' , $timer );
return $timer ;
}
/**
* Updates the timer to the current time then stops it by unsetting the user var
* @ return bool Returns true
*/
public function stop_timer () {
global $USER , $DB ;
unset ( $USER -> startlesson [ $this -> properties -> id ]);
return $this -> update_timer ( false , false );
}
/**
* Checks to see if the lesson has pages
*/
public function has_pages () {
global $DB ;
$pagecount = $DB -> count_records ( 'lesson_pages' , array ( 'lessonid' => $this -> properties -> id ));
return ( $pagecount > 0 );
}
/**
* Returns the link for the related activity
* @ return array | false
*/
public function link_for_activitylink () {
global $DB ;
$module = $DB -> get_record ( 'course_modules' , array ( 'id' => $this -> properties -> activitylink ));
if ( $module ) {
$modname = $DB -> get_field ( 'modules' , 'name' , array ( 'id' => $module -> module ));
if ( $modname ) {
$instancename = $DB -> get_field ( $modname , 'name' , array ( 'id' => $module -> instance ));
if ( $instancename ) {
return html_writer :: link ( new moodle_url ( '/mod/' . $modname . '/view.php' , array ( 'id' => $this -> properties -> activitylink )),
2011-06-24 16:41:47 +08:00
get_string ( 'activitylinkname' , 'lesson' , $instancename ),
2010-07-24 18:56:56 +00:00
array ( 'class' => 'centerpadded lessonbutton standardbutton' ));
}
}
}
return '' ;
}
/**
* Loads the requested page .
*
* This function will return the requested page id as either a specialised
* lesson_page object OR as a generic lesson_page .
* If the page has been loaded previously it will be returned from the pages
* array , otherwise it will be loaded from the database first
*
* @ param int $pageid
* @ return lesson_page A lesson_page object or an object that extends it
*/
public function load_page ( $pageid ) {
if ( ! array_key_exists ( $pageid , $this -> pages )) {
$manager = lesson_page_type_manager :: get ( $this );
$this -> pages [ $pageid ] = $manager -> load_page ( $pageid , $this );
}
return $this -> pages [ $pageid ];
}
/**
* Loads ALL of the pages for this lesson
*
* @ return array An array containing all pages from this lesson
*/
public function load_all_pages () {
if ( ! $this -> loadedallpages ) {
$manager = lesson_page_type_manager :: get ( $this );
$this -> pages = $manager -> load_all_pages ( $this );
$this -> loadedallpages = true ;
}
return $this -> pages ;
}
/**
2010-07-24 19:03:37 +00:00
* Determines if a jumpto value is correct or not .
2010-07-24 18:56:56 +00:00
*
* returns true if jumpto page is ( logically ) after the pageid page or
* if the jumpto value is a special value . Returns false in all other cases .
*
* @ param int $pageid Id of the page from which you are jumping from .
* @ param int $jumpto The jumpto number .
* @ return boolean True or false after a series of tests .
**/
public function jumpto_is_correct ( $pageid , $jumpto ) {
global $DB ;
// first test the special values
if ( ! $jumpto ) {
// same page
return false ;
} elseif ( $jumpto == LESSON_NEXTPAGE ) {
return true ;
} elseif ( $jumpto == LESSON_UNSEENBRANCHPAGE ) {
return true ;
} elseif ( $jumpto == LESSON_RANDOMPAGE ) {
return true ;
} elseif ( $jumpto == LESSON_CLUSTERJUMP ) {
return true ;
} elseif ( $jumpto == LESSON_EOL ) {
return true ;
}
$pages = $this -> load_all_pages ();
$apageid = $pages [ $pageid ] -> nextpageid ;
while ( $apageid != 0 ) {
if ( $jumpto == $apageid ) {
return true ;
}
$apageid = $pages [ $apageid ] -> nextpageid ;
}
return false ;
}
/**
* Returns the time a user has remaining on this lesson
* @ param int $starttime Starttime timestamp
* @ return string
*/
public function time_remaining ( $starttime ) {
$timeleft = $starttime + $this -> maxtime * 60 - time ();
$hours = floor ( $timeleft / 3600 );
$timeleft = $timeleft - ( $hours * 3600 );
$minutes = floor ( $timeleft / 60 );
$secs = $timeleft - ( $minutes * 60 );
if ( $minutes < 10 ) {
$minutes = " 0 $minutes " ;
}
if ( $secs < 10 ) {
$secs = " 0 $secs " ;
}
$output = array ();
$output [] = $hours ;
$output [] = $minutes ;
$output [] = $secs ;
$output = implode ( ':' , $output );
return $output ;
}
/**
* Interprets LESSON_CLUSTERJUMP jumpto value .
*
* This will select a page randomly
2010-07-24 19:03:37 +00:00
* and the page selected will be inbetween a cluster page and end of clutter or end of lesson
2010-07-24 18:56:56 +00:00
* and the page selected will be a page that has not been viewed already
* and if any pages are within a branch table or end of branch then only 1 page within
* the branch table or end of branch will be randomly selected ( sub clustering ) .
*
* @ param int $pageid Id of the current page from which we are jumping from .
* @ param int $userid Id of the user .
* @ return int The id of the next page .
**/
public function cluster_jump ( $pageid , $userid = null ) {
global $DB , $USER ;
if ( $userid === null ) {
$userid = $USER -> id ;
}
// get the number of retakes
if ( ! $retakes = $DB -> count_records ( " lesson_grades " , array ( " lessonid " => $this -> properties -> id , " userid " => $userid ))) {
$retakes = 0 ;
}
// get all the lesson_attempts aka what the user has seen
$seenpages = array ();
if ( $attempts = $this -> get_attempts ( $retakes )) {
foreach ( $attempts as $attempt ) {
$seenpages [ $attempt -> pageid ] = $attempt -> pageid ;
}
}
// get the lesson pages
$lessonpages = $this -> load_all_pages ();
// find the start of the cluster
while ( $pageid != 0 ) { // this condition should not be satisfied... should be a cluster page
if ( $lessonpages [ $pageid ] -> qtype == LESSON_PAGE_CLUSTER ) {
break ;
}
$pageid = $lessonpages [ $pageid ] -> prevpageid ;
}
$clusterpages = array ();
$clusterpages = $this -> get_sub_pages_of ( $pageid , array ( LESSON_PAGE_ENDOFCLUSTER ));
$unseen = array ();
foreach ( $clusterpages as $key => $cluster ) {
if ( $cluster -> type !== lesson_page :: TYPE_QUESTION ) {
unset ( $clusterpages [ $key ]);
} elseif ( $cluster -> is_unseen ( $seenpages )) {
$unseen [] = $cluster ;
}
}
if ( count ( $unseen ) > 0 ) {
// it does not contain elements, then use exitjump, otherwise find out next page/branch
$nextpage = $unseen [ rand ( 0 , count ( $unseen ) - 1 )];
if ( $nextpage -> qtype == LESSON_PAGE_BRANCHTABLE ) {
// if branch table, then pick a random page inside of it
$branchpages = $this -> get_sub_pages_of ( $nextpage -> id , array ( LESSON_PAGE_BRANCHTABLE , LESSON_PAGE_ENDOFBRANCH ));
return $branchpages [ rand ( 0 , count ( $branchpages ) - 1 )] -> id ;
} else { // otherwise, return the page's id
return $nextpage -> id ;
}
} else {
// seen all there is to see, leave the cluster
if ( end ( $clusterpages ) -> nextpageid == 0 ) {
return LESSON_EOL ;
} else {
$clusterendid = $pageid ;
while ( $clusterendid != 0 ) { // this condition should not be satisfied... should be a cluster page
if ( $lessonpages [ $clusterendid ] -> qtype == LESSON_PAGE_CLUSTER ) {
break ;
}
$clusterendid = $lessonpages [ $clusterendid ] -> prevpageid ;
}
$exitjump = $DB -> get_field ( " lesson_answers " , " jumpto " , array ( " pageid " => $clusterendid , " lessonid " => $this -> properties -> id ));
if ( $exitjump == LESSON_NEXTPAGE ) {
$exitjump = $lessonpages [ $pageid ] -> nextpageid ;
}
if ( $exitjump == 0 ) {
return LESSON_EOL ;
} else if ( in_array ( $exitjump , array ( LESSON_EOL , LESSON_PREVIOUSPAGE ))) {
return $exitjump ;
} else {
if ( ! array_key_exists ( $exitjump , $lessonpages )) {
$found = false ;
foreach ( $lessonpages as $page ) {
if ( $page -> id === $clusterendid ) {
$found = true ;
} else if ( $page -> qtype == LESSON_PAGE_ENDOFCLUSTER ) {
$exitjump = $DB -> get_field ( " lesson_answers " , " jumpto " , array ( " pageid " => $page -> id , " lessonid " => $this -> properties -> id ));
break ;
}
}
}
if ( ! array_key_exists ( $exitjump , $lessonpages )) {
return LESSON_EOL ;
}
return $exitjump ;
}
}
}
}
/**
* Finds all pages that appear to be a subtype of the provided pageid until
* an end point specified within $ends is encountered or no more pages exist
*
* @ param int $pageid
* @ param array $ends An array of LESSON_PAGE_ * types that signify an end of
* the subtype
* @ return array An array of specialised lesson_page objects
*/
public function get_sub_pages_of ( $pageid , array $ends ) {
$lessonpages = $this -> load_all_pages ();
$pageid = $lessonpages [ $pageid ] -> nextpageid ; // move to the first page after the branch table
$pages = array ();
while ( true ) {
if ( $pageid == 0 || in_array ( $lessonpages [ $pageid ] -> qtype , $ends )) {
break ;
}
$pages [] = $lessonpages [ $pageid ];
$pageid = $lessonpages [ $pageid ] -> nextpageid ;
}
return $pages ;
}
/**
* Checks to see if the specified page [ id ] is a subpage of a type specified in
* the $types array , until either there are no more pages of we find a type
2010-07-24 19:03:37 +00:00
* corresponding to that of a type specified in $ends
2010-07-24 18:56:56 +00:00
*
* @ param int $pageid The id of the page to check
* @ param array $types An array of types that would signify this page was a subpage
* @ param array $ends An array of types that mean this is not a subpage
* @ return bool
*/
public function is_sub_page_of_type ( $pageid , array $types , array $ends ) {
$pages = $this -> load_all_pages ();
$pageid = $pages [ $pageid ] -> prevpageid ; // move up one
array_unshift ( $ends , 0 );
// go up the pages till branch table
while ( true ) {
if ( $pageid == 0 || in_array ( $pages [ $pageid ] -> qtype , $ends )) {
return false ;
} else if ( in_array ( $pages [ $pageid ] -> qtype , $types )) {
return true ;
}
$pageid = $pages [ $pageid ] -> prevpageid ;
}
}
}
/**
* Abstract class to provide a core functions to the all lesson classes
*
* This class should be abstracted by ALL classes with the lesson module to ensure
* that all classes within this module can be interacted with in the same way .
*
* This class provides the user with a basic properties array that can be fetched
2010-07-24 19:03:37 +00:00
* or set via magic methods , or alternatively by defining methods get_blah () or
2010-07-24 18:56:56 +00:00
* set_blah () within the extending object .
*
2010-07-25 10:54:39 +00:00
* @ copyright 2009 Sam Hemelryk
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
2010-07-24 18:56:56 +00:00
*/
abstract class lesson_base {
/**
* An object containing properties
* @ var stdClass
*/
protected $properties ;
/**
* The constructor
* @ param stdClass $properties
*/
public function __construct ( $properties ) {
$this -> properties = ( object ) $properties ;
}
/**
* Magic property method
*
* Attempts to call a set_ $key method if one exists otherwise falls back
* to simply set the property
*
* @ param string $key
* @ param mixed $value
*/
public function __set ( $key , $value ) {
if ( method_exists ( $this , 'set_' . $key )) {
$this -> { 'set_' . $key }( $value );
}
$this -> properties -> { $key } = $value ;
}
/**
* Magic get method
*
* Attempts to call a get_ $key method to return the property and ralls over
* to return the raw property
*
* @ param str $key
* @ return mixed
*/
public function __get ( $key ) {
if ( method_exists ( $this , 'get_' . $key )) {
return $this -> { 'get_' . $key }();
}
return $this -> properties -> { $key };
}
/**
* Stupid PHP needs an isset magic method if you use the get magic method and
* still want empty calls to work .... blah ~!
*
* @ param string $key
* @ return bool
*/
public function __isset ( $key ) {
if ( method_exists ( $this , 'get_' . $key )) {
$val = $this -> { 'get_' . $key }();
return ! empty ( $val );
}
return ! empty ( $this -> properties -> { $key });
}
2012-03-18 18:36:04 +01:00
//NOTE: E_STRICT does not allow to change function signature!
2010-07-24 18:56:56 +00:00
/**
2012-03-18 18:36:04 +01:00
* If implemented should create a new instance , save it in the DB and return it
2010-07-24 18:56:56 +00:00
*/
2012-03-18 18:36:04 +01:00
//public static function create() {}
2010-07-24 18:56:56 +00:00
/**
2012-03-18 18:36:04 +01:00
* If implemented should load an instance from the DB and return it
2010-07-24 18:56:56 +00:00
*/
2012-03-18 18:36:04 +01:00
//public static function load() {}
2010-07-24 18:56:56 +00:00
/**
* Fetches all of the properties of the object
* @ return stdClass
*/
public function properties () {
return $this -> properties ;
}
}
/**
* Abstract class representation of a page associated with a lesson .
*
* This class should MUST be extended by all specialised page types defined in
* mod / lesson / pagetypes /.
* There are a handful of abstract methods that need to be defined as well as
* severl methods that can optionally be defined in order to make the page type
* operate in the desired way
*
* Database properties
* @ property int $id The id of this lesson page
* @ property int $lessonid The id of the lesson this page belongs to
* @ property int $prevpageid The id of the page before this one
* @ property int $nextpageid The id of the next page in the page sequence
* @ property int $qtype Identifies the page type of this page
* @ property int $qoption Used to record page type specific options
* @ property int $layout Used to record page specific layout selections
* @ property int $display Used to record page specific display selections
* @ property int $timecreated Timestamp for when the page was created
* @ property int $timemodified Timestamp for when the page was last modified
* @ property string $title The title of this page
* @ property string $contents The rich content shown to describe the page
* @ property int $contentsformat The format of the contents field
*
* Calculated properties
* @ property - read array $answers An array of answers for this page
* @ property - read bool $displayinmenublock Toggles display in the left menu block
* @ property - read array $jumps An array containing all the jumps this page uses
* @ property - read lesson $lesson The lesson this page belongs to
* @ property - read int $type The type of the page [ question | structure ]
* @ property - read typeid The unique identifier for the page type
* @ property - read typestring The string that describes this page type
*
* @ abstract
2010-07-25 10:54:39 +00:00
* @ copyright 2009 Sam Hemelryk
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
2010-07-24 18:56:56 +00:00
*/
abstract class lesson_page extends lesson_base {
/**
* A reference to the lesson this page belongs to
* @ var lesson
*/
protected $lesson = null ;
/**
* Contains the answers to this lesson_page once loaded
* @ var null | array
*/
protected $answers = null ;
/**
* This sets the type of the page , can be one of the constants defined below
* @ var int
*/
protected $type = 0 ;
/**
* Constants used to identify the type of the page
*/
const TYPE_QUESTION = 0 ;
const TYPE_STRUCTURE = 1 ;
/**
* This method should return the integer used to identify the page type within
2010-07-24 19:03:37 +00:00
* the database and throughout code . This maps back to the defines used in 1. x
2010-07-24 18:56:56 +00:00
* @ abstract
* @ return int
*/
abstract protected function get_typeid ();
/**
* This method should return the string that describes the pagetype
* @ abstract
* @ return string
*/
abstract protected function get_typestring ();
/**
* This method gets called to display the page to the user taking the lesson
* @ abstract
* @ param object $renderer
* @ param object $attempt
* @ return string
*/
abstract public function display ( $renderer , $attempt );
/**
* Creates a new lesson_page within the database and returns the correct pagetype
* object to use to interact with the new lesson
*
* @ final
* @ static
* @ param object $properties
* @ param lesson $lesson
* @ return lesson_page Specialised object that extends lesson_page
*/
final public static function create ( $properties , lesson $lesson , $context , $maxbytes ) {
global $DB ;
$newpage = new stdClass ;
$newpage -> title = $properties -> title ;
$newpage -> contents = $properties -> contents_editor [ 'text' ];
$newpage -> contentsformat = $properties -> contents_editor [ 'format' ];
$newpage -> lessonid = $lesson -> id ;
$newpage -> timecreated = time ();
$newpage -> qtype = $properties -> qtype ;
$newpage -> qoption = ( isset ( $properties -> qoption )) ? 1 : 0 ;
$newpage -> layout = ( isset ( $properties -> layout )) ? 1 : 0 ;
$newpage -> display = ( isset ( $properties -> display )) ? 1 : 0 ;
$newpage -> prevpageid = 0 ; // this is a first page
$newpage -> nextpageid = 0 ; // this is the only page
if ( $properties -> pageid ) {
$prevpage = $DB -> get_record ( " lesson_pages " , array ( " id " => $properties -> pageid ), 'id, nextpageid' );
if ( ! $prevpage ) {
print_error ( 'cannotfindpages' , 'lesson' );
}
$newpage -> prevpageid = $prevpage -> id ;
$newpage -> nextpageid = $prevpage -> nextpageid ;
} else {
$nextpage = $DB -> get_record ( 'lesson_pages' , array ( 'lessonid' => $lesson -> id , 'prevpageid' => 0 ), 'id' );
if ( $nextpage ) {
// This is the first page, there are existing pages put this at the start
$newpage -> nextpageid = $nextpage -> id ;
}
}
$newpage -> id = $DB -> insert_record ( " lesson_pages " , $newpage );
$editor = new stdClass ;
$editor -> id = $newpage -> id ;
$editor -> contents_editor = $properties -> contents_editor ;
$editor = file_postupdate_standard_editor ( $editor , 'contents' , array ( 'noclean' => true , 'maxfiles' => EDITOR_UNLIMITED_FILES , 'maxbytes' => $maxbytes ), $context , 'mod_lesson' , 'page_contents' , $editor -> id );
$DB -> update_record ( " lesson_pages " , $editor );
if ( $newpage -> prevpageid > 0 ) {
$DB -> set_field ( " lesson_pages " , " nextpageid " , $newpage -> id , array ( " id " => $newpage -> prevpageid ));
}
if ( $newpage -> nextpageid > 0 ) {
$DB -> set_field ( " lesson_pages " , " prevpageid " , $newpage -> id , array ( " id " => $newpage -> nextpageid ));
}
$page = lesson_page :: load ( $newpage , $lesson );
$page -> create_answers ( $properties );
$lesson -> add_message ( get_string ( 'insertedpage' , 'lesson' ) . ': ' . format_string ( $newpage -> title , true ), 'notifysuccess' );
return $page ;
}
/**
* This method loads a page object from the database and returns it as a
* specialised object that extends lesson_page
*
* @ final
* @ static
* @ param int $id
* @ param lesson $lesson
* @ return lesson_page Specialised lesson_page object
*/
final public static function load ( $id , lesson $lesson ) {
global $DB ;
if ( is_object ( $id ) && ! empty ( $id -> qtype )) {
$page = $id ;
} else {
$page = $DB -> get_record ( " lesson_pages " , array ( " id " => $id ));
if ( ! $page ) {
print_error ( 'cannotfindpages' , 'lesson' );
}
}
$manager = lesson_page_type_manager :: get ( $lesson );
$class = 'lesson_page_type_' . $manager -> get_page_type_idstring ( $page -> qtype );
if ( ! class_exists ( $class )) {
$class = 'lesson_page' ;
}
return new $class ( $page , $lesson );
}
/**
* Deletes a lesson_page from the database as well as any associated records .
* @ final
* @ return bool
*/
final public function delete () {
global $DB ;
// first delete all the associated records...
$DB -> delete_records ( " lesson_attempts " , array ( " pageid " => $this -> properties -> id ));
// ...now delete the answers...
$DB -> delete_records ( " lesson_answers " , array ( " pageid " => $this -> properties -> id ));
// ..and the page itself
$DB -> delete_records ( " lesson_pages " , array ( " id " => $this -> properties -> id ));
// repair the hole in the linkage
if ( ! $this -> properties -> prevpageid && ! $this -> properties -> nextpageid ) {
//This is the only page, no repair needed
} elseif ( ! $this -> properties -> prevpageid ) {
// this is the first page...
$page = $this -> lesson -> load_page ( $this -> properties -> nextpageid );
$page -> move ( null , 0 );
} elseif ( ! $this -> properties -> nextpageid ) {
// this is the last page...
$page = $this -> lesson -> load_page ( $this -> properties -> prevpageid );
$page -> move ( 0 );
} else {
// page is in the middle...
$prevpage = $this -> lesson -> load_page ( $this -> properties -> prevpageid );
$nextpage = $this -> lesson -> load_page ( $this -> properties -> nextpageid );
$prevpage -> move ( $nextpage -> id );
$nextpage -> move ( null , $prevpage -> id );
}
return true ;
}
/**
* Moves a page by updating its nextpageid and prevpageid values within
* the database
*
* @ final
* @ param int $nextpageid
* @ param int $prevpageid
*/
final public function move ( $nextpageid = null , $prevpageid = null ) {
global $DB ;
if ( $nextpageid === null ) {
$nextpageid = $this -> properties -> nextpageid ;
}
if ( $prevpageid === null ) {
$prevpageid = $this -> properties -> prevpageid ;
}
$obj = new stdClass ;
$obj -> id = $this -> properties -> id ;
$obj -> prevpageid = $prevpageid ;
$obj -> nextpageid = $nextpageid ;
$DB -> update_record ( 'lesson_pages' , $obj );
}
/**
* Returns the answers that are associated with this page in the database
*
* @ final
* @ return array
*/
final public function get_answers () {
global $DB ;
if ( $this -> answers === null ) {
$this -> answers = array ();
$answers = $DB -> get_records ( 'lesson_answers' , array ( 'pageid' => $this -> properties -> id , 'lessonid' => $this -> lesson -> id ), 'id' );
if ( ! $answers ) {
2010-12-14 15:06:08 +08:00
// It is possible that a lesson upgraded from Moodle 1.9 still
// contains questions without any answers [MDL-25632].
// debugging(get_string('cannotfindanswer', 'lesson'));
2010-07-24 18:56:56 +00:00
return array ();
}
foreach ( $answers as $answer ) {
$this -> answers [ count ( $this -> answers )] = new lesson_page_answer ( $answer );
}
}
return $this -> answers ;
}
/**
* Returns the lesson this page is associated with
* @ final
* @ return lesson
*/
final protected function get_lesson () {
return $this -> lesson ;
}
/**
* Returns the type of page this is . Not to be confused with page type
* @ final
* @ return int
*/
final protected function get_type () {
return $this -> type ;
}
/**
* Records an attempt at this page
*
* @ final
2010-12-14 16:48:05 +08:00
* @ global moodle_database $DB
2010-07-24 18:56:56 +00:00
* @ param stdClass $context
* @ return stdClass Returns the result of the attempt
*/
final public function record_attempt ( $context ) {
global $DB , $USER , $OUTPUT ;
/**
2010-07-24 19:03:37 +00:00
* This should be overridden by each page type to actually check the response
2010-07-24 18:56:56 +00:00
* against what ever custom criteria they have defined
*/
$result = $this -> check_answer ();
$result -> attemptsremaining = 0 ;
$result -> maxattemptsreached = false ;
if ( $result -> noanswer ) {
$result -> newpageid = $this -> properties -> id ; // display same page again
$result -> feedback = get_string ( 'noanswer' , 'lesson' );
} else {
if ( ! has_capability ( 'mod/lesson:manage' , $context )) {
$nretakes = $DB -> count_records ( " lesson_grades " , array ( " lessonid " => $this -> lesson -> id , " userid " => $USER -> id ));
// record student's attempt
$attempt = new stdClass ;
$attempt -> lessonid = $this -> lesson -> id ;
$attempt -> pageid = $this -> properties -> id ;
$attempt -> userid = $USER -> id ;
$attempt -> answerid = $result -> answerid ;
$attempt -> retry = $nretakes ;
$attempt -> correct = $result -> correctanswer ;
if ( $result -> userresponse !== null ) {
$attempt -> useranswer = $result -> userresponse ;
}
$attempt -> timeseen = time ();
// if allow modattempts, then update the old attempt record, otherwise, insert new answer record
if ( isset ( $USER -> modattempts [ $this -> lesson -> id ])) {
$attempt -> retry = $nretakes - 1 ; // they are going through on review, $nretakes will be too high
}
2011-06-29 18:18:52 +08:00
if ( $this -> lesson -> retake || ( ! $this -> lesson -> retake && $nretakes == 0 )) {
$DB -> insert_record ( " lesson_attempts " , $attempt );
}
2010-07-24 18:56:56 +00:00
// "number of attempts remaining" message if $this->lesson->maxattempts > 1
// displaying of message(s) is at the end of page for more ergonomic display
if ( ! $result -> correctanswer && ( $result -> newpageid == 0 )) {
// wrong answer and student is stuck on this page - check how many attempts
// the student has had at this page/question
2011-06-29 18:18:52 +08:00
$nattempts = $DB -> count_records ( " lesson_attempts " , array ( " pageid " => $this -> properties -> id , " userid " => $USER -> id , " retry " => $attempt -> retry ));
2010-07-24 18:56:56 +00:00
// retreive the number of attempts left counter for displaying at bottom of feedback page
if ( $nattempts >= $this -> lesson -> maxattempts ) {
if ( $this -> lesson -> maxattempts > 1 ) { // don't bother with message if only one attempt
$result -> maxattemptsreached = true ;
}
$result -> newpageid = LESSON_NEXTPAGE ;
} else if ( $this -> lesson -> maxattempts > 1 ) { // don't bother with message if only one attempt
$result -> attemptsremaining = $this -> lesson -> maxattempts - $nattempts ;
}
}
}
// TODO: merge this code with the jump code below. Convert jumpto page into a proper page id
if ( $result -> newpageid == 0 ) {
$result -> newpageid = $this -> properties -> id ;
} elseif ( $result -> newpageid == LESSON_NEXTPAGE ) {
2011-09-26 16:41:49 +08:00
$result -> newpageid = $this -> lesson -> get_next_page ( $this -> properties -> nextpageid );
2010-07-24 18:56:56 +00:00
}
// Determine default feedback if necessary
if ( empty ( $result -> response )) {
if ( ! $this -> lesson -> feedback && ! $result -> noanswer && ! ( $this -> lesson -> review & ! $result -> correctanswer && ! $result -> isessayquestion )) {
// These conditions have been met:
// 1. The lesson manager has not supplied feedback to the student
// 2. Not displaying default feedback
// 3. The user did provide an answer
// 4. We are not reviewing with an incorrect answer (and not reviewing an essay question)
$result -> nodefaultresponse = true ; // This will cause a redirect below
} else if ( $result -> isessayquestion ) {
$result -> response = get_string ( 'defaultessayresponse' , 'lesson' );
} else if ( $result -> correctanswer ) {
$result -> response = get_string ( 'thatsthecorrectanswer' , 'lesson' );
} else {
$result -> response = get_string ( 'thatsthewronganswer' , 'lesson' );
}
}
if ( $result -> response ) {
if ( $this -> lesson -> review && ! $result -> correctanswer && ! $result -> isessayquestion ) {
$nretakes = $DB -> count_records ( " lesson_grades " , array ( " lessonid " => $this -> lesson -> id , " userid " => $USER -> id ));
$qattempts = $DB -> count_records ( " lesson_attempts " , array ( " userid " => $USER -> id , " retry " => $nretakes , " pageid " => $this -> properties -> id ));
if ( $qattempts == 1 ) {
$result -> feedback = $OUTPUT -> box ( get_string ( " firstwrong " , " lesson " ), 'feedback' );
} else {
$result -> feedback = $OUTPUT -> BOX ( get_string ( " secondpluswrong " , " lesson " ), 'feedback' );
}
} else {
$class = 'response' ;
if ( $result -> correctanswer ) {
$class .= ' correct' ; //CSS over-ride this if they exist (!important)
} else if ( ! $result -> isessayquestion ) {
$class .= ' incorrect' ; //CSS over-ride this if they exist (!important)
}
$options = new stdClass ;
$options -> noclean = true ;
$options -> para = true ;
2010-11-05 02:53:47 +00:00
$options -> overflowdiv = true ;
2010-09-15 06:58:39 +00:00
$result -> feedback = $OUTPUT -> box ( format_text ( $this -> get_contents (), $this -> properties -> contentsformat , $options ), 'generalbox boxaligncenter' );
2010-07-24 18:56:56 +00:00
$result -> feedback .= '<div class="correctanswer generalbox"><em>' . get_string ( " youranswer " , " lesson " ) . '</em> : ' . $result -> studentanswer ; // already in clean html
$result -> feedback .= $OUTPUT -> box ( $result -> response , $class ); // already conerted to HTML
2011-03-09 14:57:35 +08:00
$result -> feedback .= '</div>' ;
2010-07-24 18:56:56 +00:00
}
}
}
return $result ;
}
/**
* Returns the string for a jump name
*
* @ final
* @ param int $jumpto Jump code or page ID
* @ return string
**/
final protected function get_jump_name ( $jumpto ) {
global $DB ;
static $jumpnames = array ();
if ( ! array_key_exists ( $jumpto , $jumpnames )) {
2010-07-28 13:45:01 +00:00
if ( $jumpto == LESSON_THISPAGE ) {
2010-07-24 18:56:56 +00:00
$jumptitle = get_string ( 'thispage' , 'lesson' );
} elseif ( $jumpto == LESSON_NEXTPAGE ) {
$jumptitle = get_string ( 'nextpage' , 'lesson' );
} elseif ( $jumpto == LESSON_EOL ) {
$jumptitle = get_string ( 'endoflesson' , 'lesson' );
} elseif ( $jumpto == LESSON_UNSEENBRANCHPAGE ) {
$jumptitle = get_string ( 'unseenpageinbranch' , 'lesson' );
} elseif ( $jumpto == LESSON_PREVIOUSPAGE ) {
$jumptitle = get_string ( 'previouspage' , 'lesson' );
} elseif ( $jumpto == LESSON_RANDOMPAGE ) {
$jumptitle = get_string ( 'randompageinbranch' , 'lesson' );
} elseif ( $jumpto == LESSON_RANDOMBRANCH ) {
$jumptitle = get_string ( 'randombranch' , 'lesson' );
} elseif ( $jumpto == LESSON_CLUSTERJUMP ) {
$jumptitle = get_string ( 'clusterjump' , 'lesson' );
} else {
if ( ! $jumptitle = $DB -> get_field ( 'lesson_pages' , 'title' , array ( 'id' => $jumpto ))) {
$jumptitle = '<strong>' . get_string ( 'notdefined' , 'lesson' ) . '</strong>' ;
}
}
$jumpnames [ $jumpto ] = format_string ( $jumptitle , true );
}
return $jumpnames [ $jumpto ];
}
/**
2010-07-24 19:03:37 +00:00
* Constructor method
2010-07-24 18:56:56 +00:00
* @ param object $properties
* @ param lesson $lesson
*/
public function __construct ( $properties , lesson $lesson ) {
parent :: __construct ( $properties );
$this -> lesson = $lesson ;
}
/**
* Returns the score for the attempt
2010-07-24 19:03:37 +00:00
* This may be overridden by page types that require manual grading
2010-07-24 18:56:56 +00:00
* @ param array $answers
* @ param object $attempt
* @ return int
*/
public function earned_score ( $answers , $attempt ) {
return $answers [ $attempt -> answerid ] -> score ;
}
/**
* This is a callback method that can be override and gets called when ever a page
* is viewed
*
* @ param bool $canmanage True if the user has the manage cap
* @ return mixed
*/
public function callback_on_view ( $canmanage ) {
return true ;
}
/**
* Updates a lesson page and its answers within the database
*
* @ param object $properties
* @ return bool
*/
public function update ( $properties , $context = null , $maxbytes = null ) {
2010-07-25 09:45:16 +00:00
global $DB , $PAGE ;
2010-07-24 18:56:56 +00:00
$answers = $this -> get_answers ();
$properties -> id = $this -> properties -> id ;
$properties -> lessonid = $this -> lesson -> id ;
if ( empty ( $properties -> qoption )) {
$properties -> qoption = '0' ;
}
if ( empty ( $context )) {
$context = $PAGE -> context ;
}
if ( $maxbytes === null ) {
2012-06-08 17:23:47 +07:00
$maxbytes = get_user_max_upload_file_size ( $context );
2010-07-24 18:56:56 +00:00
}
$properties = file_postupdate_standard_editor ( $properties , 'contents' , array ( 'noclean' => true , 'maxfiles' => EDITOR_UNLIMITED_FILES , 'maxbytes' => $maxbytes ), $context , 'mod_lesson' , 'page_contents' , $properties -> id );
$DB -> update_record ( " lesson_pages " , $properties );
for ( $i = 0 ; $i < $this -> lesson -> maxanswers ; $i ++ ) {
if ( ! array_key_exists ( $i , $this -> answers )) {
$this -> answers [ $i ] = new stdClass ;
$this -> answers [ $i ] -> lessonid = $this -> lesson -> id ;
$this -> answers [ $i ] -> pageid = $this -> id ;
$this -> answers [ $i ] -> timecreated = $this -> timecreated ;
}
2010-12-14 16:33:39 +08:00
if ( ! empty ( $properties -> answer_editor [ $i ]) && is_array ( $properties -> answer_editor [ $i ])) {
2010-07-24 18:56:56 +00:00
$this -> answers [ $i ] -> answer = $properties -> answer_editor [ $i ][ 'text' ];
$this -> answers [ $i ] -> answerformat = $properties -> answer_editor [ $i ][ 'format' ];
2010-12-14 16:33:39 +08:00
}
if ( ! empty ( $properties -> response_editor [ $i ]) && is_array ( $properties -> response_editor [ $i ])) {
$this -> answers [ $i ] -> response = $properties -> response_editor [ $i ][ 'text' ];
$this -> answers [ $i ] -> responseformat = $properties -> response_editor [ $i ][ 'format' ];
}
2011-09-23 14:02:46 +08:00
// we don't need to check for isset here because properties called it's own isset method.
if ( $this -> answers [ $i ] -> answer != '' ) {
2010-07-24 18:56:56 +00:00
if ( isset ( $properties -> jumpto [ $i ])) {
$this -> answers [ $i ] -> jumpto = $properties -> jumpto [ $i ];
}
if ( $this -> lesson -> custom && isset ( $properties -> score [ $i ])) {
$this -> answers [ $i ] -> score = $properties -> score [ $i ];
}
if ( ! isset ( $this -> answers [ $i ] -> id )) {
$this -> answers [ $i ] -> id = $DB -> insert_record ( " lesson_answers " , $this -> answers [ $i ]);
} else {
$DB -> update_record ( " lesson_answers " , $this -> answers [ $i ] -> properties ());
}
2010-12-14 16:33:39 +08:00
} else if ( isset ( $this -> answers [ $i ] -> id )) {
$DB -> delete_records ( 'lesson_answers' , array ( 'id' => $this -> answers [ $i ] -> id ));
unset ( $this -> answers [ $i ]);
2010-07-24 18:56:56 +00:00
}
}
return true ;
}
/**
* Can be set to true if the page requires a static link to create a new instance
* instead of simply being included in the dropdown
* @ param int $previd
* @ return bool
*/
public function add_page_link ( $previd ) {
return false ;
}
/**
* Returns true if a page has been viewed before
*
* @ param array | int $param Either an array of pages that have been seen or the
* number of retakes a user has had
* @ return bool
*/
public function is_unseen ( $param ) {
global $USER , $DB ;
if ( is_array ( $param )) {
$seenpages = $param ;
return ( ! array_key_exists ( $this -> properties -> id , $seenpages ));
} else {
$nretakes = $param ;
if ( ! $DB -> count_records ( " lesson_attempts " , array ( " pageid " => $this -> properties -> id , " userid " => $USER -> id , " retry " => $nretakes ))) {
return true ;
}
}
return false ;
}
/**
* Checks to see if a page has been answered previously
* @ param int $nretakes
* @ return bool
*/
public function is_unanswered ( $nretakes ) {
global $DB , $USER ;
if ( ! $DB -> count_records ( " lesson_attempts " , array ( 'pageid' => $this -> properties -> id , 'userid' => $USER -> id , 'correct' => 1 , 'retry' => $nretakes ))) {
return true ;
}
return false ;
}
/**
* Creates answers within the database for this lesson_page . Usually only ever
* called when creating a new page instance
* @ param object $properties
* @ return array
*/
public function create_answers ( $properties ) {
global $DB ;
// now add the answers
$newanswer = new stdClass ;
$newanswer -> lessonid = $this -> lesson -> id ;
$newanswer -> pageid = $this -> properties -> id ;
$newanswer -> timecreated = $this -> properties -> timecreated ;
$answers = array ();
for ( $i = 0 ; $i < $this -> lesson -> maxanswers ; $i ++ ) {
$answer = clone ( $newanswer );
2010-12-14 16:33:39 +08:00
if ( ! empty ( $properties -> answer_editor [ $i ]) && is_array ( $properties -> answer_editor [ $i ])) {
2010-07-24 18:56:56 +00:00
$answer -> answer = $properties -> answer_editor [ $i ][ 'text' ];
$answer -> answerformat = $properties -> answer_editor [ $i ][ 'format' ];
2010-12-14 16:33:39 +08:00
}
if ( ! empty ( $properties -> response_editor [ $i ]) && is_array ( $properties -> response_editor [ $i ])) {
$answer -> response = $properties -> response_editor [ $i ][ 'text' ];
$answer -> responseformat = $properties -> response_editor [ $i ][ 'format' ];
}
2011-09-23 14:02:46 +08:00
if ( isset ( $answer -> answer ) && $answer -> answer != '' ) {
2010-07-24 18:56:56 +00:00
if ( isset ( $properties -> jumpto [ $i ])) {
$answer -> jumpto = $properties -> jumpto [ $i ];
}
if ( $this -> lesson -> custom && isset ( $properties -> score [ $i ])) {
$answer -> score = $properties -> score [ $i ];
}
$answer -> id = $DB -> insert_record ( " lesson_answers " , $answer );
$answers [ $answer -> id ] = new lesson_page_answer ( $answer );
} else {
break ;
}
}
$this -> answers = $answers ;
return $answers ;
}
/**
2010-07-24 19:03:37 +00:00
* This method MUST be overridden by all question page types , or page types that
2010-07-24 18:56:56 +00:00
* wish to score a page .
*
* The structure of result should always be the same so it is a good idea when
* overriding this method on a page type to call
* < code >
* $result = parent :: check_answer ();
* </ code >
2010-07-24 19:03:37 +00:00
* before modifying it as required .
2010-07-24 18:56:56 +00:00
*
* @ return stdClass
*/
public function check_answer () {
$result = new stdClass ;
$result -> answerid = 0 ;
$result -> noanswer = false ;
$result -> correctanswer = false ;
$result -> isessayquestion = false ; // use this to turn off review button on essay questions
$result -> response = '' ;
$result -> newpageid = 0 ; // stay on the page
$result -> studentanswer = '' ; // use this to store student's answer(s) in order to display it on feedback page
$result -> userresponse = null ;
$result -> feedback = '' ;
$result -> nodefaultresponse = false ; // Flag for redirecting when default feedback is turned off
return $result ;
}
/**
* True if the page uses a custom option
*
* Should be override and set to true if the page uses a custom option .
*
* @ return bool
*/
public function has_option () {
return false ;
}
/**
* Returns the maximum number of answers for this page given the maximum number
* of answers permitted by the lesson .
*
* @ param int $default
* @ return int
*/
public function max_answers ( $default ) {
return $default ;
}
/**
* Returns the properties of this lesson page as an object
* @ return stdClass ;
*/
public function properties () {
$properties = clone ( $this -> properties );
if ( $this -> answers === null ) {
$this -> get_answers ();
}
if ( count ( $this -> answers ) > 0 ) {
$count = 0 ;
2013-01-11 11:47:13 +08:00
$qtype = $properties -> qtype ;
2010-07-24 18:56:56 +00:00
foreach ( $this -> answers as $answer ) {
2013-01-15 11:17:55 +08:00
$properties -> { 'answer_editor[' . $count . ']' } = array ( 'text' => $answer -> answer , 'format' => $answer -> answerformat );
2013-01-08 14:39:06 +08:00
if ( $qtype != LESSON_PAGE_MATCHING ) {
2013-01-15 11:17:55 +08:00
$properties -> { 'response_editor[' . $count . ']' } = array ( 'text' => $answer -> response , 'format' => $answer -> responseformat );
2013-01-08 14:39:06 +08:00
} else {
$properties -> { 'response_editor[' . $count . ']' } = $answer -> response ;
}
2010-07-24 18:56:56 +00:00
$properties -> { 'jumpto[' . $count . ']' } = $answer -> jumpto ;
$properties -> { 'score[' . $count . ']' } = $answer -> score ;
$count ++ ;
}
}
return $properties ;
}
/**
2010-07-24 19:03:37 +00:00
* Returns an array of options to display when choosing the jumpto for a page / answer
2010-07-24 18:56:56 +00:00
* @ static
* @ param int $pageid
* @ param lesson $lesson
* @ return array
*/
public static function get_jumptooptions ( $pageid , lesson $lesson ) {
global $DB ;
$jump = array ();
$jump [ 0 ] = get_string ( " thispage " , " lesson " );
$jump [ LESSON_NEXTPAGE ] = get_string ( " nextpage " , " lesson " );
$jump [ LESSON_PREVIOUSPAGE ] = get_string ( " previouspage " , " lesson " );
$jump [ LESSON_EOL ] = get_string ( " endoflesson " , " lesson " );
if ( $pageid == 0 ) {
return $jump ;
}
$pages = $lesson -> load_all_pages ();
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 ))) {
$jump [ LESSON_UNSEENBRANCHPAGE ] = get_string ( " unseenpageinbranch " , " lesson " );
$jump [ LESSON_RANDOMPAGE ] = get_string ( " randompageinbranch " , " lesson " );
}
if ( $pages [ $pageid ] -> qtype == LESSON_PAGE_CLUSTER || $lesson -> is_sub_page_of_type ( $pageid , array ( LESSON_PAGE_CLUSTER ), array ( LESSON_PAGE_ENDOFCLUSTER ))) {
$jump [ LESSON_CLUSTERJUMP ] = get_string ( " clusterjump " , " lesson " );
}
if ( ! optional_param ( 'firstpage' , 0 , PARAM_INT )) {
$apageid = $DB -> get_field ( " lesson_pages " , " id " , array ( " lessonid " => $lesson -> id , " prevpageid " => 0 ));
while ( true ) {
if ( $apageid ) {
$title = $DB -> get_field ( " lesson_pages " , " title " , array ( " id " => $apageid ));
$jump [ $apageid ] = strip_tags ( format_string ( $title , true ));
$apageid = $DB -> get_field ( " lesson_pages " , " nextpageid " , array ( " id " => $apageid ));
} else {
// last page reached
break ;
}
}
}
return $jump ;
}
/**
* Returns the contents field for the page properly formatted and with plugin
* file url ' s converted
* @ return string
*/
public function get_contents () {
global $PAGE ;
if ( ! empty ( $this -> properties -> contents )) {
if ( ! isset ( $this -> properties -> contentsformat )) {
$this -> properties -> contentsformat = FORMAT_HTML ;
}
2012-07-27 13:26:35 +08:00
$context = context_module :: instance ( $PAGE -> cm -> id );
2010-09-04 20:35:30 +00:00
$contents = file_rewrite_pluginfile_urls ( $this -> properties -> contents , 'pluginfile.php' , $context -> id , 'mod_lesson' , 'page_contents' , $this -> properties -> id ); // must do this BEFORE format_text()!!!!!!
return format_text ( $contents , $this -> properties -> contentsformat , array ( 'context' => $context , 'noclean' => true )); // page edit is marked with XSS, we want all content here
2010-07-24 18:56:56 +00:00
} else {
return '' ;
}
}
/**
* Set to true if this page should display in the menu block
* @ return bool
*/
protected function get_displayinmenublock () {
return false ;
}
/**
* Get the string that describes the options of this page type
* @ return string
*/
public function option_description_string () {
return '' ;
}
/**
* Updates a table with the answers for this page
* @ param html_table $table
* @ return html_table
*/
public function display_answers ( html_table $table ) {
$answers = $this -> get_answers ();
$i = 1 ;
foreach ( $answers as $answer ) {
$cells = array ();
$cells [] = " <span class= \" label \" > " . get_string ( " jump " , " lesson " ) . " $i <span>: " ;
$cells [] = $this -> get_jump_name ( $answer -> jumpto );
$table -> data [] = new html_table_row ( $cells );
if ( $i === 1 ){
$table -> data [ count ( $table -> data ) - 1 ] -> cells [ 0 ] -> style = 'width:20%;' ;
}
$i ++ ;
}
return $table ;
}
/**
* Determines if this page should be grayed out on the management / report screens
* @ return int 0 or 1
*/
protected function get_grayout () {
return 0 ;
}
/**
* Adds stats for this page to the & pagestats object . This should be defined
* for all page types that grade
* @ param array $pagestats
* @ param int $tries
* @ return bool
*/
public function stats ( array & $pagestats , $tries ) {
return true ;
}
/**
* Formats the answers of this page for a report
*
* @ param object $answerpage
* @ param object $answerdata
* @ param object $useranswer
* @ param array $pagestats
* @ param int $i Count of first level answers
* @ param int $n Count of second level answers
* @ return object The answer page for this
*/
public function report_answers ( $answerpage , $answerdata , $useranswer , $pagestats , & $i , & $n ) {
$answers = $this -> get_answers ();
$formattextdefoptions = new stdClass ;
$formattextdefoptions -> para = false ; //I'll use it widely in this page
foreach ( $answers as $answer ) {
$data = get_string ( 'jumpsto' , 'lesson' , $this -> get_jump_name ( $answer -> jumpto ));
$answerdata -> answers [] = array ( $data , " " );
$answerpage -> answerdata = $answerdata ;
}
return $answerpage ;
}
/**
* Gets an array of the jumps used by the answers of this page
*
* @ return array
*/
public function get_jumps () {
global $DB ;
$jumps = array ();
$params = array ( " lessonid " => $this -> lesson -> id , " pageid " => $this -> properties -> id );
if ( $answers = $this -> get_answers ()) {
foreach ( $answers as $answer ) {
$jumps [] = $this -> get_jump_name ( $answer -> jumpto );
}
2010-12-14 13:31:27 +08:00
} else {
$jumps [] = $this -> get_jump_name ( $this -> properties -> nextpageid );
2010-07-24 18:56:56 +00:00
}
return $jumps ;
}
/**
* Informs whether this page type require manual grading or not
* @ return bool
*/
public function requires_manual_grading () {
return false ;
}
/**
* A callback method that allows a page to override the next page a user will
* see during when this page is being completed .
* @ return false | int
*/
public function override_next_page () {
return false ;
}
/**
* This method is used to determine if this page is a valid page
*
* @ param array $validpages
* @ param array $pageviews
* @ return int The next page id to check
*/
public function valid_page_and_view ( & $validpages , & $pageviews ) {
$validpages [ $this -> properties -> id ] = 1 ;
return $this -> properties -> nextpageid ;
}
}
/**
* Class used to represent an answer to a page
*
* @ property int $id The ID of this answer in the database
* @ property int $lessonid The ID of the lesson this answer belongs to
* @ property int $pageid The ID of the page this answer belongs to
* @ property int $jumpto Identifies where the user goes upon completing a page with this answer
* @ property int $grade The grade this answer is worth
* @ property int $score The score this answer will give
* @ property int $flags Used to store options for the answer
* @ property int $timecreated A timestamp of when the answer was created
* @ property int $timemodified A timestamp of when the answer was modified
* @ property string $answer The answer itself
* @ property string $response The response the user sees if selecting this answer
*
2010-07-25 10:54:39 +00:00
* @ copyright 2009 Sam Hemelryk
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
2010-07-24 18:56:56 +00:00
*/
class lesson_page_answer extends lesson_base {
/**
* Loads an page answer from the DB
*
* @ param int $id
* @ return lesson_page_answer
*/
public static function load ( $id ) {
global $DB ;
$answer = $DB -> get_record ( " lesson_answers " , array ( " id " => $id ));
return new lesson_page_answer ( $answer );
}
/**
* Given an object of properties and a page created answer ( s ) and saves them
* in the database .
*
* @ param stdClass $properties
* @ param lesson_page $page
* @ return array
*/
public static function create ( $properties , lesson_page $page ) {
return $page -> create_answers ( $properties );
}
}
/**
* A management class for page types
*
* This class is responsible for managing the different pages . A manager object can
* be retrieved by calling the following line of code :
* < code >
* $manager = lesson_page_type_manager :: get ( $lesson );
* </ code >
* The first time the page type manager is retrieved the it includes all of the
* different page types located in mod / lesson / pagetypes .
*
2010-07-25 10:54:39 +00:00
* @ copyright 2009 Sam Hemelryk
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
2010-07-24 18:56:56 +00:00
*/
class lesson_page_type_manager {
/**
* An array of different page type classes
* @ var array
*/
protected $types = array ();
/**
* Retrieves the lesson page type manager object
*
* If the object hasn ' t yet been created it is created here .
*
* @ staticvar lesson_page_type_manager $pagetypemanager
* @ param lesson $lesson
* @ return lesson_page_type_manager
*/
public static function get ( lesson $lesson ) {
static $pagetypemanager ;
if ( ! ( $pagetypemanager instanceof lesson_page_type_manager )) {
$pagetypemanager = new lesson_page_type_manager ();
$pagetypemanager -> load_lesson_types ( $lesson );
}
return $pagetypemanager ;
}
/**
* Finds and loads all lesson page types in mod / lesson / pagetypes
*
* @ param lesson $lesson
*/
public function load_lesson_types ( lesson $lesson ) {
global $CFG ;
$basedir = $CFG -> dirroot . '/mod/lesson/pagetypes/' ;
$dir = dir ( $basedir );
while ( false !== ( $entry = $dir -> read ())) {
if ( strpos ( $entry , '.' ) === 0 || ! preg_match ( '#^[a-zA-Z]+\.php#i' , $entry )) {
continue ;
}
require_once ( $basedir . $entry );
$class = 'lesson_page_type_' . strtok ( $entry , '.' );
if ( class_exists ( $class )) {
$pagetype = new $class ( new stdClass , $lesson );
$this -> types [ $pagetype -> typeid ] = $pagetype ;
}
}
}
/**
* Returns an array of strings to describe the loaded page types
*
* @ param int $type Can be used to return JUST the string for the requested type
* @ return array
*/
public function get_page_type_strings ( $type = null , $special = true ) {
$types = array ();
foreach ( $this -> types as $pagetype ) {
if (( $type === null || $pagetype -> type === $type ) && ( $special === true || $pagetype -> is_standard ())) {
$types [ $pagetype -> typeid ] = $pagetype -> typestring ;
}
}
return $types ;
}
/**
* Returns the basic string used to identify a page type provided with an id
*
* This string can be used to instantiate or identify the page type class .
* If the page type id is unknown then 'unknown' is returned
*
* @ param int $id
* @ return string
*/
public function get_page_type_idstring ( $id ) {
foreach ( $this -> types as $pagetype ) {
if (( int ) $pagetype -> typeid === ( int ) $id ) {
return $pagetype -> idstring ;
}
}
return 'unknown' ;
}
/**
* Loads a page for the provided lesson given it ' s id
*
* This function loads a page from the lesson when given both the lesson it belongs
* to as well as the page ' s id .
* If the page doesn ' t exist an error is thrown
*
* @ param int $pageid The id of the page to load
* @ param lesson $lesson The lesson the page belongs to
* @ return lesson_page A class that extends lesson_page
*/
public function load_page ( $pageid , lesson $lesson ) {
global $DB ;
if ( ! ( $page = $DB -> get_record ( 'lesson_pages' , array ( 'id' => $pageid , 'lessonid' => $lesson -> id )))) {
print_error ( 'cannotfindpages' , 'lesson' );
}
$pagetype = get_class ( $this -> types [ $page -> qtype ]);
$page = new $pagetype ( $page , $lesson );
return $page ;
}
/**
* This function loads ALL pages that belong to the lesson .
*
* @ param lesson $lesson
* @ return array An array of lesson_page_type_ *
*/
public function load_all_pages ( lesson $lesson ) {
global $DB ;
if ( ! ( $pages = $DB -> get_records ( 'lesson_pages' , array ( 'lessonid' => $lesson -> id )))) {
print_error ( 'cannotfindpages' , 'lesson' );
}
foreach ( $pages as $key => $page ) {
$pagetype = get_class ( $this -> types [ $page -> qtype ]);
$pages [ $key ] = new $pagetype ( $page , $lesson );
}
$orderedpages = array ();
$lastpageid = 0 ;
while ( true ) {
foreach ( $pages as $page ) {
if (( int ) $page -> prevpageid === ( int ) $lastpageid ) {
$orderedpages [ $page -> id ] = $page ;
unset ( $pages [ $page -> id ]);
$lastpageid = $page -> id ;
if (( int ) $page -> nextpageid === 0 ) {
break 2 ;
} else {
break 1 ;
}
}
}
}
return $orderedpages ;
}
/**
2010-07-24 19:03:37 +00:00
* Fetches an mform that can be used to create / edit an page
2010-07-24 18:56:56 +00:00
*
* @ param int $type The id for the page type
* @ param array $arguments Any arguments to pass to the mform
* @ return lesson_add_page_form_base
*/
public function get_page_form ( $type , $arguments ) {
$class = 'lesson_add_page_form_' . $this -> get_page_type_idstring ( $type );
if ( ! class_exists ( $class ) || get_parent_class ( $class ) !== 'lesson_add_page_form_base' ) {
debugging ( 'Lesson page type unknown class requested ' . $class , DEBUG_DEVELOPER );
$class = 'lesson_add_page_form_selection' ;
} else if ( $class === 'lesson_add_page_form_unknown' ) {
$class = 'lesson_add_page_form_selection' ;
}
return new $class ( null , $arguments );
}
/**
* Returns an array of links to use as add page links
* @ param int $previd The id of the previous page
* @ return array
*/
public function get_add_page_type_links ( $previd ) {
global $OUTPUT ;
$links = array ();
foreach ( $this -> types as $key => $type ) {
if ( $link = $type -> add_page_link ( $previd )) {
$links [ $key ] = $link ;
}
}
return $links ;
}
}