first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+303
View File
@@ -0,0 +1,303 @@
<?php
// 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/>.
/**
* Provides support for the conversion of moodle1 backup to the moodle2 format
*
* @package mod_lesson
* @copyright 2011 Rossiani Wijaya <rwijaya@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Lesson conversion handler
*/
class moodle1_mod_lesson_handler extends moodle1_mod_handler {
// @var array of answers, when there are more that 4 answers, we need to fix <jumpto>.
protected $answers;
// @var stdClass a page object of the current page
protected $page;
// @var array of page objects to store entire pages, to help generate nextpageid and prevpageid in data
protected $pages;
// @var int a page id (previous)
protected $prevpageid = 0;
/** @var moodle1_file_manager */
protected $fileman = null;
/** @var int cmid */
protected $moduleid = null;
/**
* Declare the paths in moodle.xml we are able to convert
*
* The method returns list of {@link convert_path} instances.
* For each path returned, the corresponding conversion method must be
* defined.
*
* Note that the path /MOODLE_BACKUP/COURSE/MODULES/MOD/LESSON does not
* actually exist in the file. The last element with the module name was
* appended by the moodle1_converter class.
*
* @return array of {@link convert_path} instances
*/
public function get_paths() {
return array(
new convert_path(
'lesson', '/MOODLE_BACKUP/COURSE/MODULES/MOD/LESSON',
array(
'renamefields' => array(
'usegrademax' => 'usemaxgrade',
),
)
),
new convert_path(
'lesson_page', '/MOODLE_BACKUP/COURSE/MODULES/MOD/LESSON/PAGES/PAGE',
array(
'newfields' => array(
'contentsformat' => FORMAT_MOODLE,
'nextpageid' => 0, //set to default to the next sequencial page in process_lesson_page()
'prevpageid' => 0
),
)
),
new convert_path(
'lesson_pages', '/MOODLE_BACKUP/COURSE/MODULES/MOD/LESSON/PAGES'
),
new convert_path(
'lesson_answer', '/MOODLE_BACKUP/COURSE/MODULES/MOD/LESSON/PAGES/PAGE/ANSWERS/ANSWER',
array(
'newfields' => array(
'answerformat' => 0,
'responseformat' => 0,
),
'renamefields' => array(
'answertext' => 'answer_text',
),
)
)
);
}
/**
* This is executed every time we have one /MOODLE_BACKUP/COURSE/MODULES/MOD/LESSON
* data available
*/
public function process_lesson($data) {
// get the course module id and context id
$instanceid = $data['id'];
$cminfo = $this->get_cminfo($instanceid);
$this->moduleid = $cminfo['id'];
$contextid = $this->converter->get_contextid(CONTEXT_MODULE, $this->moduleid);
// get a fresh new file manager for this instance
$this->fileman = $this->converter->get_file_manager($contextid, 'mod_lesson');
// migrate referenced local media files
if (!empty($data['mediafile']) and strpos($data['mediafile'], '://') === false) {
$this->fileman->filearea = 'mediafile';
$this->fileman->itemid = 0;
try {
$this->fileman->migrate_file('course_files/'.$data['mediafile']);
} catch (moodle1_convert_exception $e) {
// the file probably does not exist
$this->log('error migrating lesson mediafile', backup::LOG_WARNING, 'course_files/'.$data['mediafile']);
}
}
// start writing lesson.xml
$this->open_xml_writer("activities/lesson_{$this->moduleid}/lesson.xml");
$this->xmlwriter->begin_tag('activity', array('id' => $instanceid, 'moduleid' => $this->moduleid,
'modulename' => 'lesson', 'contextid' => $contextid));
$this->xmlwriter->begin_tag('lesson', array('id' => $instanceid));
foreach ($data as $field => $value) {
if ($field <> 'id') {
$this->xmlwriter->full_tag($field, $value);
}
}
return $data;
}
public function on_lesson_pages_start() {
$this->xmlwriter->begin_tag('pages');
}
/**
* This is executed every time we have one /MOODLE_BACKUP/COURSE/MODULES/MOD/LESSON/PAGES/PAGE
* data available
*/
public function process_lesson_page($data) {
global $CFG;
// replay the upgrade step 2009120801
if ($CFG->texteditors !== 'textarea') {
$data['contents'] = text_to_html($data['contents'], false, false, true);
$data['contentsformat'] = FORMAT_HTML;
}
// store page in pages
$this->page = new stdClass();
$this->page->id = $data['pageid'];
unset($data['pageid']);
$this->page->data = $data;
}
/**
* This is executed every time we have one /MOODLE_BACKUP/COURSE/MODULES/MOD/LESSON/PAGES/PAGE/ANSWERS/ANSWER
* data available
*/
public function process_lesson_answer($data) {
// replay the upgrade step 2010072003
$flags = intval($data['flags']);
if ($flags & 1) {
$data['answer_text'] = text_to_html($data['answer_text'], false, false, true);
$data['answerformat'] = FORMAT_HTML;
}
if ($flags & 2) {
$data['response'] = text_to_html($data['response'], false, false, true);
$data['responseformat'] = FORMAT_HTML;
}
// buffer for conversion of <jumpto> in line with
// upgrade step 2010121400 from mod/lesson/db/upgrade.php
$this->answers[] = $data;
}
public function on_lesson_page_end() {
$this->page->answers = $this->answers;
$this->pages[] = $this->page;
$firstbatch = count($this->pages) > 2;
$nextbatch = count($this->pages) > 1 && $this->prevpageid != 0;
if ( $firstbatch || $nextbatch ) { //we can write out 1 page atleast
if ($this->prevpageid == 0) {
// start writing with n-2 page (relative to this on_lesson_page_end() call)
$pg1 = $this->pages[1];
$pg0 = $this->pages[0];
$this->write_single_page_xml($pg0, 0, $pg1->id);
$this->prevpageid = $pg0->id;
array_shift($this->pages); //bye bye page0
}
$pg1 = $this->pages[0];
// write pg1 referencing prevpageid and pg2
$pg2 = $this->pages[1];
$this->write_single_page_xml($pg1, $this->prevpageid, $pg2->id);
$this->prevpageid = $pg1->id;
array_shift($this->pages); //throw written n-1th page
}
$this->answers = array(); //clear answers for the page ending. do not unset, object property will be missing.
$this->page = null;
}
public function on_lesson_pages_end() {
if ($this->pages) {
if (isset($this->pages[1])) { // write the case of only 2 pages.
$this->write_single_page_xml($this->pages[0], $this->prevpageid, $this->pages[1]->id);
$this->prevpageid = $this->pages[0]->id;
array_shift($this->pages);
}
//write the remaining (first/last) single page
$this->write_single_page_xml($this->pages[0], $this->prevpageid, 0);
}
$this->xmlwriter->end_tag('pages');
//reset
unset($this->pages);
$this->prevpageid = 0;
}
/**
* This is executed when we reach the closing </MOD> tag of our 'lesson' path
*/
public function on_lesson_end() {
// Append empty <overrides> subpath element.
$this->write_xml('overrides', array());
// finish writing lesson.xml
$this->xmlwriter->end_tag('lesson');
$this->xmlwriter->end_tag('activity');
$this->close_xml_writer();
// write inforef.xml
$this->open_xml_writer("activities/lesson_{$this->moduleid}/inforef.xml");
$this->xmlwriter->begin_tag('inforef');
$this->xmlwriter->begin_tag('fileref');
foreach ($this->fileman->get_fileids() as $fileid) {
$this->write_xml('file', array('id' => $fileid));
}
$this->xmlwriter->end_tag('fileref');
$this->xmlwriter->end_tag('inforef');
$this->close_xml_writer();
}
/**
* writes out the given page into the open xml handle
* @param type $page
* @param type $prevpageid
* @param type $nextpageid
*/
protected function write_single_page_xml($page, $prevpageid=0, $nextpageid=0) {
//mince nextpageid and prevpageid
$page->data['nextpageid'] = $nextpageid;
$page->data['prevpageid'] = $prevpageid;
// write out each page data
$this->xmlwriter->begin_tag('page', array('id' => $page->id));
foreach ($page->data as $field => $value) {
$this->xmlwriter->full_tag($field, $value);
}
//effectively on_lesson_answers_end(), where we write out answers for current page.
$answers = $page->answers;
$this->xmlwriter->begin_tag('answers');
$numanswers = $answers ? count($answers) : 0;
if ($numanswers) { //if there are any answers (possible there are none!)
if ($numanswers > 3 && $page->data['qtype'] == 5) { //fix only jumpto only for matching question types.
if ($answers[0]['jumpto'] !== '0' || $answers[1]['jumpto'] !== '0') {
if ($answers[2]['jumpto'] !== '0') {
$answers[0]['jumpto'] = $answers[2]['jumpto'];
$answers[2]['jumpto'] = '0';
}
if ($answers[3]['jumpto'] !== '0') {
$answers[1]['jumpto'] = $answers[3]['jumpto'];
$answers[3]['jumpto'] = '0';
}
}
}
foreach ($answers as $data) {
$this->write_xml('answer', $data, array('/answer/id'));
}
}
$this->xmlwriter->end_tag('answers');
// answers is now closed for current page. Ending the page.
$this->xmlwriter->end_tag('page');
}
}
@@ -0,0 +1,106 @@
<?php
// 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/>.
/**
* This file contains the backup task for the lesson module
*
* @package mod_lesson
* @category backup
* @copyright 2010 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/lesson/backup/moodle2/backup_lesson_stepslib.php');
/**
* Provides the steps to perform one complete backup of the Lesson instance
*
* @copyright 2010 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class backup_lesson_activity_task extends backup_activity_task {
/**
* No specific settings for this activity
*/
protected function define_my_settings() {
}
/**
* Defines a backup step to store the instance data in the lesson.xml file
*/
protected function define_my_steps() {
$this->add_step(new backup_lesson_activity_structure_step('lesson structure', 'lesson.xml'));
}
/**
* Encodes URLs to various Lesson scripts
*
* @param string $content some HTML text that eventually contains URLs to the activity instance scripts
* @return string the content with the URLs encoded
*/
public static function encode_content_links($content) {
global $CFG;
$base = preg_quote($CFG->wwwroot.'/mod/lesson','#');
// Provides the interface for overall authoring of lessons
$pattern = '#'.$base.'/edit\.php\?id=([0-9]+)#';
$replacement = '$@LESSONEDIT*$1@$';
$content = preg_replace($pattern, $replacement, $content);
// Action for adding a question page. Prints an HTML form.
$pattern = '#'.$base.'/editpage\.php\?id=([0-9]+)&(amp;)?pageid=([0-9]+)#';
$replacement = '$@LESSONEDITPAGE*$1*$3@$';
$content = preg_replace($pattern, $replacement, $content);
// Provides the interface for grading essay questions
$pattern = '#'.$base.'/essay\.php\?id=([0-9]+)#';
$replacement = '$@LESSONESSAY*$1@$';
$content = preg_replace($pattern, $replacement, $content);
// Provides the interface for viewing the report
$pattern = '#'.$base.'/report\.php\?id=([0-9]+)#';
$replacement = '$@LESSONREPORT*$1@$';
$content = preg_replace($pattern, $replacement, $content);
// This file plays the mediafile set in lesson settings.
$pattern = '#'.$base.'/mediafile\.php\?id=([0-9]+)#';
$replacement = '$@LESSONMEDIAFILE*$1@$';
$content = preg_replace($pattern, $replacement, $content);
// This page lists all the instances of lesson in a particular course
$pattern = '#'.$base.'/index\.php\?id=([0-9]+)#';
$replacement = '$@LESSONINDEX*$1@$';
$content = preg_replace($pattern, $replacement, $content);
// This page prints a particular page of lesson
$pattern = '#'.$base.'/view\.php\?id=([0-9]+)&(amp;)?pageid=([0-9]+)#';
$replacement = '$@LESSONVIEWPAGE*$1*$3@$';
$content = preg_replace($pattern, $replacement, $content);
// Link to one lesson by cmid
$pattern = '#'.$base.'/view\.php\?id=([0-9]+)#';
$replacement = '$@LESSONVIEWBYID*$1@$';
$content = preg_replace($pattern, $replacement, $content);
// Return the now encoded content
return $content;
}
}
@@ -0,0 +1,211 @@
<?php
// 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/>.
/**
* This file contains the backup structure for the lesson module
*
* This is the "graphical" structure of the lesson module:
*
* lesson ---------->-------------|------------>---------|----------->----------|
* (CL,pk->id) | | |
* | | | |
* | lesson_grades lesson_timer lesson_overrides
* | (UL, pk->id,fk->lessonid) (UL, pk->id,fk->lessonid) (UL, pk->id,fk->lessonid)
* | |
* | |
* | |
* | |
* lesson_pages-------->-------lesson_branch
* (CL,pk->id,fk->lessonid) (UL, pk->id,fk->pageid)
* |
* |
* |
* lesson_answers
* (CL,pk->id,fk->pageid)
* |
* |
* |
* lesson_attempts
* (UL,pk->id,fk->answerid)
*
* Meaning: pk->primary key field of the table
* fk->foreign key to link with parent
* nt->nested field (recursive data)
* CL->course level info
* UL->user level info
* files->table may have files)
*
* @package mod_lesson
* @copyright 2010 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Structure step class that informs a backup task how to backup the lesson module.
*
* @copyright 2010 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class backup_lesson_activity_structure_step extends backup_activity_structure_step {
protected function define_structure() {
// The lesson table
// This table contains all of the goodness for the lesson module, quite
// alot goes into it but nothing relational other than course when will
// need to be corrected upon restore.
$lesson = new backup_nested_element('lesson', array('id'), array(
'course', 'name', 'intro', 'introformat', 'practice', 'modattempts',
'usepassword', 'password',
'dependency', 'conditions', 'grade', 'custom', 'ongoing', 'usemaxgrade',
'maxanswers', 'maxattempts', 'review', 'nextpagedefault', 'feedback',
'minquestions', 'maxpages', 'timelimit', 'retake', 'activitylink',
'mediafile', 'mediaheight', 'mediawidth', 'mediaclose', 'slideshow',
'width', 'height', 'bgcolor', 'displayleft', 'displayleftif', 'progressbar',
'available', 'deadline', 'timemodified',
'completionendreached', 'completiontimespent', 'allowofflineattempts'
));
// The lesson_pages table
// Grouped within a `pages` element, important to note that page is relational
// to the lesson, and also to the previous/next page in the series.
// Upon restore prevpageid and nextpageid will need to be corrected.
$pages = new backup_nested_element('pages');
$page = new backup_nested_element('page', array('id'), array(
'prevpageid','nextpageid','qtype','qoption','layout',
'display','timecreated','timemodified','title','contents',
'contentsformat'
));
// The lesson_answers table
// Grouped within an answers `element`, the lesson_answers table relates
// to the page and lesson with `pageid` and `lessonid` that will both need
// to be corrected during restore.
$answers = new backup_nested_element('answers');
$answer = new backup_nested_element('answer', array('id'), array(
'jumpto','grade','score','flags','timecreated','timemodified','answer_text',
'response', 'answerformat', 'responseformat'
));
// Tell the answer element about the answer_text elements mapping to the answer
// database field.
$answer->set_source_alias('answer', 'answer_text');
// The lesson_attempts table
// Grouped by an `attempts` element this is relational to the page, lesson,
// and user.
$attempts = new backup_nested_element('attempts');
$attempt = new backup_nested_element('attempt', array('id'), array(
'userid','retry','correct','useranswer','timeseen'
));
// The lesson_branch table
// Grouped by a `branch` element this is relational to the page, lesson,
// and user.
$branches = new backup_nested_element('branches');
$branch = new backup_nested_element('branch', array('id'), array(
'userid', 'retry', 'flag', 'timeseen', 'nextpageid'
));
// The lesson_grades table
// Grouped by a grades element this is relational to the lesson and user.
$grades = new backup_nested_element('grades');
$grade = new backup_nested_element('grade', array('id'), array(
'userid','grade','late','completed'
));
// The lesson_timer table
// Grouped by a `timers` element this is relational to the lesson and user.
$timers = new backup_nested_element('timers');
$timer = new backup_nested_element('timer', array('id'), array(
'userid', 'starttime', 'lessontime', 'completed', 'timemodifiedoffline'
));
$overrides = new backup_nested_element('overrides');
$override = new backup_nested_element('override', array('id'), array(
'groupid', 'userid', 'available', 'deadline', 'timelimit',
'review', 'maxattempts', 'retake', 'password'));
// Now that we have all of the elements created we've got to put them
// together correctly.
$lesson->add_child($pages);
$pages->add_child($page);
$page->add_child($answers);
$answers->add_child($answer);
$answer->add_child($attempts);
$attempts->add_child($attempt);
$page->add_child($branches);
$branches->add_child($branch);
$lesson->add_child($grades);
$grades->add_child($grade);
$lesson->add_child($timers);
$timers->add_child($timer);
$lesson->add_child($overrides);
$overrides->add_child($override);
// Set the source table for the elements that aren't reliant on the user
// at this point (lesson, lesson_pages, lesson_answers)
$lesson->set_source_table('lesson', array('id' => backup::VAR_ACTIVITYID));
//we use SQL here as it must be ordered by prevpageid so that restore gets the pages in the right order.
$page->set_source_table('lesson_pages', array('lessonid' => backup::VAR_PARENTID), 'prevpageid ASC');
// We use SQL here as answers must be ordered by id so that the restore gets them in the right order
$answer->set_source_table('lesson_answers', array('pageid' => backup::VAR_PARENTID), 'id ASC');
// Lesson overrides to backup are different depending of user info.
$overrideparams = array('lessonid' => backup::VAR_PARENTID);
// Check if we are also backing up user information
if ($this->get_setting_value('userinfo')) {
// Set the source table for elements that are reliant on the user
// lesson_attempts, lesson_branch, lesson_grades, lesson_timer.
$attempt->set_source_table('lesson_attempts', array('answerid' => backup::VAR_PARENTID));
$branch->set_source_table('lesson_branch', array('pageid' => backup::VAR_PARENTID));
$grade->set_source_table('lesson_grades', array('lessonid'=>backup::VAR_PARENTID));
$timer->set_source_table('lesson_timer', array('lessonid' => backup::VAR_PARENTID));
} else {
$overrideparams['userid'] = backup_helper::is_sqlparam(null); // Without userinfo, skip user overrides.
}
// Skip group overrides if not including groups.
$groupinfo = $this->get_setting_value('groups');
if (!$groupinfo) {
$overrideparams['groupid'] = backup_helper::is_sqlparam(null);
}
$override->set_source_table('lesson_overrides', $overrideparams);
// Annotate the user id's where required.
$attempt->annotate_ids('user', 'userid');
$branch->annotate_ids('user', 'userid');
$grade->annotate_ids('user', 'userid');
$timer->annotate_ids('user', 'userid');
$override->annotate_ids('user', 'userid');
$override->annotate_ids('group', 'groupid');
// Annotate the file areas in user by the lesson module.
$lesson->annotate_files('mod_lesson', 'intro', null);
$lesson->annotate_files('mod_lesson', 'mediafile', null);
$page->annotate_files('mod_lesson', 'page_contents', 'id');
$answer->annotate_files('mod_lesson', 'page_answers', 'id');
$answer->annotate_files('mod_lesson', 'page_responses', 'id');
$attempt->annotate_files('mod_lesson', 'essay_responses', 'id');
$attempt->annotate_files('mod_lesson', 'essay_answers', 'id');
// Prepare and return the structure we have just created for the lesson module.
return $this->prepare_activity_structure($lesson);
}
}
@@ -0,0 +1,161 @@
<?php
// 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/>.
/**
* @package mod_lesson
* @subpackage backup-moodle2
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/lesson/backup/moodle2/restore_lesson_stepslib.php'); // Because it exists (must)
/**
* lesson restore task that provides all the settings and steps to perform one
* complete restore of the activity
*/
class restore_lesson_activity_task extends restore_activity_task {
/**
* Define (add) particular settings this activity can have
*/
protected function define_my_settings() {
// No particular settings for this activity
}
/**
* Define (add) particular steps this activity can have
*/
protected function define_my_steps() {
// lesson only has one structure step
$this->add_step(new restore_lesson_activity_structure_step('lesson_structure', 'lesson.xml'));
}
/**
* Define the contents in the activity that must be
* processed by the link decoder
*/
public static function define_decode_contents() {
$contents = array();
$contents[] = new restore_decode_content('lesson', array('intro'), 'lesson');
$contents[] = new restore_decode_content('lesson_pages', array('contents'), 'lesson_page');
$contents[] = new restore_decode_content('lesson_answers', array('answer', 'response'), 'lesson_answer');
return $contents;
}
/**
* Define the decoding rules for links belonging
* to the activity to be executed by the link decoder
*/
public static function define_decode_rules() {
$rules = array();
$rules[] = new restore_decode_rule('LESSONEDIT', '/mod/lesson/edit.php?id=$1', 'course_module');
$rules[] = new restore_decode_rule('LESSONESAY', '/mod/lesson/essay.php?id=$1', 'course_module');
$rules[] = new restore_decode_rule('LESSONREPORT', '/mod/lesson/report.php?id=$1', 'course_module');
$rules[] = new restore_decode_rule('LESSONMEDIAFILE', '/mod/lesson/mediafile.php?id=$1', 'course_module');
$rules[] = new restore_decode_rule('LESSONVIEWBYID', '/mod/lesson/view.php?id=$1', 'course_module');
$rules[] = new restore_decode_rule('LESSONINDEX', '/mod/lesson/index.php?id=$1', 'course');
$rules[] = new restore_decode_rule('LESSONVIEWPAGE', '/mod/lesson/view.php?id=$1&pageid=$2', array('course_module', 'lesson_page'));
$rules[] = new restore_decode_rule('LESSONEDITPAGE', '/mod/lesson/edit.php?id=$1&pageid=$2', array('course_module', 'lesson_page'));
return $rules;
}
/**
* Define the restore log rules that will be applied
* by the {@link restore_logs_processor} when restoring
* lesson logs. It must return one array
* of {@link restore_log_rule} objects
*/
public static function define_restore_log_rules() {
$rules = array();
$rules[] = new restore_log_rule('lesson', 'add', 'view.php?id={course_module}', '{lesson}');
$rules[] = new restore_log_rule('lesson', 'update', 'view.php?id={course_module}', '{lesson}');
$rules[] = new restore_log_rule('lesson', 'view', 'view.php?id={course_module}', '{lesson}');
$rules[] = new restore_log_rule('lesson', 'start', 'view.php?id={course_module}', '{lesson}');
$rules[] = new restore_log_rule('lesson', 'end', 'view.php?id={course_module}', '{lesson}');
$rules[] = new restore_log_rule('lesson', 'view grade', 'essay.php?id={course_module}', '[name]');
$rules[] = new restore_log_rule('lesson', 'update grade', 'essay.php?id={course_module}', '[name]');
$rules[] = new restore_log_rule('lesson', 'update email essay grade', 'essay.php?id={course_module}', '[name]');
return $rules;
}
/**
* Define the restore log rules that will be applied
* by the {@link restore_logs_processor} when restoring
* course logs. It must return one array
* of {@link restore_log_rule} objects
*
* Note this rules are applied when restoring course logs
* by the restore final task, but are defined here at
* activity level. All them are rules not linked to any module instance (cmid = 0)
*/
public static function define_restore_log_rules_for_course() {
$rules = array();
$rules[] = new restore_log_rule('lesson', 'view all', 'index.php?id={course}', null);
return $rules;
}
/**
* Re-map the dependency and activitylink information
* If a depency or activitylink has no mapping in the backup data then it could either be a duplication of a
* lesson, or a backup/restore of a single lesson. We have no way to determine which and whether this is the
* same site and/or course. Therefore we try and retrieve a mapping, but fallback to the original value if one
* was not found. We then test to see whether the value found is valid for the course being restored into.
*/
public function after_restore() {
global $DB;
$lesson = $DB->get_record('lesson', array('id' => $this->get_activityid()), 'id, course, dependency, activitylink');
$updaterequired = false;
if (!empty($lesson->dependency)) {
$updaterequired = true;
if ($newitem = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'lesson', $lesson->dependency)) {
$lesson->dependency = $newitem->newitemid;
}
if (!$DB->record_exists('lesson', array('id' => $lesson->dependency, 'course' => $lesson->course))) {
$lesson->dependency = 0;
}
}
if (!empty($lesson->activitylink)) {
$updaterequired = true;
if ($newitem = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'course_module', $lesson->activitylink)) {
$lesson->activitylink = $newitem->newitemid;
}
if (!$DB->record_exists('course_modules', array('id' => $lesson->activitylink, 'course' => $lesson->course))) {
$lesson->activitylink = 0;
}
}
if ($updaterequired) {
$DB->update_record('lesson', $lesson);
}
}
}
@@ -0,0 +1,332 @@
<?php
// 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/>.
/**
* @package mod_lesson
* @subpackage backup-moodle2
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Define all the restore steps that will be used by the restore_lesson_activity_task
*/
/**
* Structure step to restore one lesson activity
*/
class restore_lesson_activity_structure_step extends restore_activity_structure_step {
// Store the answers as they're received but only process them at the
// end of the lesson
protected $answers = array();
protected function define_structure() {
$paths = array();
$userinfo = $this->get_setting_value('userinfo');
$paths[] = new restore_path_element('lesson', '/activity/lesson');
$paths[] = new restore_path_element('lesson_page', '/activity/lesson/pages/page');
$paths[] = new restore_path_element('lesson_answer', '/activity/lesson/pages/page/answers/answer');
$paths[] = new restore_path_element('lesson_override', '/activity/lesson/overrides/override');
if ($userinfo) {
$paths[] = new restore_path_element('lesson_attempt', '/activity/lesson/pages/page/answers/answer/attempts/attempt');
$paths[] = new restore_path_element('lesson_grade', '/activity/lesson/grades/grade');
$paths[] = new restore_path_element('lesson_branch', '/activity/lesson/pages/page/branches/branch');
$paths[] = new restore_path_element('lesson_highscore', '/activity/lesson/highscores/highscore');
$paths[] = new restore_path_element('lesson_timer', '/activity/lesson/timers/timer');
}
// Return the paths wrapped into standard activity structure
return $this->prepare_activity_structure($paths);
}
protected function process_lesson($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
$data->course = $this->get_courseid();
// Any changes to the list of dates that needs to be rolled should be same during course restore and course reset.
// See MDL-9367.
$data->available = $this->apply_date_offset($data->available);
$data->deadline = $this->apply_date_offset($data->deadline);
// The lesson->highscore code was removed in MDL-49581.
// Remove it if found in the backup file.
if (isset($data->showhighscores)) {
unset($data->showhighscores);
}
if (isset($data->highscores)) {
unset($data->highscores);
}
// Supply items that maybe missing from previous versions.
if (!isset($data->completionendreached)) {
$data->completionendreached = 0;
}
if (!isset($data->completiontimespent)) {
$data->completiontimespent = 0;
}
if (!isset($data->intro)) {
$data->intro = '';
$data->introformat = FORMAT_HTML;
}
// Compatibility with old backups with maxtime and timed fields.
if (!isset($data->timelimit)) {
if (isset($data->timed) && isset($data->maxtime) && $data->timed) {
$data->timelimit = 60 * $data->maxtime;
} else {
$data->timelimit = 0;
}
}
// insert the lesson record
$newitemid = $DB->insert_record('lesson', $data);
// immediately after inserting "activity" record, call this
$this->apply_activity_instance($newitemid);
}
protected function process_lesson_page($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
$data->lessonid = $this->get_new_parentid('lesson');
// We'll remap all the prevpageid and nextpageid at the end, once all pages have been created
$newitemid = $DB->insert_record('lesson_pages', $data);
$this->set_mapping('lesson_page', $oldid, $newitemid, true); // Has related fileareas
}
protected function process_lesson_answer($data) {
global $DB;
$data = (object)$data;
$data->lessonid = $this->get_new_parentid('lesson');
$data->pageid = $this->get_new_parentid('lesson_page');
$data->answer = $data->answer_text;
// Set a dummy mapping to get the old ID so that it can be used by get_old_parentid when
// processing attempts. It will be corrected in after_execute
$this->set_mapping('lesson_answer', $data->id, 0, true); // Has related fileareas.
// Answers need to be processed in order, so we store them in an
// instance variable and insert them in the after_execute stage
$this->answers[$data->id] = $data;
}
protected function process_lesson_attempt($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
$data->lessonid = $this->get_new_parentid('lesson');
$data->pageid = $this->get_new_parentid('lesson_page');
// We use the old answerid here as the answer isn't created until after_execute
$data->answerid = $this->get_old_parentid('lesson_answer');
$data->userid = $this->get_mappingid('user', $data->userid);
$newitemid = $DB->insert_record('lesson_attempts', $data);
$this->set_mapping('lesson_attempt', $oldid, $newitemid, true); // Has related fileareas.
}
protected function process_lesson_grade($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
$data->lessonid = $this->get_new_parentid('lesson');
$data->userid = $this->get_mappingid('user', $data->userid);
$newitemid = $DB->insert_record('lesson_grades', $data);
$this->set_mapping('lesson_grade', $oldid, $newitemid);
}
protected function process_lesson_branch($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
$data->lessonid = $this->get_new_parentid('lesson');
$data->pageid = $this->get_new_parentid('lesson_page');
$data->userid = $this->get_mappingid('user', $data->userid);
$newitemid = $DB->insert_record('lesson_branch', $data);
}
protected function process_lesson_highscore($data) {
// Do not process any high score data.
// high scores were removed in Moodle 3.0 See MDL-49581.
}
protected function process_lesson_timer($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
$data->lessonid = $this->get_new_parentid('lesson');
$data->userid = $this->get_mappingid('user', $data->userid);
// Supply item that maybe missing from previous versions.
if (!isset($data->completed)) {
$data->completed = 0;
}
$newitemid = $DB->insert_record('lesson_timer', $data);
}
/**
* Process a lesson override restore
* @param object $data The data in object form
* @return void
*/
protected function process_lesson_override($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
// Based on userinfo, we'll restore user overides or no.
$userinfo = $this->get_setting_value('userinfo');
// Skip user overrides if we are not restoring userinfo.
if (!$userinfo && !is_null($data->userid)) {
return;
}
$data->lessonid = $this->get_new_parentid('lesson');
if (!is_null($data->userid)) {
$data->userid = $this->get_mappingid('user', $data->userid);
}
if (!is_null($data->groupid)) {
$data->groupid = $this->get_mappingid('group', $data->groupid);
}
// Skip if there is no user and no group data.
if (empty($data->userid) && empty($data->groupid)) {
return;
}
$data->available = $this->apply_date_offset($data->available);
$data->deadline = $this->apply_date_offset($data->deadline);
$newitemid = $DB->insert_record('lesson_overrides', $data);
// Add mapping, restore of logs needs it.
$this->set_mapping('lesson_override', $oldid, $newitemid);
}
protected function after_execute() {
global $DB;
// Answers must be sorted by id to ensure that they're shown correctly
ksort($this->answers);
foreach ($this->answers as $answer) {
$newitemid = $DB->insert_record('lesson_answers', $answer);
$this->set_mapping('lesson_answer', $answer->id, $newitemid, true);
// Update the lesson attempts to use the newly created answerid
$DB->set_field('lesson_attempts', 'answerid', $newitemid, array(
'lessonid' => $answer->lessonid,
'pageid' => $answer->pageid,
'answerid' => $answer->id));
}
// Add lesson files, no need to match by itemname (just internally handled context).
$this->add_related_files('mod_lesson', 'intro', null);
$this->add_related_files('mod_lesson', 'mediafile', null);
// Add lesson page files, by lesson_page itemname
$this->add_related_files('mod_lesson', 'page_contents', 'lesson_page');
$this->add_related_files('mod_lesson', 'page_answers', 'lesson_answer');
$this->add_related_files('mod_lesson', 'page_responses', 'lesson_answer');
$this->add_related_files('mod_lesson', 'essay_responses', 'lesson_attempt');
$this->add_related_files('mod_lesson', 'essay_answers', 'lesson_attempt');
// Remap all the restored prevpageid and nextpageid now that we have all the pages and their mappings
$rs = $DB->get_recordset('lesson_pages', array('lessonid' => $this->task->get_activityid()),
'', 'id, prevpageid, nextpageid');
foreach ($rs as $page) {
$page->prevpageid = (empty($page->prevpageid)) ? 0 : $this->get_mappingid('lesson_page', $page->prevpageid);
$page->nextpageid = (empty($page->nextpageid)) ? 0 : $this->get_mappingid('lesson_page', $page->nextpageid);
$DB->update_record('lesson_pages', $page);
}
$rs->close();
// Remap all the restored 'jumpto' fields now that we have all the pages and their mappings
$rs = $DB->get_recordset('lesson_answers', array('lessonid' => $this->task->get_activityid()),
'', 'id, jumpto');
foreach ($rs as $answer) {
if ($answer->jumpto > 0) {
$answer->jumpto = $this->get_mappingid('lesson_page', $answer->jumpto);
$DB->update_record('lesson_answers', $answer);
}
}
$rs->close();
// Remap all the restored 'nextpageid' fields now that we have all the pages and their mappings.
$rs = $DB->get_recordset('lesson_branch', array('lessonid' => $this->task->get_activityid()),
'', 'id, nextpageid');
foreach ($rs as $answer) {
if ($answer->nextpageid > 0) {
$answer->nextpageid = $this->get_mappingid('lesson_page', $answer->nextpageid);
$DB->update_record('lesson_branch', $answer);
}
}
$rs->close();
// Replay the upgrade step 2015030301
// to clean lesson answers that should be plain text.
// 1 = LESSON_PAGE_SHORTANSWER, 8 = LESSON_PAGE_NUMERICAL, 20 = LESSON_PAGE_BRANCHTABLE.
$sql = 'SELECT a.*
FROM {lesson_answers} a
JOIN {lesson_pages} p ON p.id = a.pageid
WHERE a.answerformat <> :format
AND a.lessonid = :lessonid
AND p.qtype IN (1, 8, 20)';
$badanswers = $DB->get_recordset_sql($sql, array('lessonid' => $this->task->get_activityid(), 'format' => FORMAT_MOODLE));
foreach ($badanswers as $badanswer) {
// Strip tags from answer text and convert back the format to FORMAT_MOODLE.
$badanswer->answer = strip_tags($badanswer->answer);
$badanswer->answerformat = FORMAT_MOODLE;
$DB->update_record('lesson_answers', $badanswer);
}
$badanswers->close();
// Replay the upgrade step 2015032700.
// Delete any orphaned lesson_branch record.
if ($DB->get_dbfamily() === 'mysql') {
$sql = "DELETE {lesson_branch}
FROM {lesson_branch}
LEFT JOIN {lesson_pages}
ON {lesson_branch}.pageid = {lesson_pages}.id
WHERE {lesson_pages}.id IS NULL";
} else {
$sql = "DELETE FROM {lesson_branch}
WHERE NOT EXISTS (
SELECT 'x' FROM {lesson_pages}
WHERE {lesson_branch}.pageid = {lesson_pages}.id)";
}
$DB->execute($sql);
}
}
@@ -0,0 +1,65 @@
<?php
// 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/>.
/**
* Activity base class.
*
* @package mod_lesson
* @copyright 2017 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\analytics\indicator;
defined('MOODLE_INTERNAL') || die();
/**
* Activity base class.
*
* @package mod_lesson
* @copyright 2017 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class activity_base extends \core_analytics\local\indicator\community_of_inquiry_activity {
/**
* feedback_viewed_events
*
* @return string
*/
protected function feedback_viewed_events() {
return array('\mod_lesson\event\lesson_ended');
}
/**
* feedback_check_grades
*
* @return bool
*/
protected function feedback_check_grades() {
// We don't need to check grades as we get the feedback while completing the activity.
return false;
}
/**
* Returns the name of the field that controls activity availability.
*
* @return null|string
*/
protected function get_timeclose_field() {
return 'deadline';
}
}
@@ -0,0 +1,90 @@
<?php
// 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/>.
/**
* Cognitive depth indicator - lesson.
*
* @package mod_lesson
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\analytics\indicator;
defined('MOODLE_INTERNAL') || die();
/**
* Cognitive depth indicator - lesson.
*
* @package mod_lesson
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cognitive_depth extends activity_base {
/**
* Returns the name.
*
* If there is a corresponding '_help' string this will be shown as well.
*
* @return \lang_string
*/
public static function get_name(): \lang_string {
return new \lang_string('indicator:cognitivedepth', 'mod_lesson');
}
public function get_indicator_type() {
return self::INDICATOR_COGNITIVE;
}
public function get_cognitive_depth_level(\cm_info $cm) {
return self::COGNITIVE_LEVEL_5;
}
/**
* feedback_submitted
*
* @param \cm_info $cm
* @param int $contextid
* @param int $userid
* @param int $after
* @return bool
*/
protected function feedback_submitted(\cm_info $cm, $contextid, $userid, $after = false) {
if (empty($this->activitylogs[$contextid][$userid]) ||
empty($this->activitylogs[$contextid][$userid]['\mod_lesson\event\lesson_ended'])) {
return false;
}
// Multiple lesson attempts completed counts as submitted after feedback.
return (2 >= count($this->activitylogs[$contextid][$userid]['\mod_lesson\event\lesson_ended']->timecreated));
}
/**
* feedback_replied
*
* @param \cm_info $cm
* @param int $contextid
* @param int $userid
* @param int $after
* @return bool
*/
protected function feedback_replied(\cm_info $cm, $contextid, $userid, $after = false) {
// No level 4.
return false;
}
}
@@ -0,0 +1,56 @@
<?php
// 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/>.
/**
* Social breadth indicator - lesson.
*
* @package mod_lesson
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\analytics\indicator;
defined('MOODLE_INTERNAL') || die();
/**
* Social breadth indicator - lesson.
*
* @package mod_lesson
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class social_breadth extends activity_base {
/**
* Returns the name.
*
* If there is a corresponding '_help' string this will be shown as well.
*
* @return \lang_string
*/
public static function get_name(): \lang_string {
return new \lang_string('indicator:socialbreadth', 'mod_lesson');
}
public function get_indicator_type() {
return self::INDICATOR_SOCIAL;
}
public function get_social_breadth_level(\cm_info $cm) {
return self::SOCIAL_LEVEL_2;
}
}
+110
View File
@@ -0,0 +1,110 @@
<?php
// 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/>.
/**
* Cache data source for the lesson overrides.
*
* @package mod_lesson
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
namespace mod_lesson\cache;
use cache_definition;
/**
* Class lesson_overrides
*
* @package mod_lesson
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class overrides implements \cache_data_source {
/** @var overrides the singleton instance of this class. */
protected static $instance = null;
/**
* Returns an instance of the data source class that the cache can use for loading data using the other methods
* specified by this interface.
*
* @param cache_definition $definition
* @return object
*/
public static function get_instance_for_cache(cache_definition $definition): overrides {
if (is_null(self::$instance)) {
self::$instance = new overrides();
}
return self::$instance;
}
/**
* Loads the data for the key provided ready formatted for caching.
*
* @param string|int $key The key to load.
* @return mixed What ever data should be returned, or false if it can't be loaded.
* @throws \coding_exception
*/
public function load_for_cache($key) {
global $DB;
[$lessonid, $ug, $ugid] = explode('_', $key);
$lessonid = (int) $lessonid;
switch ($ug) {
case 'u':
$userid = (int) $ugid;
$override = $DB->get_record(
'lesson_overrides',
['lessonid' => $lessonid, 'userid' => $userid],
'available, deadline, timelimit, review, maxattempts, retake, password'
);
break;
case 'g':
$groupid = (int) $ugid;
$override = $DB->get_record(
'lesson_overrides',
['lessonid' => $lessonid, 'groupid' => $groupid],
'available, deadline, timelimit, review, maxattempts, retake, password'
);
break;
default:
throw new \coding_exception('Invalid cache key');
}
// Return null instead of false, because false will not be cached.
return $override ?: null;
}
/**
* Loads several keys for the cache.
*
* @param array $keys An array of keys each of which will be string|int.
* @return array An array of matching data items.
*/
public function load_many_for_cache(array $keys) {
$results = [];
foreach ($keys as $key) {
$results[] = $this->load_for_cache($key);
}
return $results;
}
}
@@ -0,0 +1,109 @@
<?php
// 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/>.
declare(strict_types=1);
namespace mod_lesson\completion;
use core_completion\activity_custom_completion;
/**
* Activity custom completion subclass for the lesson activity.
*
* Contains the class for defining mod_lesson's custom completion rules
* and fetching a lesson instance's completion statuses for a user.
*
* @package mod_lesson
* @copyright Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class custom_completion extends activity_custom_completion {
/**
* Fetches the completion state for a given completion rule.
*
* @param string $rule The completion rule.
* @return int The completion state.
*/
public function get_state(string $rule): int {
global $DB;
$this->validate_rule($rule);
switch ($rule) {
case 'completiontimespent':
$duration = $DB->get_field_sql(
"SELECT SUM(lessontime - starttime)
FROM {lesson_timer}
WHERE lessonid = :lessonid
AND userid = :userid",
['userid' => $this->userid, 'lessonid' => $this->cm->instance]);
$status = ($duration && $duration >= $this->cm->customdata['customcompletionrules']['completiontimespent']);
break;
case 'completionendreached':
$status = $DB->record_exists('lesson_timer',
['lessonid' => $this->cm->instance, 'userid' => $this->userid, 'completed' => 1]);
break;
default:
$status = false;
break;
}
return $status ? COMPLETION_COMPLETE : COMPLETION_INCOMPLETE;
}
/**
* Fetch the list of custom completion rules that this module defines.
*
* @return array
*/
public static function get_defined_custom_rules(): array {
return [
'completiontimespent',
'completionendreached',
];
}
/**
* Returns an associative array of the descriptions of custom completion rules.
*
* @return array
*/
public function get_custom_rule_descriptions(): array {
$timespent = format_time($this->cm->customdata['customcompletionrules']['completiontimespent'] ?? 0);
return [
'completiontimespent' => get_string('completiondetail:timespent', 'lesson', $timespent),
'completionendreached' => get_string('completiondetail:reachend', 'lesson'),
];
}
/**
* Returns an array of all completion rules, in the order they should be displayed to users.
*
* @return array
*/
public function get_sort_order(): array {
return [
'completionview',
'completiontimespent',
'completionendreached',
'completionusegrade',
'completionpassgrade',
];
}
}
+70
View File
@@ -0,0 +1,70 @@
<?php
// 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/>.
/**
* Contains the class for fetching the important dates in mod_lesson for a given module instance and a user.
*
* @package mod_lesson
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
namespace mod_lesson;
use core\activity_dates;
/**
* Class for fetching the important dates in mod_lesson for a given module instance and a user.
*
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class dates extends activity_dates {
/**
* Returns a list of important dates in mod_lesson
*
* @return array
*/
protected function get_dates(): array {
$timeopen = $this->cm->customdata['available'] ?? null;
$timeclose = $this->cm->customdata['deadline'] ?? null;
$now = time();
$dates = [];
if ($timeopen) {
$openlabelid = $timeopen > $now ? 'activitydate:opens' : 'activitydate:opened';
$dates[] = [
'dataid' => 'available',
'label' => get_string($openlabelid, 'course'),
'timestamp' => (int) $timeopen,
];
}
if ($timeclose) {
$closelabelid = $timeclose > $now ? 'activitydate:closes' : 'activitydate:closed';
$dates[] = [
'dataid' => 'deadline',
'label' => get_string($closelabelid, 'course'),
'timestamp' => (int) $timeclose,
];
}
return $dates;
}
}
@@ -0,0 +1,93 @@
<?php
// 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/>.
/**
* The mod_lesson content page viewed event class.
*
* @package mod_lesson
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson content page viewed event class.
*
* @package mod_lesson
* @since Moodle 2.9
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class content_page_viewed extends \core\event\base {
/**
* Set basic properties for the event.
*/
protected function init() {
$this->data['objecttable'] = 'lesson_pages';
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventcontentpageviewed', 'mod_lesson');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/view.php', array('id' => $this->contextinstanceid, 'pageid' => $this->objectid));
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' has viewed the content page with id '$this->objectid' in " .
"the lesson activity with course module id '$this->contextinstanceid'.";
}
/**
* Custom validations.
*
* @throws \coding_exception when validation fails.
* @return void
*/
protected function validate_data() {
parent::validate_data();
// Make sure this class is never used without proper object details.
if (!$this->contextlevel === CONTEXT_MODULE) {
throw new \coding_exception('Context level must be CONTEXT_MODULE.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'lesson_pages', 'restore' => 'lesson_page');
}
}
@@ -0,0 +1,39 @@
<?php
// 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/>.
/**
* The mod_lesson instance list viewed event.
*
* @package mod_lesson
* @copyright 2013 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson instance list viewed event class.
*
* @package mod_lesson
* @since Moodle 2.7
* @copyright 2013 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_module_instance_list_viewed extends \core\event\course_module_instance_list_viewed {
// No code required here as the parent class handles it all.
}
@@ -0,0 +1,52 @@
<?php
// 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/>.
/**
* The mod_lesson course module viewed event.
*
* @package mod_lesson
* @since Moodle 2.7
* @copyright 2013 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson course module viewed event class.
*
* @package mod_lesson
* @since Moodle 2.7
* @copyright 2013 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_module_viewed extends \core\event\course_module_viewed {
/**
* Set basic properties for the event.
*/
protected function init() {
$this->data['objecttable'] = 'lesson';
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
public static function get_objectid_mapping() {
return array('db' => 'lesson', 'restore' => 'lesson');
}
}
+115
View File
@@ -0,0 +1,115 @@
<?php
// 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/>.
/**
* The mod_lesson essay assessed event.
*
* @package mod_lesson
* @copyright 2014 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson essay assessed event class.
*
* @property-read array $other {
* Extra information about the event.
*
* - int lessonid: The ID of the lesson.
* - int attemptid: The ID for the attempt.
* }
*
* @package mod_lesson
* @since Moodle 2.7
* @copyright 2014 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class essay_assessed extends \core\event\base {
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_TEACHING;
$this->data['objecttable'] = 'lesson_grades';
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' has marked the essay with id '{$this->other['attemptid']}' and " .
"recorded a mark '$this->objectid' in the lesson with course module id '$this->contextinstanceid'.";
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventessayassessed', 'mod_lesson');
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/essay.php', array('id' => $this->contextinstanceid));
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->relateduserid)) {
throw new \coding_exception('The \'relateduserid\' must be set.');
}
if (!isset($this->other['lessonid'])) {
throw new \coding_exception('The \'lessonid\' value must be set in other.');
}
if (!isset($this->other['attemptid'])) {
throw new \coding_exception('The \'attemptid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'lesson_grades', 'restore' => 'lesson_grade');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['lessonid'] = array('db' => 'lesson', 'restore' => 'lesson');
$othermapped['attemptid'] = array('db' => 'lesson_attempts', 'restore' => 'lesson_attept');
return $othermapped;
}
}
@@ -0,0 +1,94 @@
<?php
// 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/>.
/**
* The mod_lesson essay attempt viewed event.
*
* @package mod_lesson
* @copyright 2013 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson essay attempt viewed event class.
*
* @package mod_lesson
* @since Moodle 2.7
* @copyright 2013 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class essay_attempt_viewed extends \core\event\base {
/**
* Set basic properties for the event.
*/
protected function init() {
$this->data['objecttable'] = 'lesson_attempts';
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventessayattemptviewed', 'mod_lesson');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/essay.php', array('id' => $this->contextinstanceid,
'mode' => 'grade', 'attemptid' => $this->objectid));
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' viewed the essay grade for the user with id '$this->relateduserid' for " .
"the attempt with id '$this->objectid' for the lesson activity with course module id '$this->contextinstanceid'.";
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->relateduserid)) {
throw new \coding_exception('The \'relateduserid\' must be set.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'lesson_attempts', 'restore' => 'lesson_attempt');
}
}
@@ -0,0 +1,112 @@
<?php
// 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/>.
/**
* The mod_lesson group override created event.
*
* @package mod_lesson
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson group override created event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int lessonid: the id of the lesson.
* - int groupid: the id of the group.
* }
*
* @package mod_lesson
* @since Moodle 2.9
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class group_override_created extends \core\event\base {
/**
* Init method.
*/
protected function init() {
$this->data['objecttable'] = 'lesson_overrides';
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventoverridecreated', 'mod_lesson');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' created the override with id '$this->objectid' for the lesson with " .
"course module id '$this->contextinstanceid' for the group with id '{$this->other['groupid']}'.";
}
/**
* Returns relevant URL.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/overrideedit.php', array('id' => $this->objectid));
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['lessonid'])) {
throw new \coding_exception('The \'lessonid\' value must be set in other.');
}
if (!isset($this->other['groupid'])) {
throw new \coding_exception('The \'groupid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'lesson_overrides', 'restore' => 'lesson_override');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['lessonid'] = array('db' => 'lesson', 'restore' => 'lesson');
$othermapped['groupid'] = array('db' => 'groups', 'restore' => 'group');
return $othermapped;
}
}
@@ -0,0 +1,111 @@
<?php
// 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/>.
/**
* The mod_lesson group override deleted event.
*
* @package mod_lesson
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson group override deleted event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int lessonid: the id of the lesson.
* - int groupid: the id of the group.
* }
*
* @package mod_lesson
* @since Moodle 2.9
* @copyright 2015 Jean-Michel vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class group_override_deleted extends \core\event\base {
/**
* Init method.
*/
protected function init() {
$this->data['objecttable'] = 'lesson_overrides';
$this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventoverridedeleted', 'mod_lesson');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' deleted the override with id '$this->objectid' for the lesson with " .
"course module id '$this->contextinstanceid' for the group with id '{$this->other['groupid']}'.";
}
/**
* Returns relevant URL.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/overrides.php', array('cmid' => $this->contextinstanceid));
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['lessonid'])) {
throw new \coding_exception('The \'lessonid\' value must be set in other.');
}
if (!isset($this->other['groupid'])) {
throw new \coding_exception('The \'groupid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'lesson_overrides', 'restore' => 'lesson_override');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['lessonid'] = array('db' => 'lesson', 'restore' => 'lesson');
$othermapped['groupid'] = array('db' => 'groups', 'restore' => 'group');
return $othermapped;
}
}
@@ -0,0 +1,111 @@
<?php
// 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/>.
/**
* The mod_lesson group override updated event.
*
* @package mod_lesson
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson group override updated event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int lessonid: the id of the lesson.
* - int groupid: the id of the group.
* }
*
* @package mod_lesson
* @since Moodle 2.9
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class group_override_updated extends \core\event\base {
/**
* Init method.
*/
protected function init() {
$this->data['objecttable'] = 'lesson_overrides';
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventoverrideupdated', 'mod_lesson');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' updated the override with id '$this->objectid' for the lesson with " .
"course module id '$this->contextinstanceid' for the group with id '{$this->other['groupid']}'.";
}
/**
* Returns relevant URL.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/overrideedit.php', array('id' => $this->objectid));
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['lessonid'])) {
throw new \coding_exception('The \'lessonid\' value must be set in other.');
}
if (!isset($this->other['groupid'])) {
throw new \coding_exception('The \'groupid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'lesson_overrides', 'restore' => 'lesson_override');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['lessonid'] = array('db' => 'lesson', 'restore' => 'lesson');
$othermapped['groupid'] = array('db' => 'groups', 'restore' => 'group');
return $othermapped;
}
}
@@ -0,0 +1,114 @@
<?php
// 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/>.
/**
* The mod_lesson highscore added event.
*
* @package mod_lesson
* @deprecated since Moodle 3.0
* @copyright 2013 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
debugging('mod_lesson\event\highscore_added has been deprecated. Since the functionality no longer resides in the lesson module.',
DEBUG_DEVELOPER);
/**
* The mod_lesson highscore added event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int lessonid: the id of the lesson in the lesson table.
* - string nickname: the user's nickname.
* }
*
* @package mod_lesson
* @since Moodle 2.7
* @copyright 2013 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class highscore_added extends \core\event\base {
/**
* Set basic properties for the event.
*/
protected function init() {
$this->data['objecttable'] = 'lesson_high_scores';
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventhighscoreadded', 'mod_lesson');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/highscores.php', array('id' => $this->contextinstanceid));
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' added a new highscore to the lesson activity with course module " .
"id '$this->contextinstanceid'.";
}
/**
* Custom validations.
*
* @throws \coding_exception when validation fails.
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['lessonid'])) {
throw new \coding_exception('The \'lessonid\' value must be set in other.');
}
if (!isset($this->other['nickname'])) {
throw new \coding_exception('The \'nickname\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
// The 'highscore' functionality was removed from core.
return false;
}
public static function get_other_mapping() {
// The 'highscore' functionality was removed from core.
return false;
}
}
@@ -0,0 +1,88 @@
<?php
// 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/>.
/**
* The mod_lesson highscores viewed.
*
* @package mod_lesson
* @deprecated since Moodle 3.0
* @copyright 2013 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
debugging('mod_lesson\event\highscores_viewed has been deprecated. Since the functionality no longer resides in the lesson module.',
DEBUG_DEVELOPER);
/**
* The mod_lesson highscores viewed class.
*
* @package mod_lesson
* @since Moodle 2.7
* @copyright 2013 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class highscores_viewed extends \core\event\base {
/**
* Set basic properties for the event.
*/
protected function init() {
$this->data['objecttable'] = 'lesson';
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventhighscoresviewed', 'mod_lesson');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/highscores.php', array('id' => $this->contextinstanceid));
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' viewed the highscores for the lesson activity with course module " .
"id '$this->contextinstanceid'.";
}
public static function get_objectid_mapping() {
// The 'highscore' functionality was removed from core.
return false;
}
public static function get_other_mapping() {
// The 'highscore' functionality was removed from core.
return false;
}
}
+79
View File
@@ -0,0 +1,79 @@
<?php
// 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/>.
/**
* The mod_lesson lesson ended event.
*
* @package mod_lesson
* @copyright 2013 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson lesson ended event class.
*
* @package mod_lesson
* @since Moodle 2.7
* @copyright 2013 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class lesson_ended extends \core\event\base {
/**
* Set basic properties for the event.
*/
protected function init() {
$this->data['objecttable'] = 'lesson';
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventlessonended', 'mod_lesson');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/view.php', array('id' => $this->contextinstanceid));
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' ended the lesson with course module id '$this->contextinstanceid'.";
}
public static function get_objectid_mapping() {
return array('db' => 'lesson', 'restore' => 'lesson');
}
}
@@ -0,0 +1,79 @@
<?php
// 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/>.
/**
* The mod_lesson lesson restarted event class.
*
* @package mod_lesson
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson lesson restarted event class
*
* @package mod_lesson
* @since Moodle 2.9
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class lesson_restarted extends \core\event\base {
/**
* Set basic properties for the event.
*/
protected function init() {
$this->data['objecttable'] = 'lesson';
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventlessonrestarted', 'mod_lesson');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/view.php', array('id' => $this->contextinstanceid));
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' abandoned their previous incomplete attempt ".
"and started a new attempt on the lesson with course module id '$this->contextinstanceid'.";
}
public static function get_objectid_mapping() {
return array('db' => 'lesson', 'restore' => 'lesson');
}
}
@@ -0,0 +1,79 @@
<?php
// 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/>.
/**
* The mod_lesson lesson resumed event class.
*
* @package mod_lesson
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson lesson resumed event class
*
* @package mod_lesson
* @since Moodle 2.9
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class lesson_resumed extends \core\event\base {
/**
* Set basic properties for the event.
*/
protected function init() {
$this->data['objecttable'] = 'lesson';
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventlessonresumed', 'mod_lesson');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/view.php', array('id' => $this->contextinstanceid));
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' resumed their previous incomplete attempt on".
" the lesson with course module id '$this->contextinstanceid'.";
}
public static function get_objectid_mapping() {
return array('db' => 'lesson', 'restore' => 'lesson');
}
}
@@ -0,0 +1,78 @@
<?php
// 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/>.
/**
* The mod_lesson lesson started event.
*
* @package mod_lesson
* @copyright 2013 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson lesson started event class.
*
* @package mod_lesson
* @since Moodle 2.7
* @copyright 2013 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class lesson_started extends \core\event\base {
/**
* Set basic properties for the event.
*/
protected function init() {
$this->data['objecttable'] = 'lesson';
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventlessonstarted', 'mod_lesson');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/view.php', array('id' => $this->contextinstanceid));
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' started the lesson with course module id '$this->contextinstanceid'.";
}
public static function get_objectid_mapping() {
return array('db' => 'lesson', 'restore' => 'lesson');
}
}
+107
View File
@@ -0,0 +1,107 @@
<?php
// 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/>.
/**
* The mod_lesson page_added event class.
*
* @package mod_lesson
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson page_created event class.
*
* @property-read array $other {
* Extra information about event.
*
* - string pagetype: the name of the pagetype as defined in the individual page class
* }
*
* @package mod_lesson
* @since Moodle 2.9
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class page_created extends \core\event\base {
/**
* Set basic properties for the event.
*/
protected function init() {
$this->data['objecttable'] = 'lesson_pages';
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventpagecreated', 'mod_lesson');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/view.php', array('id' => $this->contextinstanceid, 'pageid' => $this->objectid));
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' has created a ".$this->other['pagetype']." page with the ".
"id '$this->objectid' in the lesson activity with course module id '$this->contextinstanceid'.";
}
/**
* Custom validations.
*
* @throws \coding_exception when validation fails.
* @return void
*/
protected function validate_data() {
parent::validate_data();
// Make sure this class is never used without proper object details.
if (!$this->contextlevel === CONTEXT_MODULE) {
throw new \coding_exception('Context level must be CONTEXT_MODULE.');
}
if (!isset($this->other['pagetype'])) {
throw new \coding_exception('The \'pagetype\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'lesson_pages', 'restore' => 'lesson_page');
}
public static function get_other_mapping() {
// Nothing to map.
return false;
}
}
+107
View File
@@ -0,0 +1,107 @@
<?php
// 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/>.
/**
* The mod_lesson page_added event class.
*
* @package mod_lesson
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson page_deleted event class.
*
* @property-read array $other {
* Extra information about event.
*
* - string pagetype: the name of the pagetype as defined in the individual page class
* }
*
* @package mod_lesson
* @since Moodle 2.9
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class page_deleted extends \core\event\base {
/**
* Set basic properties for the event.
*/
protected function init() {
$this->data['objecttable'] = 'lesson_pages';
$this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventpagedeleted', 'mod_lesson');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/view.php', array('id' => $this->contextinstanceid));
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' has deleted the ".$this->other['pagetype']." page with the ".
"id '$this->objectid' in the lesson activity with course module id '$this->contextinstanceid'.";
}
/**
* Custom validations.
*
* @throws \coding_exception when validation fails.
* @return void
*/
protected function validate_data() {
parent::validate_data();
// Make sure this class is never used without proper object details.
if (!$this->contextlevel === CONTEXT_MODULE) {
throw new \coding_exception('Context level must be CONTEXT_MODULE.');
}
if (!isset($this->other['pagetype'])) {
throw new \coding_exception('The \'pagetype\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'lesson_pages', 'restore' => 'lesson_page');
}
public static function get_other_mapping() {
// Nothing to map.
return false;
}
}
+120
View File
@@ -0,0 +1,120 @@
<?php
// 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/>.
/**
* The mod_lesson page_moved event class.
*
* @package mod_lesson
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson page_moved event class.
*
* @property-read array $other {
* Extra information about event.
*
* - string pagetype: the name of the pagetype as defined in the individual page class
* - int prevpageid: the id of the previous lesson page
* - int nextpageid: the id of the next lesson page
* }
*
* @package mod_lesson
* @since Moodle 2.9
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class page_moved extends \core\event\base {
/**
* Set basic properties for the event.
*/
protected function init() {
$this->data['objecttable'] = 'lesson_pages';
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventpagemoved', 'mod_lesson');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/view.php', array('id' => $this->contextinstanceid, 'pageid' => $this->objectid));
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' has moved a ".$this->other['pagetype']." page with the ".
"id '$this->objectid' to the slot after the page with the id '".$this->other['prevpageid'].
"' and before the page with the id '".$this->other['nextpageid'].
"' in the lesson activity with course module id '$this->contextinstanceid'.";
}
/**
* Custom validations.
*
* @throws \coding_exception when validation fails.
* @return void
*/
protected function validate_data() {
parent::validate_data();
// Make sure this class is never used without proper object details.
if (!$this->contextlevel === CONTEXT_MODULE) {
throw new \coding_exception('Context level must be CONTEXT_MODULE.');
}
if (!isset($this->other['pagetype'])) {
throw new \coding_exception('The \'pagetype\' value must be set in other.');
}
if (!isset($this->other['prevpageid'])) {
throw new \coding_exception('The \'prevpageid\' value must be set in other.');
}
if (!isset($this->other['nextpageid'])) {
throw new \coding_exception('The \'nextpageid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'lesson_pages', 'restore' => 'lesson_page');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['prevpageid'] = array('db' => 'lesson_pages', 'restore' => 'lesson_page');
$othermapped['nextpageid'] = array('db' => 'lesson_pages', 'restore' => 'lesson_page');
return $othermapped;
}
}
+126
View File
@@ -0,0 +1,126 @@
<?php
// 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/>.
/**
* The mod_lesson page_added event class.
*
* @package mod_lesson
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson page_updated event class.
*
* @property-read array $other {
* Extra information about event.
*
* - string pagetype: the name of the pagetype as defined in the individual page class
* }
*
* @package mod_lesson
* @since Moodle 2.9
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class page_updated extends \core\event\base {
/**
* Create instance of event.
*
* @param \lesson_page $lessonpage
* @param \context_module $context
* @return page_updated
*/
public static function create_from_lesson_page(\lesson_page $lessonpage, \context_module $context) {
$data = array(
'context' => $context,
'objectid' => $lessonpage->properties()->id,
'other' => array(
'pagetype' => $lessonpage->get_typestring()
)
);
return self::create($data);
}
/**
* Set basic properties for the event.
*/
protected function init() {
$this->data['objecttable'] = 'lesson_pages';
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventpageupdated', 'mod_lesson');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/view.php', array('id' => $this->contextinstanceid, 'pageid' => $this->objectid));
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' has updated the ".$this->other['pagetype']." page with the ".
"id '$this->objectid' in the lesson activity with course module id '$this->contextinstanceid'.";
}
/**
* Custom validations.
*
* @throws \coding_exception when validation fails.
* @return void
*/
protected function validate_data() {
parent::validate_data();
// Make sure this class is never used without proper object details.
if (!$this->contextlevel === CONTEXT_MODULE) {
throw new \coding_exception('Context level must be CONTEXT_MODULE.');
}
if (!isset($this->other['pagetype'])) {
throw new \coding_exception('The \'pagetype\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'lesson_pages', 'restore' => 'lesson_page');
}
public static function get_other_mapping() {
// Nothing to map.
return false;
}
}
@@ -0,0 +1,107 @@
<?php
// 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/>.
/**
* The mod_lesson true / false question answered event class.
*
* @package mod_lesson
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson question answered event class.
*
* @property-read array $other {
* Extra information about event.
*
* - string pagetype: the name of the pagetype as defined in the individual page class
* }
*
* @package mod_lesson
* @since Moodle 2.9
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class question_answered extends \core\event\base {
/**
* Set basic properties for the event.
*/
protected function init() {
$this->data['objecttable'] = 'lesson_pages';
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventquestionanswered', 'mod_lesson');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/view.php', array('id' => $this->contextinstanceid, 'pageid' => $this->objectid));
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' has answered the ".$this->other['pagetype'] .
" question with id '$this->objectid' in the lesson activity with course module id '$this->contextinstanceid'.";
}
/**
* Custom validations.
*
* @throws \coding_exception when validation fails.
* @return void
*/
protected function validate_data() {
parent::validate_data();
// Make sure this class is never used without proper object details.
if (!$this->contextlevel === CONTEXT_MODULE) {
throw new \coding_exception('Context level must be CONTEXT_MODULE.');
}
if (!isset($this->other['pagetype'])) {
throw new \coding_exception('The \'pagetype\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'lesson_pages', 'restore' => 'lesson_page');
}
public static function get_other_mapping() {
// Nothing to map.
return false;
}
}
@@ -0,0 +1,107 @@
<?php
// 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/>.
/**
* The mod_lesson true / false question viewed event class.
*
* @package mod_lesson
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson question viewed event class.
*
* @property-read array $other {
* Extra information about event.
*
* - string pagetype: the name of the pagetype as defined in the individual page class
* }
*
* @package mod_lesson
* @since Moodle 2.9
* @copyright 2015 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
class question_viewed extends \core\event\base {
/**
* Set basic properties for the event.
*/
protected function init() {
$this->data['objecttable'] = 'lesson_pages';
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventquestionviewed', 'mod_lesson');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/view.php', array('id' => $this->contextinstanceid, 'pageid' => $this->objectid));
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' has viewed the ".$this->other['pagetype'] .
" question with id '$this->objectid' in the lesson activity with course module id '$this->contextinstanceid'.";
}
/**
* Custom validations.
*
* @throws \coding_exception when validation fails.
* @return void
*/
protected function validate_data() {
parent::validate_data();
// Make sure this class is never used without proper object details.
if (!$this->contextlevel === CONTEXT_MODULE) {
throw new \coding_exception('Context level must be CONTEXT_MODULE.');
}
if (!isset($this->other['pagetype'])) {
throw new \coding_exception('The \'pagetype\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'lesson_pages', 'restore' => 'lesson_page');
}
public static function get_other_mapping() {
// Nothing to map.
return false;
}
}
@@ -0,0 +1,109 @@
<?php
// 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/>.
/**
* The mod_lesson user override created event.
*
* @package mod_lesson
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson user override created event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int lessonid: the id of the lesson.
* }
*
* @package mod_lesson
* @since Moodle 2.9
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_override_created extends \core\event\base {
/**
* Init method.
*/
protected function init() {
$this->data['objecttable'] = 'lesson_overrides';
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventoverridecreated', 'mod_lesson');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' created the override with id '$this->objectid' for the lesson with " .
"course module id '$this->contextinstanceid' for the user with id '{$this->relateduserid}'.";
}
/**
* Returns relevant URL.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/overrideedit.php', array('id' => $this->objectid));
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->relateduserid)) {
throw new \coding_exception('The \'relateduserid\' must be set.');
}
if (!isset($this->other['lessonid'])) {
throw new \coding_exception('The \'lessonid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'lesson_overrides', 'restore' => 'lesson_override');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['lessonid'] = array('db' => 'lesson', 'restore' => 'lesson');
return $othermapped;
}
}
@@ -0,0 +1,109 @@
<?php
// 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/>.
/**
* The mod_lesson user override deleted event.
*
* @package mod_lesson
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson user override deleted event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int lessonid: the id of the lesson.
* }
*
* @package mod_lesson
* @since Moodle 2.9
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_override_deleted extends \core\event\base {
/**
* Init method.
*/
protected function init() {
$this->data['objecttable'] = 'lesson_overrides';
$this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventoverridedeleted', 'mod_lesson');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' deleted the override with id '$this->objectid' for the lesson with " .
"course module id '$this->contextinstanceid' for the user with id '{$this->relateduserid}'.";
}
/**
* Returns relevant URL.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/overrides.php', array('cmid' => $this->contextinstanceid));
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->relateduserid)) {
throw new \coding_exception('The \'relateduserid\' must be set.');
}
if (!isset($this->other['lessonid'])) {
throw new \coding_exception('The \'lessonid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'lesson_overrides', 'restore' => 'lesson_override');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['lessonid'] = array('db' => 'lesson', 'restore' => 'lesson');
return $othermapped;
}
}
@@ -0,0 +1,110 @@
<?php
// 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/>.
/**
* The mod_lesson user override updated event.
*
* @package mod_lesson
* @copyright 2015 Jean-Michel vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_lesson user override updated event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int lessonid: the id of the lesson.
* }
*
* @package mod_lesson
* @since Moodle 2.9
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_override_updated extends \core\event\base {
/**
* Init method.
*/
protected function init() {
$this->data['objecttable'] = 'lesson_overrides';
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventoverrideupdated', 'mod_lesson');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' updated the override with id '$this->objectid' for the lesson with " .
"course module id '$this->contextinstanceid' for the user with id '{$this->relateduserid}'.";
}
/**
* Returns relevant URL.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/lesson/overrideedit.php', array('id' => $this->objectid));
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->relateduserid)) {
throw new \coding_exception('The \'relateduserid\' must be set.');
}
if (!isset($this->other['lessonid'])) {
throw new \coding_exception('The \'lessonid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'lesson_overrides', 'restore' => 'lesson_override');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['lessonid'] = array('db' => 'lesson', 'restore' => 'lesson');
return $othermapped;
}
}
File diff suppressed because it is too large Load Diff
+312
View File
@@ -0,0 +1,312 @@
<?php
// 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/>.
/**
* Class for exporting partial lesson data.
*
* @package mod_lesson
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
use renderer_base;
use core_external\external_files;
use core_external\util as external_util;
/**
* Class for exporting partial lesson data (some fields are only viewable by admins).
*
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class lesson_summary_exporter extends exporter {
protected static function define_properties() {
return array(
'id' => array(
'type' => PARAM_INT,
'description' => 'Standard Moodle primary key.'
),
'course' => array(
'type' => PARAM_INT,
'description' => 'Foreign key reference to the course this lesson is part of.'
),
'coursemodule' => array(
'type' => PARAM_INT,
'description' => 'Course module id.'
),
'name' => array(
'type' => PARAM_RAW,
'description' => 'Lesson name.'
),
'intro' => array(
'type' => PARAM_RAW,
'description' => 'Lesson introduction text.',
'optional' => true,
),
'introformat' => array(
'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN),
'type' => PARAM_INT,
'default' => FORMAT_MOODLE
),
'lang' => array(
'type' => PARAM_LANG,
'description' => 'Forced activity language',
'null' => NULL_ALLOWED,
),
'practice' => array(
'type' => PARAM_BOOL,
'description' => 'Practice lesson?',
'optional' => true,
),
'modattempts' => array(
'type' => PARAM_BOOL,
'description' => 'Allow student review?',
'optional' => true,
),
'usepassword' => array(
'type' => PARAM_BOOL,
'description' => 'Password protected lesson?',
'optional' => true,
),
'password' => array(
'type' => PARAM_RAW,
'description' => 'Password',
'optional' => true,
),
'dependency' => array(
'type' => PARAM_INT,
'description' => 'Dependent on (another lesson id)',
'optional' => true,
),
'conditions' => array(
'type' => PARAM_RAW,
'description' => 'Conditions to enable the lesson',
'optional' => true,
),
'grade' => array(
'type' => PARAM_INT,
'description' => 'The total that the grade is scaled to be out of',
'optional' => true,
),
'custom' => array(
'type' => PARAM_BOOL,
'description' => 'Custom scoring?',
'optional' => true,
),
'ongoing' => array(
'type' => PARAM_BOOL,
'description' => 'Display ongoing score?',
'optional' => true,
),
'usemaxgrade' => array(
'type' => PARAM_INT,
'description' => 'How to calculate the final grade',
'optional' => true,
),
'maxanswers' => array(
'type' => PARAM_INT,
'description' => 'Maximum answers per page',
'optional' => true,
),
'maxattempts' => array(
'type' => PARAM_INT,
'description' => 'Maximum attempts',
'optional' => true,
),
'review' => array(
'type' => PARAM_BOOL,
'description' => 'Provide option to try a question again',
'optional' => true,
),
'nextpagedefault' => array(
'type' => PARAM_INT,
'description' => 'Action for a correct answer',
'optional' => true,
),
'feedback' => array(
'type' => PARAM_BOOL,
'description' => 'Display default feedback',
'optional' => true,
),
'minquestions' => array(
'type' => PARAM_INT,
'description' => 'Minimum number of questions',
'optional' => true,
),
'maxpages' => array(
'type' => PARAM_INT,
'description' => 'Number of pages to show',
'optional' => true,
),
'timelimit' => array(
'type' => PARAM_INT,
'description' => 'Time limit',
'optional' => true,
),
'retake' => array(
'type' => PARAM_BOOL,
'description' => 'Re-takes allowed',
'optional' => true,
),
'activitylink' => array(
'type' => PARAM_INT,
'description' => 'Id of the next activity to be linked once the lesson is completed',
'optional' => true,
),
'mediafile' => array(
'type' => PARAM_RAW,
'description' => 'Local file path or full external URL',
'optional' => true,
),
'mediaheight' => array(
'type' => PARAM_INT,
'description' => 'Popup for media file height',
'optional' => true,
),
'mediawidth' => array(
'type' => PARAM_INT,
'description' => 'Popup for media with',
'optional' => true,
),
'mediaclose' => array(
'type' => PARAM_INT,
'description' => 'Display a close button in the popup?',
'optional' => true,
),
'slideshow' => array(
'type' => PARAM_BOOL,
'description' => 'Display lesson as slideshow',
'optional' => true,
),
'width' => array(
'type' => PARAM_INT,
'description' => 'Slideshow width',
'optional' => true,
),
'height' => array(
'type' => PARAM_INT,
'description' => 'Slideshow height',
'optional' => true,
),
'bgcolor' => array(
'type' => PARAM_TEXT,
'description' => 'Slideshow bgcolor',
'optional' => true,
),
'displayleft' => array(
'type' => PARAM_BOOL,
'description' => 'Display left pages menu?',
'optional' => true,
),
'displayleftif' => array(
'type' => PARAM_INT,
'description' => 'Minimum grade to display menu',
'optional' => true,
),
'progressbar' => array(
'type' => PARAM_BOOL,
'description' => 'Display progress bar?',
'optional' => true,
),
'available' => array(
'type' => PARAM_INT,
'description' => 'Available from',
'optional' => true,
),
'deadline' => array(
'type' => PARAM_INT,
'description' => 'Available until',
'optional' => true,
),
'timemodified' => array(
'type' => PARAM_INT,
'description' => 'Last time settings were updated',
'optional' => true,
),
'completionendreached' => array(
'type' => PARAM_INT,
'description' => 'Require end reached for completion?',
'optional' => true,
),
'completiontimespent' => array(
'type' => PARAM_INT,
'description' => 'Student must do this activity at least for',
'optional' => true,
),
'allowofflineattempts' => array(
'type' => PARAM_BOOL,
'description' => 'Whether to allow the lesson to be attempted offline in the mobile app',
),
);
}
protected static function define_related() {
return array(
'context' => 'context'
);
}
protected static function define_other_properties() {
return array(
'coursemodule' => array(
'type' => PARAM_INT
),
'introfiles' => array(
'type' => external_files::get_properties_for_exporter(),
'multiple' => true,
'optional' => true,
),
'mediafiles' => array(
'type' => external_files::get_properties_for_exporter(),
'multiple' => true,
'optional' => true,
),
);
}
protected function get_other_values(renderer_base $output) {
$context = $this->related['context'];
$values = array(
'coursemodule' => $context->instanceid,
);
if (isset($this->data->intro)) {
$values['introfiles'] = external_util::get_area_files($context->id, 'mod_lesson', 'intro', false, false);
$values['mediafiles'] = external_util::get_area_files($context->id, 'mod_lesson', 'mediafile', 0);
}
return $values;
}
/**
* Get the formatting parameters for the intro.
*
* @return array
*/
protected function get_format_parameters_for_intro() {
return [
'component' => 'mod_lesson',
'filearea' => 'intro',
'options' => array('noclean' => true),
];
}
}
+194
View File
@@ -0,0 +1,194 @@
<?php
// 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/>.
/**
* File browsing support.
*
* @package mod_lesson
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* File browsing support class.
*
* @package mod_lesson
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_lesson_file_info extends file_info {
/** @var stdClass Course object */
protected $course;
/** @var stdClass Course module object */
protected $cm;
/** @var array Available file areas */
protected $areas;
/** @var string File area to browse */
protected $filearea;
/**
* Constructor
*
* @param file_browser $browser file_browser instance
* @param stdClass $course course object
* @param stdClass $cm course module object
* @param stdClass $context module context
* @param array $areas available file areas
* @param string $filearea file area to browse
*/
public function __construct($browser, $course, $cm, $context, $areas, $filearea) {
parent::__construct($browser, $context);
$this->course = $course;
$this->cm = $cm;
$this->areas = $areas;
$this->filearea = $filearea;
}
/**
* Returns list of standard virtual file/directory identification.
* The difference from stored_file parameters is that null values
* are allowed in all fields
* @return array with keys contextid, filearea, itemid, filepath and filename
*/
public function get_params() {
return array('contextid' => $this->context->id,
'component' => 'mod_lesson',
'filearea' => $this->filearea,
'itemid' => null,
'filepath' => null,
'filename' => null);
}
/**
* Returns localised visible name.
* @return string
*/
public function get_visible_name() {
return $this->areas[$this->filearea];
}
/**
* Can I add new files or directories?
* @return bool
*/
public function is_writable() {
return false;
}
/**
* Is directory?
* @return bool
*/
public function is_directory() {
return true;
}
/**
* Returns list of children.
* @return array of file_info instances
*/
public function get_children() {
return $this->get_filtered_children('*', false, true);
}
/**
* Help function to return files matching extensions or their count
*
* @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
* @param bool|int $countonly if false returns the children, if an int returns just the
* count of children but stops counting when $countonly number of children is reached
* @param bool $returnemptyfolders if true returns items that don't have matching files inside
* @return array|int array of file_info instances or the count
*/
private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
global $DB;
$params = array(
'contextid' => $this->context->id,
'component' => 'mod_lesson',
'filearea' => $this->filearea
);
$sql = 'SELECT DISTINCT itemid
FROM {files}
WHERE contextid = :contextid
AND component = :component
AND filearea = :filearea';
if (!$returnemptyfolders) {
$sql .= ' AND filename <> :emptyfilename';
$params['emptyfilename'] = '.';
}
list($sql2, $params2) = $this->build_search_files_sql($extensions);
$sql .= ' ' . $sql2;
$params = array_merge($params, $params2);
if ($countonly !== false) {
$sql .= ' ORDER BY itemid DESC';
}
$rs = $DB->get_recordset_sql($sql, $params);
$children = array();
foreach ($rs as $record) {
if (($child = $this->browser->get_file_info($this->context, 'mod_lesson', $this->filearea, $record->itemid))
&& ($returnemptyfolders || $child->count_non_empty_children($extensions))) {
$children[] = $child;
}
if ($countonly !== false && count($children) >= $countonly) {
break;
}
}
$rs->close();
if ($countonly !== false) {
return count($children);
}
return $children;
}
/**
* Returns list of children which are either files matching the specified extensions
* or folders that contain at least one such file.
*
* @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
* @return array of file_info instances
*/
public function get_non_empty_children($extensions = '*') {
return $this->get_filtered_children($extensions, false);
}
/**
* Returns the number of children which are either files matching the specified extensions
* or folders containing at least one such file.
*
* @param string|array $extensions, for example '*' or array('.gif','.jpg')
* @param int $limit stop counting after at least $limit non-empty children are found
* @return int
*/
public function count_non_empty_children($extensions = '*', $limit = 1) {
return $this->get_filtered_children($extensions, $limit);
}
/**
* Returns parent file_info instance
* @return file_info or null for root
*/
public function get_parent() {
return $this->browser->get_file_info($this->context);
}
}
+85
View File
@@ -0,0 +1,85 @@
<?php
// 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/>.
/**
* Group observers.
*
* @package mod_lesson
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/lesson/locallib.php');
/**
* Group observers class.
*
* @package mod_lesson
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class group_observers {
/**
* Flag whether a course reset is in progress or not.
*
* @var int The course ID.
*/
protected static $resetinprogress = false;
/**
* A course reset has started.
*
* @param \core\event\base $event The event.
* @return void
*/
public static function course_reset_started($event) {
self::$resetinprogress = $event->courseid;
}
/**
* A course reset has ended.
*
* @param \core\event\base $event The event.
* @return void
*/
public static function course_reset_ended($event) {
if (!empty(self::$resetinprogress)) {
if (!empty($event->other['reset_options']['reset_groups_remove'])) {
lesson_process_group_deleted_in_course($event->courseid);
}
}
self::$resetinprogress = null;
}
/**
* A group was deleted.
*
* @param \core\event\base $event The event.
* @return void
*/
public static function group_deleted($event) {
if (!empty(self::$resetinprogress)) {
// We will take care of that once the course reset ends.
return;
}
lesson_process_group_deleted_in_course($event->courseid, $event->objectid);
}
}
@@ -0,0 +1,79 @@
<?php
// 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/>.
/**
* Lesson's numeric helper lib.
*
* Contains any helper functions for the numeric pagetyep
*
* @package mod_lesson
* @copyright 2020 Peter Dias <peter@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\local\numeric;
/**
* Lesson numeric page helper
*
* @copyright 2020 Peter Dias<peter@moodle.com>
* @package core_lesson
*/
class helper {
/**
* Helper function to unformat a given numeric value from locale specific values with n:n signifying ranges to standards
* with decimal point numbers/ranges
*
* @param string $value The value to be formatted
* @return string|float|bool $formattedvalue unformatted value
* String - If it is a range it will return a value e.g. 2:4
* Float - if it's a properly formatted float
* Null - If empty and could not be converted
*/
public static function lesson_unformat_numeric_value(string $value) {
if (strpos($value, ':')) {
list($min, $max) = explode(':', $value);
$formattedvalue = unformat_float($min) . ':' . unformat_float($max);
} else {
$formattedvalue = unformat_float($value);
}
return $formattedvalue;
}
/**
* Helper function to format a given value into locale specific values with n:n signifying ranges
*
* @param string|number $value The value to be formatted
* @return string $formattedvalue Formatted value OR $value if not numeric
*/
public static function lesson_format_numeric_value($value): string {
$formattedvalue = $value;
if (strpos($value, ':')) {
list($min, $max) = explode(':', $value);
$formattedvalue = $min . ':' . $max;
if (is_numeric($min) && is_numeric($max)) {
$formattedvalue = format_float($min, strlen($min), true, true) . ':'
. format_float($max, strlen($max), true, true);
}
} else {
$formattedvalue = is_numeric($value) ? format_float($value, strlen($value), true, true) : $value;
}
return $formattedvalue;
}
}
@@ -0,0 +1,87 @@
<?php
// 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/>.
/**
* Output the actionbar for this activity.
*
* @package mod_lesson
* @copyright 2021 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\output;
use moodle_url;
use templatable;
use renderable;
/**
* Output the actionbar for this activity.
*
* @package mod_lesson
* @copyright 2021 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class edit_action_area implements templatable, renderable {
/** @var int The course module ID. */
protected $cmid;
/** @var moodle_url The current url for the page. */
protected $currenturl;
/**
* Constructor for this object.
*
* @param int $cmid The course module ID.
* @param moodle_url $currenturl The current url for the page.
*/
public function __construct(int $cmid, moodle_url $currenturl) {
$this->cmid = $cmid;
$this->currenturl = $currenturl;
}
/**
* Data for use with a template.
*
* @param \renderer_base $output render base output.
* @return array Said data.
*/
public function export_for_template(\renderer_base $output): array {
global $PAGE;
$viewurl = new moodle_url('/mod/lesson/edit.php', ['id' => $this->cmid, 'mode' => 'collapsed']);
$fullviewurl = new moodle_url('/mod/lesson/edit.php', ['id' => $this->cmid, 'mode' => 'full']);
$menu = [
$viewurl->out(false) => get_string('collapsed', 'mod_lesson'),
$fullviewurl->out(false) => get_string('full', 'mod_lesson')
];
$selectmenu = new \url_select($menu, $this->currenturl->out(false), null, 'mod_lesson_navigation_select');
$selectmenu->label = get_string('displaymode', 'mod_lesson');
$selectmenu->labelattributes = ['class' => 'sr-only'];
$headinglevel = $PAGE->activityheader->get_heading_level();
return [
'back' => [
'text' => get_string('back', 'core'),
'link' => (new moodle_url('/mod/lesson/view.php', ['id' => $this->cmid]))->out(false)
],
'viewselect' => $selectmenu->export_for_template($output),
'heading' => get_string('editinglesson', 'mod_lesson'),
'headinglevel' => $headinglevel,
];
}
}
@@ -0,0 +1,113 @@
<?php
// 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/>.
/**
* Output the action buttons for this activity.
*
* @package mod_lesson
* @copyright 2021 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\output;
use core\output\notification;
use moodle_url;
use templatable;
use renderable;
use single_button;
/**
* Output the action buttons for this activity.
*
* @package mod_lesson
* @copyright 2021 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class edit_action_buttons implements templatable, renderable {
/** @var \lesson The lesson object. */
protected $lesson;
/** @var int The currently viewed lesson page id. */
protected $currentpage;
/**
* Constructor for this object.
*
* @param \lesson $lesson The lesson object.
* @param int|null $currentpage The current lesson page that is being viewed
*/
public function __construct(\lesson $lesson, ?int $currentpage = null) {
$this->lesson = $lesson;
$this->currentpage = $currentpage;
}
/**
* Sets the current page being viewed.
*
* @param int|null $page
*/
public function set_currentpage(?int $page) {
$this->currentpage = $page;
}
/**
* Data for use with a template.
*
* @param \renderer_base $output Renderer information.
* @return array Said data.
*/
public function export_for_template(\renderer_base $output) {
global $PAGE;
$data = [];
// A shortcut to edit the lesson's question page.
if (has_capability('mod/lesson:edit', $this->lesson->context) &&
!empty($this->currentpage) && $this->currentpage != LESSON_EOL) {
$url = new moodle_url('/mod/lesson/editpage.php', [
'id' => $this->lesson->get_cm()->id,
'pageid' => $this->currentpage,
'edit' => 1,
'returnto' => $PAGE->url->out_as_local_url(false)
]);
$editcontent = new single_button($url, get_string('editpagecontent', 'lesson'));
$data['editcontents']['button'] = $editcontent->export_for_template($output);
}
if ($this->lesson->can_manage()) {
$url = new moodle_url('/mod/lesson/edit.php', ['id' => $this->lesson->get_cm()->id]);
$editbutton = new single_button($url, get_string('editlesson', 'mod_lesson'), 'get', single_button::BUTTON_PRIMARY);
$url = new moodle_url('/mod/lesson/essay.php', ['id' => $this->lesson->get_cm()->id]);
$essaybutton = new single_button($url, get_string('manualgrading', 'mod_lesson'), 'get');
$data += [
'edit' => [
'button' => $editbutton->export_for_template($output),
],
'gradeessays' => [
'button' => $essaybutton->export_for_template($output),
]
];
}
// Standard notification to indicate the lesson is being previewed.
if ($data) {
$message = new notification(get_string('lessonbeingpreviewed', 'mod_lesson'), notification::NOTIFY_INFO);
$data['notification'] = $message->export_for_template($output);
}
return $data;
}
}
@@ -0,0 +1,111 @@
<?php
// 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/>.
/**
* Output the override action menu for this activity.
*
* @package mod_lesson
* @copyright 2021 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\output;
use moodle_url;
use templatable;
use renderable;
/**
* Output the override action menu for this activity.
*
* @package mod_lesson
* @copyright 2021 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class override_action_menu implements templatable, renderable {
/** @var int The course module ID. */
protected $cmid;
/** @var moodle_url The current url for the page. */
protected $currenturl;
/** @var bool Whether can add user or group override (depending on the override type). */
protected $canoverride;
/**
* Constructor for this object.
*
* @param int $cmid The course module ID.
* @param moodle_url $currenturl The current url for the page.
* @param bool $canoverride Whether can add user or group override (depending on the override type).
*/
public function __construct(int $cmid, moodle_url $currenturl, bool $canoverride = false) {
$this->cmid = $cmid;
$this->currenturl = $currenturl;
$this->canoverride = $canoverride;
}
/**
* Creates a select menu for the override options.
*
* @return \url_select The override select.
*/
protected function create_override_select_menu(): \url_select {
$userlink = new moodle_url('/mod/lesson/overrides.php', ['cmid' => $this->cmid, 'mode' => 'user']);
$grouplink = new moodle_url('/mod/lesson/overrides.php', ['cmid' => $this->cmid, 'mode' => 'group']);
$menu = [
$userlink->out(false) => get_string('useroverrides', 'mod_lesson'),
$grouplink->out(false) => get_string('groupoverrides', 'mod_lesson'),
];
$selectmenu = new \url_select($menu, $this->currenturl->out(false), null, 'mod_lesson_override_select');
$selectmenu->label = get_string('manageoverrides', 'mod_lesson');
$selectmenu->labelattributes = ['class' => 'sr-only'];
return $selectmenu;
}
/**
* Data for use with a template.
*
* @param \renderer_base $output renderer base output.
* @return array Said data.
*/
public function export_for_template(\renderer_base $output): array {
global $PAGE;
$type = $this->currenturl->get_param('mode');
if ($type == 'user') {
$text = get_string('addnewuseroverride', 'mod_lesson');
} else {
$text = get_string('addnewgroupoverride', 'mod_lesson');
}
$action = ($type == 'user') ? 'adduser' : 'addgroup';
$urlselect = $this->create_override_select_menu();
$data = [
'urlselect' => $urlselect->export_for_template($output)
];
if ($this->canoverride) {
$data['addoverride'] = [
'text' => $text,
'link' => (new moodle_url('/mod/lesson/overrideedit.php', [
'cmid' => $this->currenturl->get_param('cmid'),
'action' => $action
]))->out(false)
];
}
$data['heading'] = get_string($type == 'user' ? 'useroverrides' : 'groupoverrides', 'mod_lesson');
$data['headinglevel'] = $PAGE->activityheader->get_heading_level();
return $data;
}
}
@@ -0,0 +1,82 @@
<?php
// 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/>.
/**
* Output the report action menu for this activity.
*
* @package mod_lesson
* @copyright 2021 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\output;
use moodle_url;
use templatable;
use renderable;
/**
* Output the report action menu for this activity.
*
* @package mod_lesson
* @copyright 2021 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class report_action_menu implements templatable, renderable {
/** int The lesson id. */
protected $lessonid;
/** moodle_url The url for this page. */
protected $url;
/**
* Constructor for this object.
*
* @param int $lessonid The lessonid.
* @param moodle_url $url The url for this page.
*/
public function __construct(int $lessonid, moodle_url $url) {
$this->lessonid = $lessonid;
$this->url = $url;
}
/**
* Export this url select menu for navigating between reports.
*
* @param \renderer_base $output Renderer output.
* @return array The data for the template.
*/
public function export_for_template(\renderer_base $output): array {
global $PAGE;
$overviewlink = new moodle_url('/mod/lesson/report.php', ['id' => $this->lessonid, 'action' => 'reportoverview']);
$fulllink = new moodle_url('/mod/lesson/report.php', ['id' => $this->lessonid, 'action' => 'reportdetail']);
$menu = [
$overviewlink->out(false) => get_string('overview', 'mod_lesson'),
$fulllink->out(false) => get_string('detailedstats', 'mod_lesson')
];
$reportselect = new \url_select($menu, $this->url->out(false), null, 'lesson-report-select');
$reportselect->label = get_string('selectreport', 'mod_lesson');
$reportselect->labelattributes = ['class' => 'sr-only'];
$data = [
'reportselect' => $reportselect->export_for_template($output),
'heading' => $menu[$reportselect->selected] ?? '',
'headinglevel' => $PAGE->activityheader->get_heading_level(),
];
return $data;
}
}
+698
View File
@@ -0,0 +1,698 @@
<?php
// 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/>.
/**
* Data provider.
*
* @package mod_lesson
* @copyright 2018 Frédéric Massart
* @author Frédéric Massart <fred@branchup.tech>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\privacy;
defined('MOODLE_INTERNAL') || die();
use context;
use context_helper;
use context_module;
use stdClass;
use core_privacy\local\metadata\collection;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\approved_userlist;
use core_privacy\local\request\helper;
use core_privacy\local\request\transform;
use core_privacy\local\request\userlist;
use core_privacy\local\request\writer;
require_once($CFG->dirroot . '/mod/lesson/locallib.php');
require_once($CFG->dirroot . '/mod/lesson/pagetypes/essay.php');
require_once($CFG->dirroot . '/mod/lesson/pagetypes/matching.php');
require_once($CFG->dirroot . '/mod/lesson/pagetypes/multichoice.php');
/**
* Data provider class.
*
* @package mod_lesson
* @copyright 2018 Frédéric Massart
* @author Frédéric Massart <fred@branchup.tech>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
\core_privacy\local\metadata\provider,
\core_privacy\local\request\core_userlist_provider,
\core_privacy\local\request\plugin\provider,
\core_privacy\local\request\user_preference_provider {
/**
* Returns metadata.
*
* @param collection $collection The initialised collection to add items to.
* @return collection A listing of user data stored through this system.
*/
public static function get_metadata(collection $collection): collection {
$collection->add_database_table('lesson_attempts', [
'userid' => 'privacy:metadata:attempts:userid',
'pageid' => 'privacy:metadata:attempts:pageid',
'answerid' => 'privacy:metadata:attempts:answerid',
'retry' => 'privacy:metadata:attempts:retry',
'correct' => 'privacy:metadata:attempts:correct',
'useranswer' => 'privacy:metadata:attempts:useranswer',
'timeseen' => 'privacy:metadata:attempts:timeseen',
], 'privacy:metadata:attempts');
$collection->add_database_table('lesson_grades', [
'userid' => 'privacy:metadata:grades:userid',
'grade' => 'privacy:metadata:grades:grade',
'completed' => 'privacy:metadata:grades:completed',
// The column late is not used.
], 'privacy:metadata:grades');
$collection->add_database_table('lesson_timer', [
'userid' => 'privacy:metadata:timer:userid',
'starttime' => 'privacy:metadata:timer:starttime',
'lessontime' => 'privacy:metadata:timer:lessontime',
'completed' => 'privacy:metadata:timer:completed',
'timemodifiedoffline' => 'privacy:metadata:timer:timemodifiedoffline',
], 'privacy:metadata:timer');
$collection->add_database_table('lesson_branch', [
'userid' => 'privacy:metadata:branch:userid',
'pageid' => 'privacy:metadata:branch:pageid',
'retry' => 'privacy:metadata:branch:retry',
'flag' => 'privacy:metadata:branch:flag',
'timeseen' => 'privacy:metadata:branch:timeseen',
'nextpageid' => 'privacy:metadata:branch:nextpageid',
], 'privacy:metadata:branch');
$collection->add_database_table('lesson_overrides', [
'userid' => 'privacy:metadata:overrides:userid',
'available' => 'privacy:metadata:overrides:available',
'deadline' => 'privacy:metadata:overrides:deadline',
'timelimit' => 'privacy:metadata:overrides:timelimit',
'review' => 'privacy:metadata:overrides:review',
'maxattempts' => 'privacy:metadata:overrides:maxattempts',
'retake' => 'privacy:metadata:overrides:retake',
'password' => 'privacy:metadata:overrides:password',
], 'privacy:metadata:overrides');
$collection->add_user_preference('lesson_view', 'privacy:metadata:userpref:lessonview');
return $collection;
}
/**
* Get the list of contexts that contain user information for the specified user.
*
* @param int $userid The user to search.
* @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
*/
public static function get_contexts_for_userid(int $userid): \core_privacy\local\request\contextlist {
$contextlist = new \core_privacy\local\request\contextlist();
$sql = "
SELECT DISTINCT ctx.id
FROM {lesson} l
JOIN {modules} m
ON m.name = :lesson
JOIN {course_modules} cm
ON cm.instance = l.id
AND cm.module = m.id
JOIN {context} ctx
ON ctx.instanceid = cm.id
AND ctx.contextlevel = :modulelevel
LEFT JOIN {lesson_attempts} la
ON la.lessonid = l.id
AND la.userid = :userid1
LEFT JOIN {lesson_branch} lb
ON lb.lessonid = l.id
AND lb.userid = :userid2
LEFT JOIN {lesson_grades} lg
ON lg.lessonid = l.id
AND lg.userid = :userid3
LEFT JOIN {lesson_overrides} lo
ON lo.lessonid = l.id
AND lo.userid = :userid4
LEFT JOIN {lesson_timer} lt
ON lt.lessonid = l.id
AND lt.userid = :userid5
WHERE la.id IS NOT NULL
OR lb.id IS NOT NULL
OR lg.id IS NOT NULL
OR lo.id IS NOT NULL
OR lt.id IS NOT NULL";
$params = [
'lesson' => 'lesson',
'modulelevel' => CONTEXT_MODULE,
'userid1' => $userid,
'userid2' => $userid,
'userid3' => $userid,
'userid4' => $userid,
'userid5' => $userid,
];
$contextlist->add_from_sql($sql, $params);
return $contextlist;
}
/**
* Get the list of users who have data within a context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*
*/
public static function get_users_in_context(userlist $userlist) {
$context = $userlist->get_context();
if (!is_a($context, \context_module::class)) {
return;
}
$params = [
'lesson' => 'lesson',
'modulelevel' => CONTEXT_MODULE,
'contextid' => $context->id,
];
// Mapping of lesson tables which may contain user data.
$joins = [
'lesson_attempts',
'lesson_branch',
'lesson_grades',
'lesson_overrides',
'lesson_timer',
];
foreach ($joins as $join) {
$sql = "
SELECT lx.userid
FROM {lesson} l
JOIN {modules} m
ON m.name = :lesson
JOIN {course_modules} cm
ON cm.instance = l.id
AND cm.module = m.id
JOIN {context} ctx
ON ctx.instanceid = cm.id
AND ctx.contextlevel = :modulelevel
JOIN {{$join}} lx
ON lx.lessonid = l.id
WHERE ctx.id = :contextid";
$userlist->add_from_sql('userid', $sql, $params);
}
}
/**
* Export all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts to export information for.
*/
public static function export_user_data(approved_contextlist $contextlist) {
global $DB;
$user = $contextlist->get_user();
$userid = $user->id;
$cmids = array_reduce($contextlist->get_contexts(), function($carry, $context) {
if ($context->contextlevel == CONTEXT_MODULE) {
$carry[] = $context->instanceid;
}
return $carry;
}, []);
if (empty($cmids)) {
return;
}
// If the context export was requested, then let's at least describe the lesson.
foreach ($cmids as $cmid) {
$context = context_module::instance($cmid);
$contextdata = helper::get_context_data($context, $user);
helper::export_context_files($context, $user);
writer::with_context($context)->export_data([], $contextdata);
}
// Find the lesson IDs.
$lessonidstocmids = static::get_lesson_ids_to_cmids_from_cmids($cmids);
// Prepare the common SQL fragments.
list($inlessonsql, $inlessonparams) = $DB->get_in_or_equal(array_keys($lessonidstocmids), SQL_PARAMS_NAMED);
$sqluserlesson = "userid = :userid AND lessonid $inlessonsql";
$paramsuserlesson = array_merge($inlessonparams, ['userid' => $userid]);
// Export the overrides.
$recordset = $DB->get_recordset_select('lesson_overrides', $sqluserlesson, $paramsuserlesson);
static::recordset_loop_and_export($recordset, 'lessonid', null, function($carry, $record) {
// We know that there is only one row per lesson, so no need to use $carry.
return (object) [
'available' => $record->available !== null ? transform::datetime($record->available) : null,
'deadline' => $record->deadline !== null ? transform::datetime($record->deadline) : null,
'timelimit' => $record->timelimit !== null ? format_time($record->timelimit) : null,
'review' => $record->review !== null ? transform::yesno($record->review) : null,
'maxattempts' => $record->maxattempts,
'retake' => $record->retake !== null ? transform::yesno($record->retake) : null,
'password' => $record->password,
];
}, function($lessonid, $data) use ($lessonidstocmids) {
$context = context_module::instance($lessonidstocmids[$lessonid]);
writer::with_context($context)->export_related_data([], 'overrides', $data);
});
// Export the grades.
$recordset = $DB->get_recordset_select('lesson_grades', $sqluserlesson, $paramsuserlesson, 'lessonid, completed');
static::recordset_loop_and_export($recordset, 'lessonid', [], function($carry, $record) {
$carry[] = (object) [
'grade' => $record->grade,
'completed' => transform::datetime($record->completed),
];
return $carry;
}, function($lessonid, $data) use ($lessonidstocmids) {
$context = context_module::instance($lessonidstocmids[$lessonid]);
writer::with_context($context)->export_related_data([], 'grades', (object) ['grades' => $data]);
});
// Export the timers.
$recordset = $DB->get_recordset_select('lesson_timer', $sqluserlesson, $paramsuserlesson, 'lessonid, starttime');
static::recordset_loop_and_export($recordset, 'lessonid', [], function($carry, $record) {
$carry[] = (object) [
'starttime' => transform::datetime($record->starttime),
'lastactivity' => transform::datetime($record->lessontime),
'completed' => transform::yesno($record->completed),
'timemodifiedoffline' => $record->timemodifiedoffline ? transform::datetime($record->timemodifiedoffline) : null,
];
return $carry;
}, function($lessonid, $data) use ($lessonidstocmids) {
$context = context_module::instance($lessonidstocmids[$lessonid]);
writer::with_context($context)->export_related_data([], 'timers', (object) ['timers' => $data]);
});
// Export the attempts and branches.
$sql = "
SELECT " . $DB->sql_concat('lp.id', "':'", 'COALESCE(la.id, 0)', "':'", 'COALESCE(lb.id, 0)') . " AS uniqid,
lp.lessonid,
lp.id AS page_id,
lp.qtype AS page_qtype,
lp.qoption AS page_qoption,
lp.title AS page_title,
lp.contents AS page_contents,
lp.contentsformat AS page_contentsformat,
la.id AS attempt_id,
la.retry AS attempt_retry,
la.correct AS attempt_correct,
la.useranswer AS attempt_useranswer,
la.timeseen AS attempt_timeseen,
lb.id AS branch_id,
lb.retry AS branch_retry,
lb.timeseen AS branch_timeseen,
lpb.id AS nextpage_id,
lpb.title AS nextpage_title
FROM {lesson_pages} lp
LEFT JOIN {lesson_attempts} la
ON la.pageid = lp.id
AND la.userid = :userid1
LEFT JOIN {lesson_branch} lb
ON lb.pageid = lp.id
AND lb.userid = :userid2
LEFT JOIN {lesson_pages} lpb
ON lpb.id = lb.nextpageid
WHERE lp.lessonid $inlessonsql
AND (la.id IS NOT NULL OR lb.id IS NOT NULL)
ORDER BY lp.lessonid, lp.id, la.retry, lb.retry, la.id, lb.id";
$params = array_merge($inlessonparams, ['userid1' => $userid, 'userid2' => $userid]);
$recordset = $DB->get_recordset_sql($sql, $params);
static::recordset_loop_and_export($recordset, 'lessonid', [], function($carry, $record) use ($lessonidstocmids) {
$context = context_module::instance($lessonidstocmids[$record->lessonid]);
$options = ['context' => $context];
$take = isset($record->attempt_retry) ? $record->attempt_retry : $record->branch_retry;
if (!isset($carry[$take])) {
$carry[$take] = (object) [
'number' => $take + 1,
'answers' => [],
'jumps' => []
];
}
$pagefilespath = [get_string('privacy:path:pages', 'mod_lesson'), $record->page_id];
writer::with_context($context)->export_area_files($pagefilespath, 'mod_lesson', 'page_contents', $record->page_id);
$pagecontents = format_text(
writer::with_context($context)->rewrite_pluginfile_urls(
$pagefilespath,
'mod_lesson',
'page_contents',
$record->page_id,
$record->page_contents
),
$record->page_contentsformat,
$options
);
$pagebase = [
'id' => $record->page_id,
'page' => $record->page_title,
'contents' => $pagecontents,
'contents_files_folder' => implode('/', $pagefilespath)
];
if (isset($record->attempt_id)) {
$carry[$take]->answers[] = array_merge($pagebase, static::transform_attempt($record, $context));
} else if (isset($record->branch_id)) {
if (!empty($record->nextpage_id)) {
$wentto = $record->nextpage_title . " (id: {$record->nextpage_id})";
} else {
$wentto = get_string('endoflesson', 'mod_lesson');
}
$carry[$take]->jumps[] = array_merge($pagebase, [
'went_to' => $wentto,
'timeseen' => transform::datetime($record->attempt_timeseen)
]);
}
return $carry;
}, function($lessonid, $data) use ($lessonidstocmids) {
$context = context_module::instance($lessonidstocmids[$lessonid]);
writer::with_context($context)->export_related_data([], 'attempts', (object) [
'attempts' => array_values($data)
]);
});
}
/**
* Export all user preferences for the plugin.
*
* @param int $userid The userid of the user whose data is to be exported.
*/
public static function export_user_preferences(int $userid) {
$lessonview = get_user_preferences('lesson_view', null, $userid);
if ($lessonview !== null) {
$value = $lessonview;
// The code seems to indicate that there also is the option 'simple', but it's not
// described nor accessible from anywhere so we won't describe it more than being 'simple'.
if ($lessonview == 'full') {
$value = get_string('full', 'mod_lesson');
} else if ($lessonview == 'collapsed') {
$value = get_string('collapsed', 'mod_lesson');
}
writer::export_user_preference('mod_lesson', 'lesson_view', $lessonview,
get_string('privacy:metadata:userpref:lessonview', 'mod_lesson'));
}
}
/**
* Delete all data for all users in the specified context.
*
* @param context $context The specific context to delete data for.
*/
public static function delete_data_for_all_users_in_context(context $context) {
global $DB;
if ($context->contextlevel != CONTEXT_MODULE) {
return;
}
if (!$lessonid = static::get_lesson_id_from_context($context)) {
return;
}
$DB->delete_records('lesson_attempts', ['lessonid' => $lessonid]);
$DB->delete_records('lesson_branch', ['lessonid' => $lessonid]);
$DB->delete_records('lesson_grades', ['lessonid' => $lessonid]);
$DB->delete_records('lesson_timer', ['lessonid' => $lessonid]);
$DB->delete_records_select('lesson_overrides', 'lessonid = :id AND userid IS NOT NULL', ['id' => $lessonid]);
$fs = get_file_storage();
$fs->delete_area_files($context->id, 'mod_lesson', 'essay_responses');
$fs->delete_area_files($context->id, 'mod_lesson', 'essay_answers');
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
global $DB;
$userid = $contextlist->get_user()->id;
$cmids = array_reduce($contextlist->get_contexts(), function($carry, $context) {
if ($context->contextlevel == CONTEXT_MODULE) {
$carry[] = $context->instanceid;
}
return $carry;
}, []);
if (empty($cmids)) {
return;
}
// Find the lesson IDs.
$lessonidstocmids = static::get_lesson_ids_to_cmids_from_cmids($cmids);
$lessonids = array_keys($lessonidstocmids);
if (empty($lessonids)) {
return;
}
// Prepare the SQL we'll need below.
list($insql, $inparams) = $DB->get_in_or_equal($lessonids, SQL_PARAMS_NAMED);
$sql = "lessonid $insql AND userid = :userid";
$params = array_merge($inparams, ['userid' => $userid]);
// Delete the attempt files.
$fs = get_file_storage();
$recordset = $DB->get_recordset_select('lesson_attempts', $sql, $params, '', 'id, lessonid');
foreach ($recordset as $record) {
$cmid = $lessonidstocmids[$record->lessonid];
$context = context_module::instance($cmid);
$fs->delete_area_files($context->id, 'mod_lesson', 'essay_responses', $record->id);
$fs->delete_area_files($context->id, 'mod_lesson', 'essay_answers', $record->id);
}
$recordset->close();
// Delete all the things.
$DB->delete_records_select('lesson_attempts', $sql, $params);
$DB->delete_records_select('lesson_branch', $sql, $params);
$DB->delete_records_select('lesson_grades', $sql, $params);
$DB->delete_records_select('lesson_timer', $sql, $params);
$DB->delete_records_select('lesson_overrides', $sql, $params);
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
global $DB;
$context = $userlist->get_context();
$lessonid = static::get_lesson_id_from_context($context);
$userids = $userlist->get_userids();
if (empty($lessonid)) {
return;
}
// Prepare the SQL we'll need below.
list($insql, $inparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
$sql = "lessonid = :lessonid AND userid {$insql}";
$params = array_merge($inparams, ['lessonid' => $lessonid]);
// Delete the attempt files.
$fs = get_file_storage();
$recordset = $DB->get_recordset_select('lesson_attempts', $sql, $params, '', 'id, lessonid');
foreach ($recordset as $record) {
$fs->delete_area_files($context->id, 'mod_lesson', 'essay_responses', $record->id);
$fs->delete_area_files($context->id, 'mod_lesson', 'essay_answers', $record->id);
}
$recordset->close();
// Delete all the things.
$DB->delete_records_select('lesson_attempts', $sql, $params);
$DB->delete_records_select('lesson_branch', $sql, $params);
$DB->delete_records_select('lesson_grades', $sql, $params);
$DB->delete_records_select('lesson_timer', $sql, $params);
$DB->delete_records_select('lesson_overrides', $sql, $params);
}
/**
* Get a survey ID from its context.
*
* @param context_module $context The module context.
* @return int
*/
protected static function get_lesson_id_from_context(context_module $context) {
$cm = get_coursemodule_from_id('lesson', $context->instanceid);
return $cm ? (int) $cm->instance : 0;
}
/**
* Return a dict of lesson IDs mapped to their course module ID.
*
* @param array $cmids The course module IDs.
* @return array In the form of [$lessonid => $cmid].
*/
protected static function get_lesson_ids_to_cmids_from_cmids(array $cmids) {
global $DB;
list($insql, $inparams) = $DB->get_in_or_equal($cmids, SQL_PARAMS_NAMED);
$sql = "
SELECT l.id, cm.id AS cmid
FROM {lesson} l
JOIN {modules} m
ON m.name = :lesson
JOIN {course_modules} cm
ON cm.instance = l.id
AND cm.module = m.id
WHERE cm.id $insql";
$params = array_merge($inparams, ['lesson' => 'lesson']);
return $DB->get_records_sql_menu($sql, $params);
}
/**
* Loop and export from a recordset.
*
* @param moodle_recordset $recordset The recordset.
* @param string $splitkey The record key to determine when to export.
* @param mixed $initial The initial data to reduce from.
* @param callable $reducer The function to return the dataset, receives current dataset, and the current record.
* @param callable $export The function to export the dataset, receives the last value from $splitkey and the dataset.
* @return void
*/
protected static function recordset_loop_and_export(\moodle_recordset $recordset, $splitkey, $initial,
callable $reducer, callable $export) {
$data = $initial;
$lastid = null;
foreach ($recordset as $record) {
if ($lastid && $record->{$splitkey} != $lastid) {
$export($lastid, $data);
$data = $initial;
}
$data = $reducer($data, $record);
$lastid = $record->{$splitkey};
}
$recordset->close();
if (!empty($lastid)) {
$export($lastid, $data);
}
}
/**
* Transform an attempt.
*
* @param stdClass $data Data from the database, as per the exporting method.
* @param context_module $context The module context.
* @return array
*/
protected static function transform_attempt(stdClass $data, context_module $context) {
global $DB;
$options = ['context' => $context];
$answer = $data->attempt_useranswer;
$response = null;
$responsefilesfolder = null;
if ($answer !== null) {
if ($data->page_qtype == LESSON_PAGE_ESSAY) {
// Essay questions serialise data in the answer field.
$info = \lesson_page_type_essay::extract_useranswer($answer);
$answerfilespath = [get_string('privacy:path:essayanswers', 'mod_lesson'), $data->attempt_id];
$answer = format_text(
writer::with_context($context)->rewrite_pluginfile_urls(
$answerfilespath,
'mod_lesson',
'essay_answers',
$data->attempt_id,
$info->answer
),
$info->answerformat,
$options
);
writer::with_context($context)->export_area_files($answerfilespath, 'mod_lesson',
'essay_answers', $data->page_id);
if ($info->response !== null) {
// We export the files in a subfolder to avoid conflicting files, and tell the user
// where those files were exported. That is because we are not using a subfolder for
// every single essay response.
$responsefilespath = [get_string('privacy:path:essayresponses', 'mod_lesson'), $data->attempt_id];
$responsefilesfolder = implode('/', $responsefilespath);
$response = format_text(
writer::with_context($context)->rewrite_pluginfile_urls(
$responsefilespath,
'mod_lesson',
'essay_responses',
$data->attempt_id,
$info->response
),
$info->responseformat,
$options
);
writer::with_context($context)->export_area_files($responsefilespath, 'mod_lesson',
'essay_responses', $data->page_id);
}
} else if ($data->page_qtype == LESSON_PAGE_MULTICHOICE && $data->page_qoption) {
// Multiple choice quesitons with multiple answers encode the answers.
list($insql, $inparams) = $DB->get_in_or_equal(explode(',', $answer), SQL_PARAMS_NAMED);
$orderby = 'id, ' . $DB->sql_order_by_text('answer') . ', answerformat';
$records = $DB->get_records_select('lesson_answers', "id $insql", $inparams, $orderby);
$answer = array_values(array_map(function($record) use ($options) {
return format_text($record->answer, $record->answerformat, $options);
}, empty($records) ? [] : $records));
} else if ($data->page_qtype == LESSON_PAGE_MATCHING) {
// Matching questions need sorting.
$chosen = explode(',', $answer);
$answers = $DB->get_records_select('lesson_answers', 'pageid = :pageid', ['pageid' => $data->page_id],
'id', 'id, answer, answerformat', 2); // The two first entries are not options.
$i = -1;
$answer = array_values(array_map(function($record) use (&$i, $chosen, $options) {
$i++;
return [
'label' => format_text($record->answer, $record->answerformat, $options),
'matched_with' => array_key_exists($i, $chosen) ? $chosen[$i] : null
];
}, empty($answers) ? [] : $answers));
}
}
$result = [
'answer' => $answer,
'correct' => transform::yesno($data->attempt_correct),
'timeseen' => transform::datetime($data->attempt_timeseen),
];
if ($response !== null) {
$result['response'] = $response;
$result['response_files_folder'] = $responsefilesfolder;
}
return $result;
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
// 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/>.
/**
* Search area for mod_lesson activities.
*
* @package mod_lesson
* @copyright 2015 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\search;
defined('MOODLE_INTERNAL') || die();
/**
* Search area for mod_lesson activities.
*
* @package mod_lesson
* @copyright 2015 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class activity extends \core_search\base_activity {
/**
* Returns true if this area uses file indexing.
*
* @return bool
*/
public function uses_file_indexing() {
return true;
}
/**
* Return the context info required to index files for
* this search area.
*
* @return array
*/
public function get_search_fileareas() {
$fileareas = array('intro', 'page_contents'); // Fileareas.
return $fileareas;
}
}
+130
View File
@@ -0,0 +1,130 @@
<?php
// 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/>.
/**
* Action for processing page answers by users
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
/** Require the specific libraries */
require_once("../../config.php");
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
$id = required_param('id', PARAM_INT);
$cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST), $cm, $course);
require_login($course, false, $cm);
require_sesskey();
// Apply overrides.
$lesson->update_effective_access($USER->id);
$context = $lesson->context;
$canmanage = $lesson->can_manage();
$lessonoutput = $PAGE->get_renderer('mod_lesson');
$url = new moodle_url('/mod/lesson/continue.php', array('id'=>$cm->id));
$PAGE->set_url($url);
$PAGE->set_pagetype('mod-lesson-view');
$PAGE->navbar->add(get_string('continue', 'lesson'));
// This is the code updates the lesson time for a timed test
// get time information for this user
if (!$canmanage) {
$lesson->displayleft = lesson_displayleftif($lesson);
$timer = $lesson->update_timer();
if (!$lesson->check_time($timer)) {
redirect(new moodle_url('/mod/lesson/view.php', array('id' => $cm->id, 'pageid' => LESSON_EOL, 'outoftime' => 'normal')));
die; // Shouldn't be reached, but make sure.
}
} else {
$timer = new stdClass;
}
// record answer (if necessary) and show response (if none say if answer is correct or not)
$page = $lesson->load_page(required_param('pageid', PARAM_INT));
$reviewmode = $lesson->is_in_review_mode();
// Process the page responses.
$result = $lesson->process_page_responses($page);
if ($result->nodefaultresponse || $result->inmediatejump) {
// Don't display feedback or force a redirecto to newpageid.
redirect(new moodle_url('/mod/lesson/view.php', array('id'=>$cm->id,'pageid'=>$result->newpageid)));
}
// Set Messages.
$lesson->add_messages_on_page_process($page, $result, $reviewmode);
$PAGE->set_secondary_active_tab('modulepage');
$PAGE->set_url('/mod/lesson/view.php', array('id' => $cm->id, 'pageid' => $page->id));
$PAGE->set_subpage($page->id);
/// Print the header, heading and tabs
lesson_add_fake_blocks($PAGE, $cm, $lesson, $timer);
echo $lessonoutput->header($lesson, $cm, 'view', true, $page->id, get_string('continue', 'lesson'));
$editbuttons = new \mod_lesson\output\edit_action_buttons($lesson, $page->id ?? null);
echo $lessonoutput->render($editbuttons);
if ($lesson->displayleft) {
echo '<a name="maincontent" id="maincontent" title="'.get_string('anchortitle', 'lesson').'"></a>';
}
// This calculates and prints the ongoing score message
if ($lesson->ongoing && !$reviewmode) {
echo $lessonoutput->ongoing_score($lesson);
}
if (!$reviewmode) {
echo format_text($result->feedback, FORMAT_MOODLE, array('context' => $context, 'noclean' => true));
}
// User is modifying attempts - save button and some instructions
if (isset($USER->modattempts[$lesson->id])) {
$content = $OUTPUT->box(get_string("gotoendoflesson", "lesson"), 'center');
$content .= $OUTPUT->box(get_string("or", "lesson"), 'center');
$content .= $OUTPUT->box(get_string("continuetonextpage", "lesson"), 'center');
$url = new moodle_url('/mod/lesson/view.php', array('id' => $cm->id, 'pageid' => LESSON_EOL));
echo $content . $OUTPUT->single_button($url, get_string('finish', 'lesson'));
}
// Review button back
if (!$result->correctanswer && !$result->noanswer && !$result->isessayquestion && !$reviewmode && $lesson->review && !$result->maxattemptsreached) {
$url = new moodle_url('/mod/lesson/view.php', array('id' => $cm->id, 'pageid' => $page->id));
echo $OUTPUT->single_button($url, get_string('reviewquestionback', 'lesson'));
}
$url = new moodle_url('/mod/lesson/view.php', array('id'=>$cm->id, 'pageid'=>$result->newpageid));
if ($lesson->review && !$result->correctanswer && !$result->noanswer && !$result->isessayquestion && !$result->maxattemptsreached) {
// If both the "Yes, I'd like to try again" and "No, I just want to go on to the next question" point to the same
// page then don't show the "No, I just want to go on to the next question" button. It's confusing.
if ($page->id != $result->newpageid) {
// Button to continue the lesson (the page to go is configured by the teacher).
echo $OUTPUT->single_button($url, get_string('reviewquestioncontinue', 'lesson'));
}
} else {
// Normal continue button
echo $OUTPUT->single_button($url, get_string('continue', 'lesson'));
}
echo $lessonoutput->footer();
+108
View File
@@ -0,0 +1,108 @@
<?php
// 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/>.
/**
* Capability definitions for the lesson module.
*
* For naming conventions, see lib/db/access.php.
*
* @package mod_lesson
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or late
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = array(
'mod/lesson:addinstance' => array(
'riskbitmask' => RISK_XSS,
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/course:manageactivities'
),
'mod/lesson:edit' => array(
'riskbitmask' => RISK_XSS, // we want flash and JS in question pages
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
// Grade essay questions.
'mod/lesson:grade' => array(
'riskbitmask' => RISK_SPAM | RISK_XSS,
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
// View the lesson reports.
'mod/lesson:viewreports' => array(
'riskbitmask' => RISK_PERSONAL,
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
'mod/lesson:manage' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
// Edit the lesson overrides.
'mod/lesson:manageoverrides' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
'mod/lesson:view' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'user' => CAP_ALLOW,
'guest' => CAP_ALLOW
)
),
);
+35
View File
@@ -0,0 +1,35 @@
<?php
// 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/>.
/**
* Defined caches used internally by the plugin.
*
* @package mod_lesson
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
defined('MOODLE_INTERNAL') || die();
$definitions = [
'overrides' => [
'mode' => cache_store::MODE_APPLICATION,
'simplekeys' => true,
'datasource' => '\mod_lesson\cache\overrides',
],
];
+43
View File
@@ -0,0 +1,43 @@
<?php
// 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/>.
/**
* Add event handlers for the lesson
*
* @package mod_lesson
* @category event
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$observers = array(
array(
'eventname' => '\core\event\course_reset_started',
'callback' => '\mod_lesson\group_observers::course_reset_started',
),
array(
'eventname' => '\core\event\course_reset_ended',
'callback' => '\mod_lesson\group_observers::course_reset_ended',
),
array(
'eventname' => '\core\event\group_deleted',
'callback' => '\mod_lesson\group_observers::group_deleted'
),
);
+201
View File
@@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="mod/lesson/db" VERSION="20150720" COMMENT="XMLDB file for Moodle mod/lesson"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="lesson" COMMENT="Defines lesson">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="intro" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="introformat" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="practice" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="modattempts" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="usepassword" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="password" TYPE="char" LENGTH="32" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="dependency" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="conditions" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="grade" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="custom" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="ongoing" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="usemaxgrade" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="maxanswers" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="4" SEQUENCE="false"/>
<FIELD NAME="maxattempts" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="5" SEQUENCE="false"/>
<FIELD NAME="review" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="nextpagedefault" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="feedback" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
<FIELD NAME="minquestions" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="maxpages" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timelimit" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="retake" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
<FIELD NAME="activitylink" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="mediafile" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="Local file path or full external URL"/>
<FIELD NAME="mediaheight" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="100" SEQUENCE="false"/>
<FIELD NAME="mediawidth" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="650" SEQUENCE="false"/>
<FIELD NAME="mediaclose" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="slideshow" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="width" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="640" SEQUENCE="false"/>
<FIELD NAME="height" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="480" SEQUENCE="false"/>
<FIELD NAME="bgcolor" TYPE="char" LENGTH="7" NOTNULL="true" DEFAULT="#FFFFFF" SEQUENCE="false"/>
<FIELD NAME="displayleft" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="displayleftif" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="progressbar" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="available" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="deadline" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="completionendreached" TYPE="int" LENGTH="1" NOTNULL="false" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="completiontimespent" TYPE="int" LENGTH="11" NOTNULL="false" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="allowofflineattempts" TYPE="int" LENGTH="1" NOTNULL="false" DEFAULT="0" SEQUENCE="false" COMMENT="Whether to allow the lesson to be attempted offline in the mobile app"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="course" UNIQUE="false" FIELDS="course"/>
</INDEXES>
</TABLE>
<TABLE NAME="lesson_pages" COMMENT="Defines lesson_pages">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="lessonid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="prevpageid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="nextpageid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="qtype" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="qoption" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="layout" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
<FIELD NAME="display" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="title" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="contents" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="contentsformat" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="lessonid" TYPE="foreign" FIELDS="lessonid" REFTABLE="lesson" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="lesson_answers" COMMENT="Defines lesson_answers">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="lessonid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="pageid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="jumpto" TYPE="int" LENGTH="11" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="grade" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="score" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="flags" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="answer" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="answerformat" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="response" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="responseformat" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="lessonid" TYPE="foreign" FIELDS="lessonid" REFTABLE="lesson" REFFIELDS="id"/>
<KEY NAME="pageid" TYPE="foreign" FIELDS="pageid" REFTABLE="lesson_pages" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="lesson_attempts" COMMENT="Defines lesson_attempts">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="lessonid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="pageid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="answerid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="retry" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="correct" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="useranswer" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="timeseen" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="lessonid" TYPE="foreign" FIELDS="lessonid" REFTABLE="lesson" REFFIELDS="id"/>
<KEY NAME="pageid" TYPE="foreign" FIELDS="pageid" REFTABLE="lesson_pages" REFFIELDS="id"/>
<KEY NAME="answerid" TYPE="foreign" FIELDS="answerid" REFTABLE="lesson_answers" REFFIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="userid" UNIQUE="false" FIELDS="userid"/>
</INDEXES>
</TABLE>
<TABLE NAME="lesson_grades" COMMENT="Defines lesson_grades">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="lessonid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="grade" TYPE="float" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="late" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="completed" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="lessonid" TYPE="foreign" FIELDS="lessonid" REFTABLE="lesson" REFFIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="userid" UNIQUE="false" FIELDS="userid"/>
</INDEXES>
</TABLE>
<TABLE NAME="lesson_timer" COMMENT="lesson timer for each lesson">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="lessonid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="starttime" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="lessontime" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="completed" TYPE="int" LENGTH="1" NOTNULL="false" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timemodifiedoffline" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Last modified time via web services (mobile app)."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="lessonid" TYPE="foreign" FIELDS="lessonid" REFTABLE="lesson" REFFIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="userid" UNIQUE="false" FIELDS="userid"/>
</INDEXES>
</TABLE>
<TABLE NAME="lesson_branch" COMMENT="branches for each lesson/user">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="lessonid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="pageid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="retry" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="flag" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timeseen" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="nextpageid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="lessonid" TYPE="foreign" FIELDS="lessonid" REFTABLE="lesson" REFFIELDS="id"/>
<KEY NAME="pageid" TYPE="foreign" FIELDS="pageid" REFTABLE="lesson_pages" REFFIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="userid" UNIQUE="false" FIELDS="userid"/>
</INDEXES>
</TABLE>
<TABLE NAME="lesson_overrides" COMMENT="The overrides to lesson settings.">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="lessonid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Foreign key references lesson.id"/>
<FIELD NAME="groupid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Foreign key references groups.id. Can be null if this is a per-user override."/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Foreign key references user.id. Can be null if this is a per-group override."/>
<FIELD NAME="available" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time at which students may start attempting this lesson. Can be null, in which case the lesson default is used."/>
<FIELD NAME="deadline" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time by which students must have completed their attempt. Can be null, in which case the lesson default is used."/>
<FIELD NAME="timelimit" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time limit in seconds. Can be null, in which case the lesson default is used."/>
<FIELD NAME="review" TYPE="int" LENGTH="3" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="maxattempts" TYPE="int" LENGTH="3" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="retake" TYPE="int" LENGTH="3" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="password" TYPE="char" LENGTH="32" NOTNULL="false" SEQUENCE="false" COMMENT="Lesson password. Can be null, in which case the lesson default is used."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="lessonid" TYPE="foreign" FIELDS="lessonid" REFTABLE="lesson" REFFIELDS="id"/>
<KEY NAME="groupid" TYPE="foreign" FIELDS="groupid" REFTABLE="groups" REFFIELDS="id"/>
<KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
+33
View File
@@ -0,0 +1,33 @@
<?php
// 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/>.
/**
* Definition of log events
*
* @package mod_lesson
* @category log
* @copyright 2010 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$logs = array(
array('module'=>'lesson', 'action'=>'start', 'mtable'=>'lesson', 'field'=>'name'),
array('module'=>'lesson', 'action'=>'end', 'mtable'=>'lesson', 'field'=>'name'),
array('module'=>'lesson', 'action'=>'view', 'mtable'=>'lesson_pages', 'field'=>'title'),
);
+37
View File
@@ -0,0 +1,37 @@
<?php
// 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/>.
/**
* Defines message providers (types of messages being sent)
*
* @package mod_lesson
* @copyright 2010 Andrew Davis
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$messageproviders = array (
// Essay graded notification.
'graded_essay' => array (
'defaults' => [
'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED,
'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED,
'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED,
],
)
);
+167
View File
@@ -0,0 +1,167 @@
<?php
// 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/>.
/**
* Lesson external functions and service definitions.
*
* @package mod_lesson
* @category external
* @copyright 2017 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 3.3
*/
defined('MOODLE_INTERNAL') || die;
$functions = array(
'mod_lesson_get_lessons_by_courses' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'get_lessons_by_courses',
'description' => 'Returns a list of lessons in a provided list of courses,
if no list is provided all lessons that the user can view will be returned.',
'type' => 'read',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'mod_lesson_get_lesson_access_information' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'get_lesson_access_information',
'description' => 'Return access information for a given lesson.',
'type' => 'read',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'mod_lesson_view_lesson' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'view_lesson',
'description' => 'Trigger the course module viewed event and update the module completion status.',
'type' => 'write',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_lesson_get_questions_attempts' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'get_questions_attempts',
'description' => 'Return the list of questions attempts in a given lesson.',
'type' => 'read',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_lesson_get_user_grade' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'get_user_grade',
'description' => 'Return the final grade in the lesson for the given user.',
'type' => 'read',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_lesson_get_user_attempt_grade' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'get_user_attempt_grade',
'description' => 'Return grade information in the attempt for a given user.',
'type' => 'read',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_lesson_get_content_pages_viewed' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'get_content_pages_viewed',
'description' => 'Return the list of content pages viewed by a user during a lesson attempt.',
'type' => 'read',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_lesson_get_user_timers' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'get_user_timers',
'description' => 'Return the timers in the current lesson for the given user.',
'type' => 'read',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_lesson_get_pages' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'get_pages',
'description' => 'Return the list of pages in a lesson (based on the user permissions).',
'type' => 'read',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_lesson_launch_attempt' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'launch_attempt',
'description' => 'Starts a new attempt or continues an existing one.',
'type' => 'write',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_lesson_get_page_data' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'get_page_data',
'description' => 'Return information of a given page, including its contents.',
'type' => 'read',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_lesson_process_page' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'process_page',
'description' => 'Processes page responses.',
'type' => 'write',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_lesson_finish_attempt' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'finish_attempt',
'description' => 'Finishes the current attempt.',
'type' => 'write',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_lesson_get_attempts_overview' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'get_attempts_overview',
'description' => 'Get a list of all the attempts made by users in a lesson.',
'type' => 'read',
'capabilities' => 'mod/lesson:viewreports',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_lesson_get_user_attempt' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'get_user_attempt',
'description' => 'Return information about the given user attempt (including answers).',
'type' => 'read',
'capabilities' => 'mod/lesson:viewreports',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_lesson_get_pages_possible_jumps' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'get_pages_possible_jumps',
'description' => 'Return all the possible jumps for the pages in a given lesson.',
'type' => 'read',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_lesson_get_lesson' => array(
'classname' => 'mod_lesson_external',
'methodname' => 'get_lesson',
'description' => 'Return information of a given lesson.',
'type' => 'read',
'capabilities' => 'mod/lesson:view',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
);
+64
View File
@@ -0,0 +1,64 @@
<?php
// 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/>.
/**
* This file keeps track of upgrades to
* the lesson module
*
* Sometimes, changes between versions involve
* alterations to database structures and other
* major things that may break installations.
*
* The upgrade function in this file will attempt
* to perform all the necessary actions to upgrade
* your older installation to the current version.
*
* If there's something it cannot do itself, it
* will tell you what you need to do.
*
* The commands in here will all be database-neutral,
* using the methods of database_manager class
*
* Please do not forget to use upgrade_set_timeout()
* before any action that may take longer time to finish.
*
* @package mod_lesson
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 o
*/
/**
*
* @global stdClass $CFG
* @global moodle_database $DB
* @param int $oldversion
* @return bool
*/
function xmldb_lesson_upgrade($oldversion) {
// Automatically generated Moodle v4.1.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.2.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.3.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.4.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+38
View File
@@ -0,0 +1,38 @@
<?php
// 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/>.
/**
* List of deprecated mod_lesson functions.
*
* @package mod_lesson
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @deprecated since Moodle 3.11
*/
function lesson_get_completion_state() {
$completionclass = \mod_lesson\completion\custom_completion::class;
throw new coding_exception(__FUNCTION__ . "() has been removed, please use the '{$completionclass}' class instead");
}
/**
* @deprecated since Moodle 4.0
*/
function lesson_add_header_buttons() {
throw new coding_exception(__FUNCTION__ . '() has been removed');
}
+85
View File
@@ -0,0 +1,85 @@
<?php
// 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/>.
/**
* Provides the interface for overall authoring of lessons
*
* @package mod_lesson
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
require_once('../../config.php');
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
$id = required_param('id', PARAM_INT);
$cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST));
require_login($course, false, $cm);
$context = context_module::instance($cm->id);
require_capability('mod/lesson:manage', $context);
$mode = optional_param('mode', get_user_preferences('lesson_view', 'collapsed'), PARAM_ALPHA);
// Ensure a valid mode is set.
if (!in_array($mode, array('single', 'full', 'collapsed'))) {
$mode = 'collapsed';
}
$url = new moodle_url('/mod/lesson/edit.php', ['id' => $cm->id, 'mode' => $mode]);
$PAGE->set_url($url);
$PAGE->force_settings_menu();
$PAGE->set_secondary_active_tab('modulepage');
$PAGE->add_body_class('limitedwidth');
$PAGE->activityheader->set_description('');
if ($mode != get_user_preferences('lesson_view', 'collapsed') && $mode !== 'single') {
set_user_preference('lesson_view', $mode);
}
$lessonoutput = $PAGE->get_renderer('mod_lesson');
$PAGE->navbar->add(get_string('edit'));
echo $lessonoutput->header($lesson, $cm, $mode, false, null, get_string('edit', 'lesson'));
$actionarea = new \mod_lesson\output\edit_action_area($id, $url);
echo $lessonoutput->render($actionarea);
if (!$lesson->has_pages()) {
// There are no pages; give teacher some options
require_capability('mod/lesson:edit', $context);
echo $lessonoutput->add_first_page_links($lesson);
} else {
switch ($mode) {
case 'collapsed':
echo $lessonoutput->display_edit_collapsed($lesson, $lesson->firstpageid);
break;
case 'single':
$pageid = required_param('pageid', PARAM_INT);
$PAGE->url->param('pageid', $pageid);
$singlepage = $lesson->load_page($pageid);
echo $lessonoutput->display_edit_full($lesson, $singlepage->id, $singlepage->prevpageid, true);
break;
case 'full':
echo $lessonoutput->display_edit_full($lesson, $lesson->firstpageid, 0);
break;
}
}
echo $lessonoutput->footer();
+189
View File
@@ -0,0 +1,189 @@
<?php
// 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/>.
/**
* Action for adding a question page. Prints an HTML form.
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
require_once("../../config.php");
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
require_once('editpage_form.php');
// first get the preceeding page
$pageid = required_param('pageid', PARAM_INT);
$id = required_param('id', PARAM_INT); // Course Module ID
$qtype = optional_param('qtype', 0, PARAM_INT);
$edit = optional_param('edit', false, PARAM_BOOL);
$returnto = optional_param('returnto', null, PARAM_LOCALURL);
if (!empty($returnto)) {
$returnto = new moodle_url($returnto);
} else {
$returnto = new moodle_url('/mod/lesson/edit.php', array('id' => $id));
$returnto->set_anchor('lesson-' . $pageid);
}
$cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST));
require_login($course, false, $cm);
$context = context_module::instance($cm->id);
require_capability('mod/lesson:edit', $context);
$PAGE->set_url('/mod/lesson/editpage.php', array('pageid'=>$pageid, 'id'=>$id, 'qtype'=>$qtype));
$PAGE->set_pagelayout('admin');
$PAGE->set_secondary_active_tab('modulepage');
$PAGE->add_body_class('limitedwidth');
if ($edit) {
$editpage = lesson_page::load($pageid, $lesson);
$qtype = $editpage->qtype;
$edit = true;
} else {
$edit = false;
}
$jumpto = lesson_page::get_jumptooptions($pageid, $lesson);
$manager = lesson_page_type_manager::get($lesson);
$editoroptions = array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$CFG->maxbytes);
// If the previous page was the Question type selection form, this form
// will have a different name (e.g. _qf__lesson_add_page_form_selection
// versus _qf__lesson_add_page_form_multichoice). This causes confusion
// in moodleform::_process_submission because the array key check doesn't
// tie up with the current form name, which in turn means the "submitted"
// check ends up evaluating as false, thus it's not possible to check whether
// the Question type selection was cancelled. For this reason, a dummy form
// is created here solely to check whether the selection was cancelled.
if ($qtype) {
$mformdummy = $manager->get_page_form(0, array(
'editoroptions' => $editoroptions,
'jumpto' => $jumpto,
'lesson' => $lesson,
'edit' => $edit,
'maxbytes' => $PAGE->course->maxbytes,
'returnto' => $returnto
));
if ($mformdummy->is_cancelled()) {
redirect($returnto);
exit;
}
}
$mform = $manager->get_page_form($qtype, array(
'editoroptions' => $editoroptions,
'jumpto' => $jumpto,
'lesson' => $lesson,
'edit' => $edit,
'maxbytes' => $PAGE->course->maxbytes,
'returnto' => $returnto
));
if ($mform->is_cancelled()) {
redirect($returnto);
exit;
}
if ($edit) {
$data = $editpage->properties();
$data->pageid = $editpage->id;
$data->id = $cm->id;
$editoroptions['context'] = $context;
$data = file_prepare_standard_editor($data, 'contents', $editoroptions, $context, 'mod_lesson', 'page_contents', $editpage->id);
$answerscount = 0;
$answers = $editpage->get_answers();
foreach ($answers as $answer) {
$answereditor = 'answer_editor['.$answerscount.']';
if (is_array($data->$answereditor)) {
$answerdata = $data->$answereditor;
if ($mform->get_answer_format() === LESSON_ANSWER_HTML) {
$answerdraftid = file_get_submitted_draft_itemid($answereditor);
$answertext = file_prepare_draft_area($answerdraftid, $PAGE->cm->context->id,
'mod_lesson', 'page_answers', $answer->id, $editoroptions, $answerdata['text']);
$data->$answereditor = array('text' => $answertext, 'format' => $answerdata['format'], 'itemid' => $answerdraftid);
} else {
$data->$answereditor = $answerdata['text'];
}
}
$responseeditor = 'response_editor['.$answerscount.']';
if (is_array($data->$responseeditor)) {
$responsedata = $data->$responseeditor;
if ($mform->get_response_format() === LESSON_ANSWER_HTML) {
$responsedraftid = file_get_submitted_draft_itemid($responseeditor);
$responsetext = file_prepare_draft_area($responsedraftid, $PAGE->cm->context->id,
'mod_lesson', 'page_responses', $answer->id, $editoroptions, $responsedata['text']);
$data->$responseeditor = array('text' => $responsetext, 'format' => $responsedata['format'],
'itemid' => $responsedraftid);
} else {
$data->$responseeditor = $responsedata['text'];
}
}
$answerscount++;
}
// Let the lesson pages make updates if required.
$data = $editpage->update_form_data($data);
$mform->set_data($data);
$PAGE->navbar->add(get_string('edit'), new moodle_url('/mod/lesson/edit.php', array('id'=>$id)));
$PAGE->navbar->add(get_string('editingquestionpage', 'lesson', get_string($mform->qtypestring, 'lesson')));
} else {
// Give the page type being created a chance to override the creation process
// this is used by endofbranch, cluster, and endofcluster to skip the creation form.
// IT SHOULD ALWAYS CALL require_sesskey();
$mform->construction_override($pageid, $lesson);
$data = new stdClass;
$data->id = $cm->id;
$data->pageid = $pageid;
if ($qtype) {
//TODO: the handling of form for the selection of question type is a bloody hack! (skodak)
$data->qtype = $qtype;
}
$data = file_prepare_standard_editor($data, 'contents', $editoroptions, null);
$mform->set_data($data);
$PAGE->navbar->add(get_string('addanewpage', 'lesson'), $PAGE->url);
if ($qtype !== 'unknown') {
$PAGE->navbar->add(get_string($mform->qtypestring, 'lesson'));
}
}
if ($data = $mform->get_data()) {
require_sesskey();
if ($edit) {
$data->lessonid = $data->id;
$data->id = $data->pageid;
unset($data->pageid);
unset($data->edit);
$editpage->update($data, $context, $PAGE->course->maxbytes);
} else {
$editpage = lesson_page::create($data, $lesson, $context, $PAGE->course->maxbytes);
}
redirect($returnto);
}
$lessonoutput = $PAGE->get_renderer('mod_lesson');
echo $lessonoutput->header($lesson, $cm, '', false, null, get_string('edit', 'lesson'));
$mform->display();
echo $lessonoutput->footer();
+59
View File
@@ -0,0 +1,59 @@
<?php
// 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/>.
/**
* Generic forms used for page selection
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
defined('MOODLE_INTERNAL') || die();
/**
* Question selection form
*
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
class lesson_add_page_form_selection extends lesson_add_page_form_base {
public $qtype = 'questiontype';
public $qtypestring = 'selectaqtype';
protected $standard = false;
protected $manager = null;
public function __construct($arg1, $arg2) {
$this->manager = lesson_page_type_manager::get($arg2['lesson']);
parent::__construct($arg1, $arg2);
}
public function custom_definition() {
$mform = $this->_form;
$types = $this->manager->get_page_type_strings(lesson_page::TYPE_QUESTION);
asort($types);
$mform->addElement('select', 'qtype', get_string('selectaqtype', 'lesson'), $types);
$mform->setDefault('qtype', LESSON_PAGE_MULTICHOICE); // preselect the most common type
}
}
/**
* Dummy class to represent an unknown question type and direct to the selection
* form.
*/
final class lesson_add_page_form_unknown extends lesson_add_page_form_base {}
+496
View File
@@ -0,0 +1,496 @@
<?php
// 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/>.
/**
* Provides the interface for grading essay questions
*
* @package mod_lesson
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
require_once('../../config.php');
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
require_once($CFG->dirroot.'/mod/lesson/pagetypes/essay.php');
require_once($CFG->dirroot.'/mod/lesson/essay_form.php');
$id = required_param('id', PARAM_INT); // Course Module ID
$mode = optional_param('mode', 'display', PARAM_ALPHA);
$cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
$dblesson = $DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST);
$lesson = new lesson($dblesson);
require_login($course, false, $cm);
$context = context_module::instance($cm->id);
require_capability('mod/lesson:grade', $context);
$url = new moodle_url('/mod/lesson/essay.php', array('id'=>$id));
if ($mode !== 'display') {
$url->param('mode', $mode);
}
$PAGE->set_url($url);
$PAGE->set_secondary_active_tab('modulepage');
$currentgroup = groups_get_activity_group($cm, true);
$attempt = new stdClass();
$user = new stdClass();
$attemptid = optional_param('attemptid', 0, PARAM_INT);
$formattextdefoptions = new stdClass();
$formattextdefoptions->noclean = true;
$formattextdefoptions->para = false;
$formattextdefoptions->context = $context;
if ($attemptid > 0) {
$attempt = $DB->get_record('lesson_attempts', array('id' => $attemptid));
$answer = $DB->get_record('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $attempt->pageid));
$user = $DB->get_record('user', array('id' => $attempt->userid));
// Apply overrides.
$lesson->update_effective_access($user->id);
$scoreoptions = array();
if ($lesson->custom) {
$i = $answer->score;
while ($i >= 0) {
$scoreoptions[$i] = (string)$i;
$i--;
}
} else {
$scoreoptions[0] = get_string('nocredit', 'lesson');
$scoreoptions[1] = get_string('credit', 'lesson');
}
}
/// Handle any preprocessing before header is printed - based on $mode
switch ($mode) {
case 'grade':
// Grading form - get the necessary data
require_sesskey();
if (empty($attempt)) {
throw new \moodle_exception('cannotfindattempt', 'lesson');
}
if (empty($user)) {
throw new \moodle_exception('cannotfinduser', 'lesson');
}
if (empty($answer)) {
throw new \moodle_exception('cannotfindanswer', 'lesson');
}
break;
case 'update':
require_sesskey();
if (empty($attempt)) {
throw new \moodle_exception('cannotfindattempt', 'lesson');
}
if (empty($user)) {
throw new \moodle_exception('cannotfinduser', 'lesson');
}
$editoroptions = array('noclean' => true, 'maxfiles' => EDITOR_UNLIMITED_FILES,
'maxbytes' => $CFG->maxbytes, 'context' => $context);
$essayinfo = lesson_page_type_essay::extract_useranswer($attempt->useranswer);
$essayinfo = file_prepare_standard_editor($essayinfo, 'response', $editoroptions, $context,
'mod_lesson', 'essay_responses', $attempt->id);
$mform = new essay_grading_form(null, array('scoreoptions' => $scoreoptions, 'user' => $user));
$mform->set_data($essayinfo);
if ($mform->is_cancelled()) {
redirect("$CFG->wwwroot/mod/lesson/essay.php?id=$cm->id");
}
if ($form = $mform->get_data()) {
if (!$grades = $DB->get_records('lesson_grades', array("lessonid"=>$lesson->id, "userid"=>$attempt->userid), 'completed', '*', $attempt->retry, 1)) {
throw new \moodle_exception('cannotfindgrade', 'lesson');
}
$essayinfo->graded = 1;
$essayinfo->score = $form->score;
$form = file_postupdate_standard_editor($form, 'response', $editoroptions, $context,
'mod_lesson', 'essay_responses', $attempt->id);
$essayinfo->response = $form->response;
$essayinfo->responseformat = $form->responseformat;
$essayinfo->sent = 0;
if (!$lesson->custom && $essayinfo->score == 1) {
$attempt->correct = 1;
} else {
$attempt->correct = 0;
}
$attempt->useranswer = serialize($essayinfo);
$DB->update_record('lesson_attempts', $attempt);
// Get grade information
$grade = current($grades);
$gradeinfo = lesson_grade($lesson, $attempt->retry, $attempt->userid);
// Set and update
$updategrade = new stdClass();
$updategrade->id = $grade->id;
$updategrade->grade = $gradeinfo->grade;
$DB->update_record('lesson_grades', $updategrade);
$params = array(
'context' => $context,
'objectid' => $grade->id,
'courseid' => $course->id,
'relateduserid' => $attempt->userid,
'other' => array(
'lessonid' => $lesson->id,
'attemptid' => $attemptid
)
);
$event = \mod_lesson\event\essay_assessed::create($params);
$event->add_record_snapshot('lesson', $dblesson);
$event->trigger();
$lesson->add_message(get_string('changessaved'), 'notifysuccess');
// update central gradebook
lesson_update_grades($lesson, $grade->userid);
redirect(new moodle_url('/mod/lesson/essay.php', array('id'=>$cm->id)));
} else {
throw new \moodle_exception('invalidformdata');
}
break;
case 'email':
// Sending an email(s) to a single user or all
require_sesskey();
// Get our users (could be singular)
if ($userid = optional_param('userid', 0, PARAM_INT)) {
$queryadd = " AND userid = ?";
if (! $users = $DB->get_records('user', array('id' => $userid))) {
throw new \moodle_exception('cannotfinduser', 'lesson');
}
} else {
$queryadd = '';
// If group selected, only send to group members.
list($esql, $params) = get_enrolled_sql($context, '', $currentgroup, true);
list($sort, $sortparams) = users_order_by_sql('u');
$params['lessonid'] = $lesson->id;
// Need to use inner view to avoid distinct + text
if (!$users = $DB->get_records_sql("
SELECT u.*
FROM {user} u
JOIN (
SELECT DISTINCT userid
FROM {lesson_attempts}
WHERE lessonid = :lessonid
) ui ON u.id = ui.userid
JOIN ($esql) ue ON ue.id = u.id
ORDER BY $sort", $params)) {
throw new \moodle_exception('cannotfinduser', 'lesson');
}
}
$pages = $lesson->load_all_pages();
foreach ($pages as $key=>$page) {
if ($page->qtype != LESSON_PAGE_ESSAY) {
unset($pages[$key]);
}
}
// Get only the attempts that are in response to essay questions
list($usql, $params) = $DB->get_in_or_equal(array_keys($pages));
if (!empty($queryadd)) {
$params[] = $userid;
}
if (!$attempts = $DB->get_records_select('lesson_attempts', "pageid $usql".$queryadd, $params)) {
throw new \moodle_exception('nooneansweredthisquestion', 'lesson');
}
// Get the answers
list($answerUsql, $parameters) = $DB->get_in_or_equal(array_keys($pages));
array_unshift($parameters, $lesson->id);
if (!$answers = $DB->get_records_select('lesson_answers', "lessonid = ? AND pageid $answerUsql", $parameters, '', 'pageid, score')) {
throw new \moodle_exception('cannotfindanswer', 'lesson');
}
$userpicture = new user_picture($USER);
$userpicture->size = 1; // Use f1 size.
foreach ($attempts as $attempt) {
$essayinfo = lesson_page_type_essay::extract_useranswer($attempt->useranswer);
if ($essayinfo->graded && !$essayinfo->sent) {
// Holds values for the essayemailsubject string for the email message
$a = new stdClass;
// Set the grade
$grades = $DB->get_records('lesson_grades', array("lessonid"=>$lesson->id, "userid"=>$attempt->userid), 'completed', '*', $attempt->retry, 1);
$grade = current($grades);
$a->newgrade = $grade->grade;
// Set the points
if ($lesson->custom) {
$a->earned = $essayinfo->score;
$a->outof = $answers[$attempt->pageid]->score;
} else {
$a->earned = $essayinfo->score;
$a->outof = 1;
}
// Set rest of the message values
$currentpage = $lesson->load_page($attempt->pageid);
$a->question = format_text($currentpage->contents, $currentpage->contentsformat, $formattextdefoptions);
$answer = file_rewrite_pluginfile_urls($essayinfo->answer, 'pluginfile.php', $context->id,
'mod_lesson', 'essay_answers', $attempt->id);
$a->response = format_text($answer, $essayinfo->answerformat,
array('context' => $context, 'para' => true));
$a->comment = $essayinfo->response;
$a->comment = file_rewrite_pluginfile_urls($a->comment, 'pluginfile.php', $context->id,
'mod_lesson', 'essay_responses', $attempt->id);
$a->comment = format_text($a->comment, $essayinfo->responseformat, $formattextdefoptions);
$a->lesson = format_string($lesson->name, true);
// Fetch message HTML and plain text formats
$message = get_string('essayemailmessage2', 'lesson', $a);
$plaintext = format_text_email($message, FORMAT_HTML);
$smallmessage = get_string('essayemailmessagesmall', 'lesson', $a);
$smallmessage = format_text_email($smallmessage, FORMAT_HTML);
// Subject
$subject = get_string('essayemailsubject', 'lesson');
// Context url.
$contexturl = new moodle_url('/grade/report/user/index.php', array('id' => $course->id));
$eventdata = new \core\message\message();
$eventdata->courseid = $course->id;
$eventdata->modulename = 'lesson';
$eventdata->userfrom = $USER;
$eventdata->userto = $users[$attempt->userid];
$eventdata->subject = $subject;
$eventdata->fullmessage = $plaintext;
$eventdata->fullmessageformat = FORMAT_PLAIN;
$eventdata->fullmessagehtml = $message;
$eventdata->smallmessage = $smallmessage;
$eventdata->contexturl = $contexturl->out(false);
$userpicture->includetoken = $attempt->userid; // Generate an out-of-session token for the destinatary.
$eventdata->customdata = [
'cmid' => $cm->id,
'instance' => $lesson->id,
'retake' => $lesson->id,
'notificationiconurl' => $userpicture->get_url($PAGE)->out(false),
];
// Required for messaging framework
$eventdata->component = 'mod_lesson';
$eventdata->name = 'graded_essay';
message_send($eventdata);
$essayinfo->sent = 1;
$attempt->useranswer = serialize($essayinfo);
$DB->update_record('lesson_attempts', $attempt);
}
}
$lesson->add_message(get_string('emailsuccess', 'lesson'), 'notifysuccess');
redirect(new moodle_url('/mod/lesson/essay.php', array('id'=>$cm->id)));
break;
case 'display': // Default view - get the necessary data
default:
// Get lesson pages that are essay
$pages = $lesson->load_all_pages();
foreach ($pages as $key=>$page) {
if ($page->qtype != LESSON_PAGE_ESSAY) {
unset($pages[$key]);
}
}
if (count($pages) > 0) {
// Get only the attempts that are in response to essay questions
list($usql, $parameters) = $DB->get_in_or_equal(array_keys($pages), SQL_PARAMS_NAMED);
// If group selected, only get group members attempts.
list($esql, $params) = get_enrolled_sql($context, '', $currentgroup, true);
$parameters = array_merge($params, $parameters);
$sql = "SELECT a.*
FROM {lesson_attempts} a
JOIN ($esql) ue ON a.userid = ue.id
WHERE pageid $usql";
if ($essayattempts = $DB->get_records_sql($sql, $parameters)) {
$userfieldsapi = \core_user\fields::for_userpic();
$ufields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
// Get all the users who have taken this lesson.
list($sort, $sortparams) = users_order_by_sql('u');
$params['lessonid'] = $lesson->id;
$sql = "SELECT DISTINCT $ufields
FROM {user} u
JOIN {lesson_attempts} a ON u.id = a.userid
JOIN ($esql) ue ON ue.id = a.userid
WHERE a.lessonid = :lessonid
ORDER BY $sort";
if (!$users = $DB->get_records_sql($sql, $params)) {
$mode = 'none'; // not displaying anything
if (!empty($currentgroup)) {
$groupname = groups_get_group_name($currentgroup);
$lesson->add_message(get_string('noonehasansweredgroup', 'lesson', $groupname));
} else {
$lesson->add_message(get_string('noonehasanswered', 'lesson'));
}
}
} else {
$mode = 'none'; // not displaying anything
if (!empty($currentgroup)) {
$groupname = groups_get_group_name($currentgroup);
$lesson->add_message(get_string('noonehasansweredgroup', 'lesson', $groupname));
} else {
$lesson->add_message(get_string('noonehasanswered', 'lesson'));
}
}
} else {
$mode = 'none'; // not displaying anything
$lesson->add_message(get_string('noessayquestionsfound', 'lesson'));
}
break;
}
$lessonoutput = $PAGE->get_renderer('mod_lesson');
echo $lessonoutput->header($lesson, $cm, 'essay', false, null, get_string('manualgrading', 'lesson'));
switch ($mode) {
case 'display':
groups_print_activity_menu($cm, $url);
// Expects $user, $essayattempts and $pages to be set already
// Group all the essays by userid
$studentessays = array();
foreach ($essayattempts as $essay) {
// Not very nice :) but basically
// this organizes the essays so we know how many
// times a student answered an essay per try and per page
$studentessays[$essay->userid][$essay->pageid][$essay->retry][] = $essay;
}
// Setup table
$table = new html_table();
$table->head = array(get_string('name'), get_string('essays', 'lesson'), get_string('status'),
get_string('email', 'lesson'));
$table->attributes['class'] = 'standardtable generaltable';
$table->align = array('left', 'left', 'left');
$table->wrap = array('nowrap', 'nowrap', '');
// Cycle through all the students
foreach (array_keys($studentessays) as $userid) {
$studentname = fullname($users[$userid], true);
$essaylinks = array();
$essaystatuses = array();
// Number of attempts on the lesson
$attempts = $lesson->count_user_retries($userid);
// Go through each essay page
foreach ($studentessays[$userid] as $page => $tries) {
$count = 0;
// Go through each attempt per page
foreach($tries as $try) {
if ($count == $attempts) {
break; // Stop displaying essays (attempt not completed)
}
$count++;
// Make sure they didn't answer it more than the max number of attempts.
$essay = $lesson->get_last_attempt($try);
// Start processing the attempt
$essayinfo = lesson_page_type_essay::extract_useranswer($essay->useranswer);
// link for each essay
$url = new moodle_url('/mod/lesson/essay.php', array('id'=>$cm->id,'mode'=>'grade','attemptid'=>$essay->id,'sesskey'=>sesskey()));
$linktitle = userdate($essay->timeseen, get_string('strftimedatetime')).' '.
format_string($pages[$essay->pageid]->title, true);
// Different colors for all the states of an essay (graded, if sent, not graded)
if (!$essayinfo->graded) {
$class = "badge bg-warning text-dark";
$status = get_string('notgraded', 'lesson');
} elseif (!$essayinfo->sent) {
$class = "badge bg-success text-white";
$status = get_string('graded', 'lesson');
} else {
$class = "badge bg-success text-white";
$status = get_string('sent', 'lesson');
}
$attributes = array('tabindex' => 0);
$essaylinks[] = html_writer::link($url, $linktitle);
$essaystatuses[] = html_writer::span($status, $class, $attributes);
}
}
// email link for this user
$url = new moodle_url('/mod/lesson/essay.php', array('id'=>$cm->id,'mode'=>'email','userid'=>$userid,'sesskey'=>sesskey()));
$emaillink = html_writer::link($url, get_string('emailgradedessays', 'lesson'));
$table->data[] = array($OUTPUT->user_picture($users[$userid], array('courseid' => $course->id)) . $studentname,
implode("<br />", $essaylinks), implode("<br />", $essaystatuses), $emaillink);
}
// email link for all users
$url = new moodle_url('/mod/lesson/essay.php', array('id'=>$cm->id,'mode'=>'email','sesskey'=>sesskey()));
$emailalllink = html_writer::link($url, get_string('emailallgradedessays', 'lesson'));
$table->data[] = array(' ', ' ', ' ', $emailalllink);
echo html_writer::table($table);
break;
case 'grade':
// Trigger the essay grade viewed event.
$event = \mod_lesson\event\essay_attempt_viewed::create(array(
'objectid' => $attempt->id,
'relateduserid' => $attempt->userid,
'context' => $context,
'courseid' => $course->id,
));
$event->add_record_snapshot('lesson_attempts', $attempt);
$event->trigger();
// Grading form
// Expects the following to be set: $attemptid, $answer, $user, $page, $attempt
$essayinfo = lesson_page_type_essay::extract_useranswer($attempt->useranswer);
$answer = file_rewrite_pluginfile_urls($essayinfo->answer, 'pluginfile.php', $context->id,
'mod_lesson', 'essay_answers', $attempt->id);
$currentpage = $lesson->load_page($attempt->pageid);
$mform = new essay_grading_form(null, array('scoreoptions'=>$scoreoptions, 'user'=>$user));
$data = new stdClass;
$data->id = $cm->id;
$data->attemptid = $attemptid;
$data->score = $essayinfo->score;
$data->question = format_text($currentpage->contents, $currentpage->contentsformat, $formattextdefoptions);
$data->studentanswer = format_text($answer, $essayinfo->answerformat,
array('context' => $context, 'para' => true));
$data->response = $essayinfo->response;
$data->responseformat = $essayinfo->responseformat;
$editoroptions = array('noclean' => true, 'maxfiles' => EDITOR_UNLIMITED_FILES,
'maxbytes' => $CFG->maxbytes, 'context' => $context);
$data = file_prepare_standard_editor($data, 'response', $editoroptions, $context,
'mod_lesson', 'essay_responses', $attempt->id);
$mform->set_data($data);
$mform->display();
break;
default:
groups_print_activity_menu($cm, $url);
break;
}
echo $OUTPUT->footer();
+70
View File
@@ -0,0 +1,70 @@
<?php
// 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/>.
/**
* Essay grading form
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
/**
* Include formslib if it has not already been included
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/formslib.php');
/**
* Essay grading form
*
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
class essay_grading_form extends moodleform {
public function definition() {
global $CFG;
$mform = $this->_form;
$mform->addElement('header', 'formheader', get_string('question', 'lesson'));
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'attemptid');
$mform->setType('attemptid', PARAM_INT);
$mform->addElement('hidden', 'mode', 'update');
$mform->setType('mode', PARAM_ALPHA);
$mform->addElement('static', 'question', get_string('question', 'lesson'));
$mform->addElement('static', 'studentanswer', get_string('studentresponse', 'lesson', fullname($this->_customdata['user'], true)));
$editoroptions = array('noclean' => true, 'maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes' => $CFG->maxbytes);
$mform->addElement('editor', 'response_editor', get_string('comments', 'lesson'), null, $editoroptions);
$mform->setType('response', PARAM_RAW);
$mform->addElement('select', 'score', get_string('essayscore', 'lesson'), $this->_customdata['scoreoptions']);
$mform->setType('score', PARAM_INT);
$this->add_action_buttons(get_string('cancel'), get_string('savechanges'));
}
}
+821
View File
@@ -0,0 +1,821 @@
<?php
// 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/>.
/**
* format.php - Default format class for file imports/exports. Doesn't do
* everything on it's own -- it needs to be extended.
*
* Included by import.ph
*
* @package mod_lesson
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
defined('MOODLE_INTERNAL') || die();
/**
* Import files embedded into answer or response
*
* @param string $field nfield name (answer or response)
* @param array $data imported data
* @param object $answer answer object
* @param int $contextid
**/
function lesson_import_question_files($field, $data, $answer, $contextid) {
global $DB;
if (!isset($data['itemid'])) {
return;
}
$text = file_save_draft_area_files($data['itemid'],
$contextid, 'mod_lesson', 'page_' . $field . 's', $answer->id,
array('subdirs' => false, 'maxfiles' => -1, 'maxbytes' => 0),
$answer->$field);
$DB->set_field("lesson_answers", $field, $text, array("id" => $answer->id));
}
/**
* Given some question info and some data about the the answers
* this function parses, organises and saves the question
*
* This is only used when IMPORTING questions and is only called
* from format.php
* Lifted from mod/quiz/lib.php -
* 1. all reference to oldanswers removed
* 2. all reference to quiz_multichoice table removed
* 3. In shortanswer questions usecase is store in the qoption field
* 4. In numeric questions store the range as two answers
* 5. truefalse options are ignored
* 6. For multichoice questions with more than one answer the qoption field is true
*
* @param object $question Contains question data like question, type and answers.
* @param object $lesson
* @param int $contextid
* @return object Returns $result->error or $result->notice.
**/
function lesson_save_question_options($question, $lesson, $contextid) {
global $DB;
// These lines are required to ensure that all page types have
// been loaded for the following switch
if (!($lesson instanceof lesson)) {
$lesson = new lesson($lesson);
}
$manager = lesson_page_type_manager::get($lesson);
$timenow = time();
$result = new stdClass();
// Default answer to avoid code duplication.
$defaultanswer = new stdClass();
$defaultanswer->lessonid = $question->lessonid;
$defaultanswer->pageid = $question->id;
$defaultanswer->timecreated = $timenow;
$defaultanswer->answerformat = FORMAT_HTML;
$defaultanswer->jumpto = LESSON_THISPAGE;
$defaultanswer->grade = 0;
$defaultanswer->score = 0;
switch ($question->qtype) {
case LESSON_PAGE_SHORTANSWER:
$answers = array();
$maxfraction = -1;
// Insert all the new answers
foreach ($question->answer as $key => $dataanswer) {
if ($dataanswer != "") {
$answer = clone($defaultanswer);
if ($question->fraction[$key] >=0.5) {
$answer->jumpto = LESSON_NEXTPAGE;
$answer->score = 1;
}
$answer->grade = round($question->fraction[$key] * 100);
$answer->answer = $dataanswer;
$answer->response = $question->feedback[$key]['text'];
$answer->responseformat = $question->feedback[$key]['format'];
$answer->id = $DB->insert_record("lesson_answers", $answer);
lesson_import_question_files('response', $question->feedback[$key], $answer, $contextid);
$answers[] = $answer->id;
if ($question->fraction[$key] > $maxfraction) {
$maxfraction = $question->fraction[$key];
}
}
}
/// Perform sanity checks on fractional grades
if ($maxfraction != 1) {
$maxfraction = $maxfraction * 100;
$result->notice = get_string("fractionsnomax", "lesson", $maxfraction);
return $result;
}
break;
case LESSON_PAGE_NUMERICAL: // Note similarities to shortanswer.
$answers = array();
$maxfraction = -1;
// for each answer store the pair of min and max values even if they are the same
foreach ($question->answer as $key => $dataanswer) {
if ($dataanswer != "") {
$answer = clone($defaultanswer);
if ($question->fraction[$key] >= 0.5) {
$answer->jumpto = LESSON_NEXTPAGE;
$answer->score = 1;
}
$answer->grade = round($question->fraction[$key] * 100);
$min = $question->answer[$key] - $question->tolerance[$key];
$max = $question->answer[$key] + $question->tolerance[$key];
$answer->answer = $min.":".$max;
$answer->response = $question->feedback[$key]['text'];
$answer->responseformat = $question->feedback[$key]['format'];
$answer->id = $DB->insert_record("lesson_answers", $answer);
lesson_import_question_files('response', $question->feedback[$key], $answer, $contextid);
$answers[] = $answer->id;
if ($question->fraction[$key] > $maxfraction) {
$maxfraction = $question->fraction[$key];
}
}
}
/// Perform sanity checks on fractional grades
if ($maxfraction != 1) {
$maxfraction = $maxfraction * 100;
$result->notice = get_string("fractionsnomax", "lesson", $maxfraction);
return $result;
}
break;
case LESSON_PAGE_TRUEFALSE:
// In lesson the correct answer always come first, as it was the case
// in question bank exports years ago.
$answer = clone($defaultanswer);
$answer->grade = 100;
$answer->jumpto = LESSON_NEXTPAGE;
$answer->score = 1;
if ($question->correctanswer) {
$answer->answer = get_string("true", "lesson");
if (isset($question->feedbacktrue)) {
$answer->response = $question->feedbacktrue['text'];
$answer->responseformat = $question->feedbacktrue['format'];
$answer->id = $DB->insert_record("lesson_answers", $answer);
lesson_import_question_files('response', $question->feedbacktrue, $answer, $contextid);
}
} else {
$answer->answer = get_string("false", "lesson");
if (isset($question->feedbackfalse)) {
$answer->response = $question->feedbackfalse['text'];
$answer->responseformat = $question->feedbackfalse['format'];
$answer->id = $DB->insert_record("lesson_answers", $answer);
lesson_import_question_files('response', $question->feedbackfalse, $answer, $contextid);
}
}
// Now the wrong answer.
$answer = clone($defaultanswer);
if ($question->correctanswer) {
$answer->answer = get_string("false", "lesson");
if (isset($question->feedbackfalse)) {
$answer->response = $question->feedbackfalse['text'];
$answer->responseformat = $question->feedbackfalse['format'];
$answer->id = $DB->insert_record("lesson_answers", $answer);
lesson_import_question_files('response', $question->feedbackfalse, $answer, $contextid);
}
} else {
$answer->answer = get_string("true", "lesson");
if (isset($question->feedbacktrue)) {
$answer->response = $question->feedbacktrue['text'];
$answer->responseformat = $question->feedbacktrue['format'];
$answer->id = $DB->insert_record("lesson_answers", $answer);
lesson_import_question_files('response', $question->feedbacktrue, $answer, $contextid);
}
}
break;
case LESSON_PAGE_MULTICHOICE:
$totalfraction = 0;
$maxfraction = -1;
$answers = array();
// Insert all the new answers
foreach ($question->answer as $key => $dataanswer) {
if ($dataanswer != "") {
$answer = clone($defaultanswer);
$answer->grade = round($question->fraction[$key] * 100);
if ($question->single) {
if ($answer->grade > 50) {
$answer->jumpto = LESSON_NEXTPAGE;
$answer->score = 1;
}
} else {
// If multi answer allowed, any answer with fraction > 0 is considered correct.
if ($question->fraction[$key] > 0) {
$answer->jumpto = LESSON_NEXTPAGE;
$answer->score = 1;
}
}
$answer->answer = $dataanswer['text'];
$answer->answerformat = $dataanswer['format'];
$answer->response = $question->feedback[$key]['text'];
$answer->responseformat = $question->feedback[$key]['format'];
$answer->id = $DB->insert_record("lesson_answers", $answer);
lesson_import_question_files('answer', $dataanswer, $answer, $contextid);
lesson_import_question_files('response', $question->feedback[$key], $answer, $contextid);
// for Sanity checks
if ($question->fraction[$key] > 0) {
$totalfraction += $question->fraction[$key];
}
if ($question->fraction[$key] > $maxfraction) {
$maxfraction = $question->fraction[$key];
}
}
}
/// Perform sanity checks on fractional grades
if ($question->single) {
if ($maxfraction != 1) {
$maxfraction = $maxfraction * 100;
$result->notice = get_string("fractionsnomax", "lesson", $maxfraction);
return $result;
}
} else {
$totalfraction = round($totalfraction,2);
if ($totalfraction != 1) {
$totalfraction = $totalfraction * 100;
$result->notice = get_string("fractionsaddwrong", "lesson", $totalfraction);
return $result;
}
}
break;
case LESSON_PAGE_MATCHING:
$subquestions = array();
// The first answer should always be the correct answer
$correctanswer = clone($defaultanswer);
$correctanswer->answer = get_string('thatsthecorrectanswer', 'lesson');
$correctanswer->jumpto = LESSON_NEXTPAGE;
$correctanswer->score = 1;
$DB->insert_record("lesson_answers", $correctanswer);
// The second answer should always be the wrong answer
$wronganswer = clone($defaultanswer);
$wronganswer->answer = get_string('thatsthewronganswer', 'lesson');
$DB->insert_record("lesson_answers", $wronganswer);
$i = 0;
// Insert all the new question+answer pairs
foreach ($question->subquestions as $key => $questiontext) {
$answertext = $question->subanswers[$key];
if (!empty($questiontext) and !empty($answertext)) {
$answer = clone($defaultanswer);
$answer->answer = $questiontext['text'];
$answer->answerformat = $questiontext['format'];
$answer->response = $answertext;
if ($i == 0) {
// first answer contains the correct answer jump
$answer->jumpto = LESSON_NEXTPAGE;
}
$answer->id = $DB->insert_record("lesson_answers", $answer);
lesson_import_question_files('answer', $questiontext, $answer, $contextid);
$subquestions[] = $answer->id;
$i++;
}
}
if (count($subquestions) < 3) {
$result->notice = get_string("notenoughsubquestions", "lesson");
return $result;
}
break;
case LESSON_PAGE_ESSAY:
$answer = new stdClass();
$answer->lessonid = $question->lessonid;
$answer->pageid = $question->id;
$answer->timecreated = $timenow;
$answer->answer = null;
$answer->answerformat = FORMAT_MOODLE;
$answer->grade = 0;
$answer->score = 1;
$answer->jumpto = LESSON_NEXTPAGE;
$answer->response = null;
$answer->responseformat = FORMAT_MOODLE;
$answer->id = $DB->insert_record("lesson_answers", $answer);
break;
default:
$result->error = "Unsupported question type ($question->qtype)!";
return $result;
}
return true;
}
class qformat_default {
var $displayerrors = true;
var $category = null;
var $questionids = array();
protected $importcontext = null;
var $qtypeconvert = array('numerical' => LESSON_PAGE_NUMERICAL,
'multichoice' => LESSON_PAGE_MULTICHOICE,
'truefalse' => LESSON_PAGE_TRUEFALSE,
'shortanswer' => LESSON_PAGE_SHORTANSWER,
'match' => LESSON_PAGE_MATCHING,
'essay' => LESSON_PAGE_ESSAY
);
// Importing functions
function provide_import() {
return false;
}
function set_importcontext($context) {
$this->importcontext = $context;
}
/**
* Handle parsing error
*
* @param string $message information about error
* @param string $text imported text that triggered the error
* @param string $questionname imported question name
*/
protected function error($message, $text='', $questionname='') {
$importerrorquestion = get_string('importerrorquestion', 'question');
echo "<div class=\"importerror\">\n";
echo "<strong>$importerrorquestion $questionname</strong>";
if (!empty($text)) {
$text = s($text);
echo "<blockquote>$text</blockquote>\n";
}
echo "<strong>$message</strong>\n";
echo "</div>";
}
/**
* Import for questiontype plugins
* @param mixed $data The segment of data containing the question
* @param object $question processed (so far) by standard import code if appropriate
* @param object $extra mixed any additional format specific data that may be passed by the format
* @param string $qtypehint hint about a question type from format
* @return object question object suitable for save_options() or false if cannot handle
*/
public function try_importing_using_qtypes($data, $question = null, $extra = null,
$qtypehint = '') {
return false;
}
function importpreprocess() {
// Does any pre-processing that may be desired
return true;
}
function importprocess($filename, $lesson, $pageid) {
global $DB, $OUTPUT;
/// Processes a given file. There's probably little need to change this
$timenow = time();
if (! $lines = $this->readdata($filename)) {
echo $OUTPUT->notification("File could not be read, or was empty");
return false;
}
if (! $questions = $this->readquestions($lines)) { // Extract all the questions
echo $OUTPUT->notification("There are no questions in this file!");
return false;
}
//Avoid category as question type
echo $OUTPUT->notification(get_string('importcount', 'lesson',
$this->count_questions($questions)), 'notifysuccess');
$count = 0;
$addquestionontop = false;
if ($pageid == 0) {
$addquestionontop = true;
$updatelessonpage = $DB->get_record('lesson_pages', array('lessonid' => $lesson->id, 'prevpageid' => 0));
} else {
$updatelessonpage = $DB->get_record('lesson_pages', array('lessonid' => $lesson->id, 'id' => $pageid));
}
$unsupportedquestions = 0;
foreach ($questions as $question) { // Process and store each question
switch ($question->qtype) {
//TODO: Bad way to bypass category in data... Quickfix for MDL-27964
case 'category':
break;
// the good ones
case 'shortanswer' :
case 'numerical' :
case 'truefalse' :
case 'multichoice' :
case 'match' :
case 'essay' :
$count++;
//Show nice formated question in one line.
echo "<hr><p><b>$count</b>. ".$this->format_question_text($question)."</p>";
$newpage = new stdClass;
$newpage->lessonid = $lesson->id;
$newpage->qtype = $this->qtypeconvert[$question->qtype];
switch ($question->qtype) {
case 'shortanswer' :
if (isset($question->usecase)) {
$newpage->qoption = $question->usecase;
}
break;
case 'multichoice' :
if (isset($question->single)) {
$newpage->qoption = !$question->single;
}
break;
}
$newpage->timecreated = $timenow;
if ($question->name != $question->questiontext) {
$newpage->title = $question->name;
} else {
$newpage->title = "Page $count";
}
$newpage->contents = $question->questiontext;
$newpage->contentsformat = isset($question->questionformat) ? $question->questionformat : FORMAT_HTML;
// set up page links
if ($pageid) {
// the new page follows on from this page
if (!$page = $DB->get_record("lesson_pages", array("id" => $pageid))) {
throw new \moodle_exception('invalidpageid', 'lesson');
}
$newpage->prevpageid = $pageid;
$newpage->nextpageid = $page->nextpageid;
// insert the page and reset $pageid
$newpageid = $DB->insert_record("lesson_pages", $newpage);
// update the linked list
$DB->set_field("lesson_pages", "nextpageid", $newpageid, array("id" => $pageid));
} else {
// new page is the first page
// get the existing (first) page (if any)
$params = array ("lessonid" => $lesson->id, "prevpageid" => 0);
if (!$page = $DB->get_record_select("lesson_pages", "lessonid = :lessonid AND prevpageid = :prevpageid", $params)) {
// there are no existing pages
$newpage->prevpageid = 0; // this is a first page
$newpage->nextpageid = 0; // this is the only page
$newpageid = $DB->insert_record("lesson_pages", $newpage);
} else {
// there are existing pages put this at the start
$newpage->prevpageid = 0; // this is a first page
$newpage->nextpageid = $page->id;
$newpageid = $DB->insert_record("lesson_pages", $newpage);
// update the linked list
$DB->set_field("lesson_pages", "prevpageid", $newpageid, array("id" => $page->id));
}
}
// reset $pageid and put the page ID in $question, used in save_question_option()
$pageid = $newpageid;
$question->id = $newpageid;
$this->questionids[] = $question->id;
// Import images in question text.
if (isset($question->questiontextitemid)) {
$questiontext = file_save_draft_area_files($question->questiontextitemid,
$this->importcontext->id, 'mod_lesson', 'page_contents', $newpageid,
null , $question->questiontext);
// Update content with recoded urls.
$DB->set_field("lesson_pages", "contents", $questiontext, array("id" => $newpageid));
}
// Now to save all the answers and type-specific options
$question->lessonid = $lesson->id; // needed for foreign key
$question->qtype = $this->qtypeconvert[$question->qtype];
$result = lesson_save_question_options($question, $lesson, $this->importcontext->id);
if (!empty($result->error)) {
echo $OUTPUT->notification($result->error);
return false;
}
if (!empty($result->notice)) {
echo $OUTPUT->notification($result->notice);
return true;
}
break;
// the Bad ones
default :
$unsupportedquestions++;
break;
}
}
// Update the prev links if there were existing pages.
if (!empty($updatelessonpage)) {
if ($addquestionontop) {
$DB->set_field("lesson_pages", "prevpageid", $pageid, array("id" => $updatelessonpage->id));
} else {
$DB->set_field("lesson_pages", "prevpageid", $pageid, array("id" => $updatelessonpage->nextpageid));
}
}
if ($unsupportedquestions) {
echo $OUTPUT->notification(get_string('unknownqtypesnotimported', 'lesson', $unsupportedquestions));
}
return true;
}
/**
* Count all non-category questions in the questions array.
*
* @param array questions An array of question objects.
* @return int The count.
*
*/
protected function count_questions($questions) {
$count = 0;
if (!is_array($questions)) {
return $count;
}
foreach ($questions as $question) {
if (!is_object($question) || !isset($question->qtype) ||
($question->qtype == 'category')) {
continue;
}
$count++;
}
return $count;
}
function readdata($filename) {
/// Returns complete file with an array, one item per line
if (is_readable($filename)) {
$filearray = file($filename);
/// Check for Macintosh OS line returns (ie file on one line), and fix
if (preg_match("/\r/", $filearray[0]) AND !preg_match("/\n/", $filearray[0])) {
return explode("\r", $filearray[0]);
} else {
return $filearray;
}
}
return false;
}
protected function readquestions($lines) {
/// Parses an array of lines into an array of questions,
/// where each item is a question object as defined by
/// readquestion(). Questions are defined as anything
/// between blank lines.
$questions = array();
$currentquestion = array();
foreach ($lines as $line) {
$line = trim($line);
if (empty($line)) {
if (!empty($currentquestion)) {
if ($question = $this->readquestion($currentquestion)) {
$questions[] = $question;
}
$currentquestion = array();
}
} else {
$currentquestion[] = $line;
}
}
if (!empty($currentquestion)) { // There may be a final question
if ($question = $this->readquestion($currentquestion)) {
$questions[] = $question;
}
}
return $questions;
}
protected function readquestion($lines) {
/// Given an array of lines known to define a question in
/// this format, this function converts it into a question
/// object suitable for processing and insertion into Moodle.
// We should never get there unless the qformat plugin is broken.
throw new coding_exception('Question format plugin is missing important code: readquestion.');
return null;
}
/**
* Construct a reasonable default question name, based on the start of the question text.
* @param string $questiontext the question text.
* @param string $default default question name to use if the constructed one comes out blank.
* @return string a reasonable question name.
*/
public function create_default_question_name($questiontext, $default) {
$name = $this->clean_question_name(shorten_text($questiontext, 80));
if ($name) {
return $name;
} else {
return $default;
}
}
/**
* Ensure that a question name does not contain anything nasty, and will fit in the DB field.
* @param string $name the raw question name.
* @return string a safe question name.
*/
public function clean_question_name($name) {
$name = clean_param($name, PARAM_TEXT); // Matches what the question editing form does.
$name = trim($name);
$trimlength = 251;
while (core_text::strlen($name) > 255 && $trimlength > 0) {
$name = shorten_text($name, $trimlength);
$trimlength -= 10;
}
return $name;
}
/**
* return an "empty" question
* Somewhere to specify question parameters that are not handled
* by import but are required db fields.
* This should not be overridden.
* @return object default question
*/
protected function defaultquestion() {
global $CFG;
static $defaultshuffleanswers = null;
if (is_null($defaultshuffleanswers)) {
$defaultshuffleanswers = get_config('quiz', 'shuffleanswers');
}
$question = new stdClass();
$question->shuffleanswers = $defaultshuffleanswers;
$question->defaultmark = 1;
$question->image = "";
$question->usecase = 0;
$question->multiplier = array();
$question->questiontextformat = FORMAT_MOODLE;
$question->generalfeedback = '';
$question->generalfeedbackformat = FORMAT_MOODLE;
$question->correctfeedback = '';
$question->partiallycorrectfeedback = '';
$question->incorrectfeedback = '';
$question->answernumbering = 'abc';
$question->penalty = 0.3333333;
$question->length = 1;
$question->qoption = 0;
$question->layout = 1;
// this option in case the questiontypes class wants
// to know where the data came from
$question->export_process = true;
$question->import_process = true;
return $question;
}
function importpostprocess() {
/// Does any post-processing that may be desired
/// Argument is a simple array of question ids that
/// have just been added.
return true;
}
/**
* Convert the question text to plain text, so it can safely be displayed
* during import to let the user see roughly what is going on.
*/
protected function format_question_text($question) {
$formatoptions = new stdClass();
$formatoptions->noclean = true;
// The html_to_text call strips out all URLs, but format_text complains
// if it finds @@PLUGINFILE@@ tokens. So, we need to replace
// @@PLUGINFILE@@ with a real URL, but it doesn't matter what.
// We use http://example.com/.
$text = str_replace('@@PLUGINFILE@@/', 'http://example.com/', $question->questiontext);
return s(html_to_text(format_text($text,
$question->questiontextformat, $formatoptions), 0, false));
}
/**
* Since the lesson module tries to re-use the question bank import classes in
* a crazy way, this is necessary to stop things breaking.
*/
protected function add_blank_combined_feedback($question) {
return $question;
}
}
/**
* Since the lesson module tries to re-use the question bank import classes in
* a crazy way, this is necessary to stop things breaking. This should be exactly
* the same as the class defined in question/format.php.
*/
class qformat_based_on_xml extends qformat_default {
/**
* A lot of imported files contain unwanted entities.
* This method tries to clean up all known problems.
* @param string str string to correct
* @return string the corrected string
*/
public function cleaninput($str) {
$html_code_list = array(
"&#039;" => "'",
"&#8217;" => "'",
"&#8220;" => "\"",
"&#8221;" => "\"",
"&#8211;" => "-",
"&#8212;" => "-",
);
$str = strtr($str, $html_code_list);
// Use core_text entities_to_utf8 function to convert only numerical entities.
$str = core_text::entities_to_utf8($str, false);
return $str;
}
/**
* Return the array moodle is expecting
* for an HTML text. No processing is done on $text.
* qformat classes that want to process $text
* for instance to import external images files
* and recode urls in $text must overwrite this method.
* @param array $text some HTML text string
* @return array with keys text, format and files.
*/
public function text_field($text) {
return array(
'text' => trim($text),
'format' => FORMAT_HTML,
'files' => array(),
);
}
/**
* Return the value of a node, given a path to the node
* if it doesn't exist return the default value.
* @param array xml data to read
* @param array path path to node expressed as array
* @param mixed default
* @param bool istext process as text
* @param string error if set value must exist, return false and issue message if not
* @return mixed value
*/
public function getpath($xml, $path, $default, $istext=false, $error='') {
foreach ($path as $index) {
if (!isset($xml[$index])) {
if (!empty($error)) {
$this->error($error);
return false;
} else {
return $default;
}
}
$xml = $xml[$index];
}
if ($istext) {
if (!is_string($xml)) {
$this->error(get_string('invalidxml', 'qformat_xml'));
}
$xml = trim($xml);
}
return $xml;
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
// 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/>.
/**
* Redirects the user to either a lesson or to the lesson statistics
*
* @package mod_lesson
* @category grade
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
/**
* Require config.php
*/
require_once("../../config.php");
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
$id = required_param('id', PARAM_INT);
$cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST));
require_login($course, false, $cm);
$PAGE->set_url('/mod/lesson/grade.php', array('id'=>$cm->id));
if (has_capability('mod/lesson:viewreports', context_module::instance($cm->id))) {
redirect('report.php?id='.$cm->id);
} else {
redirect('view.php?id='.$cm->id);
}
+113
View File
@@ -0,0 +1,113 @@
<?php
// 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/>.
/**
* Imports lesson pages
*
* @package mod_lesson
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
require_once("../../config.php");
require_once($CFG->libdir.'/questionlib.php');
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
require_once($CFG->dirroot.'/mod/lesson/import_form.php');
require_once($CFG->dirroot.'/mod/lesson/format.php'); // Parent class
$id = required_param('id', PARAM_INT); // Course Module ID
$pageid = optional_param('pageid', '', PARAM_INT); // Page ID
$PAGE->set_url('/mod/lesson/import.php', array('id'=>$id, 'pageid'=>$pageid));
$cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST));
require_login($course, false, $cm);
$context = context_module::instance($cm->id);
require_capability('mod/lesson:edit', $context);
$strimportquestions = get_string("importquestions", "lesson");
$strlessons = get_string("modulenameplural", "lesson");
$manager = lesson_page_type_manager::get($lesson);
$data = new stdClass;
$data->id = $PAGE->cm->id;
$data->pageid = $pageid;
$mform = new lesson_import_form(null, array('formats'=>lesson_get_import_export_formats('import')));
$mform->set_data($data);
$PAGE->navbar->add($strimportquestions);
$PAGE->set_title($strimportquestions);
$PAGE->set_heading($course->fullname);
$PAGE->activityheader->set_attrs([
'hidecompletion' => true,
'description' => ''
]);
$PAGE->add_body_class('limitedwidth');
echo $OUTPUT->header();
$headinglevel = $PAGE->activityheader->get_heading_level();
echo $OUTPUT->heading_with_help($strimportquestions, 'importquestions', 'lesson', '', '', $headinglevel);
if ($data = $mform->get_data()) {
require_sesskey();
$realfilename = $mform->get_new_filename('questionfile');
$importfile = make_request_directory() . "/{$realfilename}";
if (!$result = $mform->save_file('questionfile', $importfile, true)) {
throw new moodle_exception('uploadproblem');
}
$formatclass = 'qformat_'.$data->format;
$formatclassfile = $CFG->dirroot.'/question/format/'.$data->format.'/format.php';
if (!is_readable($formatclassfile)) {
throw new \moodle_exception('unknowformat', '', '', $data->format);
}
require_once($formatclassfile);
$format = new $formatclass();
$format->set_importcontext($context);
// Do anything before that we need to
if (! $format->importpreprocess()) {
throw new \moodle_exception('preprocesserror', 'lesson');
}
// Process the uploaded file
if (! $format->importprocess($importfile, $lesson, $pageid)) {
throw new \moodle_exception('processerror', 'lesson');
}
// In case anything needs to be done after
if (! $format->importpostprocess()) {
throw new \moodle_exception('postprocesserror', 'lesson');
}
echo "<hr>";
echo $OUTPUT->continue_button('view.php?id='.$PAGE->cm->id);
} else {
// Print upload form
$mform->display();
}
echo $OUTPUT->footer();
+96
View File
@@ -0,0 +1,96 @@
<?php
// 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/>.
/**
* Form used to select a file and file format for the import
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
defined('MOODLE_INTERNAL') || die();
/**
* Form used to select a file and file format for the import
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class lesson_import_form extends moodleform {
public function definition() {
$mform = $this->_form;
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'pageid');
$mform->setType('pageid', PARAM_INT);
$mform->addElement('select', 'format', get_string('fileformat', 'lesson'), $this->_customdata['formats']);
$mform->setDefault('format', 'gift');
$mform->setType('format', 'text');
$mform->addRule('format', null, 'required');
//Using filemanager as filepicker
$mform->addElement('filepicker', 'questionfile', get_string('upload'));
$mform->addRule('questionfile', null, 'required', null, 'client');
$this->add_action_buttons(null, get_string("import"));
}
/**
* Checks that a file has been uploaded, and that it is of a plausible type.
* @param array $data the submitted data.
* @param array $errors the errors so far.
* @return array the updated errors.
* @throws moodle_exception
*/
protected function validate_uploaded_file($data, $errors) {
global $CFG;
if (empty($data['questionfile'])) {
$errors['questionfile'] = get_string('required');
return $errors;
}
$files = $this->get_draft_files('questionfile');
if (!is_array($files) || count($files) < 1) {
$errors['questionfile'] = get_string('required');
return $errors;
}
$formatfile = $CFG->dirroot.'/question/format/'.$data['format'].'/format.php';
if (!is_readable($formatfile)) {
throw new moodle_exception('formatnotfound', 'lesson', '', $data['format']);
}
require_once($formatfile);
$classname = 'qformat_' . $data['format'];
$qformat = new $classname();
return $errors;
}
public function validation($data, $files) {
$errors = parent::validation($data, $files);
$errors = $this->validate_uploaded_file($data, $errors);
return $errors;
}
}
+122
View File
@@ -0,0 +1,122 @@
<?php
// 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/>.
/**
* This page lists all the instances of lesson in a particular course
*
* @package mod_lesson
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
/** Include required files */
require_once("../../config.php");
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
$id = required_param('id', PARAM_INT); // course
$PAGE->set_url('/mod/lesson/index.php', array('id'=>$id));
if (!$course = $DB->get_record("course", array("id" => $id))) {
throw new \moodle_exception('invalidcourseid');
}
require_login($course);
$PAGE->set_pagelayout('incourse');
// Trigger instances list viewed event.
$params = array(
'context' => context_course::instance($course->id)
);
$event = \mod_lesson\event\course_module_instance_list_viewed::create($params);
$event->add_record_snapshot('course', $course);
$event->trigger();
/// Get all required strings
$strlessons = get_string("modulenameplural", "lesson");
$strlesson = get_string("modulename", "lesson");
/// Print the header
$PAGE->navbar->add($strlessons);
$PAGE->set_title("$course->shortname: $strlessons");
$PAGE->set_heading($course->fullname);
echo $OUTPUT->header();
echo $OUTPUT->heading($strlessons, 2);
/// Get all the appropriate data
if (! $lessons = get_all_instances_in_course("lesson", $course)) {
notice(get_string('thereareno', 'moodle', $strlessons), "../../course/view.php?id=$course->id");
die;
}
$usesections = course_format_uses_sections($course->format);
/// Print the list of instances (your module will probably extend this)
$timenow = time();
$strname = get_string("name");
$strgrade = get_string("gradenoun");
$strdeadline = get_string("deadline", "lesson");
$strnodeadline = get_string("nodeadline", "lesson");
$table = new html_table();
if ($usesections) {
$strsectionname = get_string('sectionname', 'format_'.$course->format);
$table->head = array ($strsectionname, $strname, $strgrade, $strdeadline);
$table->align = array ("center", "left", "center", "center");
} else {
$table->head = array ($strname, $strgrade, $strdeadline);
$table->align = array ("left", "center", "center");
}
// Get all deadlines.
$deadlines = lesson_get_user_deadline($course->id);
foreach ($lessons as $lesson) {
$cm = get_coursemodule_from_instance('lesson', $lesson->id);
$context = context_module::instance($cm->id);
$class = $lesson->visible ? null : array('class' => 'dimmed'); // Hidden modules are dimmed.
$link = html_writer::link(new moodle_url('view.php', array('id' => $cm->id)), format_string($lesson->name, true), $class);
$deadline = $deadlines[$lesson->id]->userdeadline;
if ($deadline == 0) {
$due = $strnodeadline;
} else if ($deadline > $timenow) {
$due = userdate($deadline);
} else {
$due = html_writer::tag('span', userdate($deadline), array('class' => 'text-danger'));
}
if ($usesections) {
if (has_capability('mod/lesson:manage', $context)) {
$grade_value = $lesson->grade;
} else {
// it's a student, show their grade
$grade_value = 0;
if ($return = lesson_get_user_grades($lesson, $USER->id)) {
$grade_value = $return[$USER->id]->rawgrade;
}
}
$table->data[] = array (get_section_name($course, $lesson->section), $link, $grade_value, $due);
} else {
$table->data[] = array ($link, $lesson->grade, $due);
}
}
echo html_writer::table($table);
echo $OUTPUT->footer();
+2
View File
@@ -0,0 +1,2 @@
completiontimespent,mod_lesson
grade,mod_lesson
+618
View File
@@ -0,0 +1,618 @@
<?php
// 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/>.
/**
* Strings for component 'lesson', language 'en', branch 'MOODLE_20_STABLE'
*
* @package mod_lesson
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['accesscontrol'] = 'Access control';
$string['actionaftercorrectanswer'] = 'Action after correct answer';
$string['actionaftercorrectanswer_help'] = 'After answering a question correctly, there are 3 options for the following page:
* Normal - Follow lesson path
* Show an unseen page - Pages are shown in a random order with no page shown twice
* Show an unanswered page - Pages are shown in a random order, with pages containing unanswered questions shown again';
$string['actions'] = 'Actions';
$string['activitylink'] = 'Link to next activity';
$string['activitylink_help'] = 'To provide a link at the end of the lesson to another activity in the course, select the activity from the drop-down menu.';
$string['activitylinkname'] = 'Go to {$a}';
$string['activityoverview'] = 'You have lessons that are due';
$string['addabranchtable'] = 'Add a content page';
$string['addanendofbranch'] = 'Add an end of branch';
$string['addanewpage'] = 'Add a new page';
$string['addaquestionpage'] = 'Add a question page';
$string['addaquestionpagehere'] = 'Add a question page here';
$string['addbranchtable'] = 'Add a content page';
$string['addcluster'] = 'Add a cluster';
$string['addessay'] = 'Create an Essay question page';
$string['addedabranchtable'] = 'Added a content page';
$string['addedanendofbranch'] = 'Added an end of branch';
$string['addedaquestionpage'] = 'Added a question page';
$string['addedcluster'] = 'Added a cluster';
$string['addedendofcluster'] = 'Added an end of cluster';
$string['addendofbranch'] = 'Add end of branch';
$string['addendofcluster'] = 'Add an end of cluster';
$string['addmatching'] = 'Create a Matching question page';
$string['addmultichoice'] = 'Create a Multichoice question page';
$string['addnewgroupoverride'] = 'Add group override';
$string['addnewuseroverride'] = 'Add user override';
$string['addnumerical'] = 'Create a Numerical question page';
$string['addpage'] = 'Add a page';
$string['addshortanswer'] = 'Create a Short answer question page';
$string['addtruefalse'] = 'Create a True/false question page';
$string['allotheranswers'] = 'All other answers';
$string['allotheranswersjump'] = 'All other answers jump';
$string['allotheranswersscore'] = 'All other answers score';
$string['allowofflineattempts'] = 'Allow lesson to be attempted offline using the mobile app';
$string['allowofflineattempts_help'] = 'If enabled, a mobile app user can download the lesson and attempt it offline.
All the possible answers and correct responses will be downloaded as well.
Note: It is not possible for a lesson to be attempted offline if it has a time limit.';
$string['and'] = 'AND';
$string['anchortitle'] = 'Start of main content';
$string['answer'] = 'Answer';
$string['answeredcorrectly'] = 'answered correctly.';
$string['answersfornumerical'] = 'Answers for numerical questions should be matched pairs of minimum and maximum values';
$string['arrangebuttonshorizontally'] = 'Arrange content buttons horizontally?';
$string['attempt'] = 'Attempt: {$a}';
$string['attemptheader'] = 'Attempt';
$string['attemptinfonograde'] = '{$a->timestart} ({$a->duration})';
$string['attemptinfowithgrade'] = '{$a->grade}% {$a->timestart} ({$a->duration})';
$string['attempts'] = 'Attempts';
$string['attemptsdeleted'] = 'Deleted attempts';
$string['attemptsremaining'] = 'You have {$a} attempt(s) remaining';
$string['available'] = 'Available from';
$string['averagescore'] = 'Average score';
$string['averagetime'] = 'Average time';
$string['branch'] = 'Content';
$string['branchtable'] = 'Content';
$string['cachedef_overrides'] = 'User and group override information';
$string['cancel'] = 'Cancel';
$string['cannotfindanswer'] = 'Error: could not find answer';
$string['cannotfindattempt'] = 'Error: could not find attempt';
$string['cannotfindessay'] = 'Error: could not find essay';
$string['cannotfindfirstgrade'] = 'Error: could not find grades';
$string['cannotfindfirstpage'] = 'Could not find first page';
$string['cannotfindgrade'] = 'Error: could not find grades';
$string['cannotfindnewestgrade'] = 'Error: could not find newest grade';
$string['cannotfindnextpage'] = 'Lesson backup: Next page not found!';
$string['cannotfindpagerecord'] = 'Add end of branch: page record not found';
$string['cannotfindpages'] = 'Could not find lesson pages';
$string['cannotfindpagetitle'] = 'Confirm delete: page title not found';
$string['cannotfindpreattempt'] = 'Previous attempt record could not be found!';
$string['cannotfindrecords'] = 'Error: could not find lesson records';
$string['cannotfindtimer'] = 'Error: could not find lesson_timer records';
$string['cannotfinduser'] = 'Error: could not find users';
$string['canretake'] = '{$a} can re-take';
$string['casesensitive'] = 'Use regular expressions';
$string['casesensitive_help'] = 'Tick the checkbox to use regular expressions for analysing responses.';
$string['classstats'] = 'Class statistics';
$string['clicktodownload'] = 'Click on the following link to download the file.';
$string['closebeforeopen'] = 'Could not update the lesson. You have specified a close date before the open date.';
$string['cluster'] = 'Cluster';
$string['clusterjump'] = 'Unseen question within a cluster';
$string['clustertitle'] = 'Cluster';
$string['collapsed'] = 'Collapsed';
$string['comments'] = 'Your comments';
$string['completed'] = 'Completed';
$string['completederror'] = 'Complete the lesson';
$string['completethefollowingconditions'] = 'You must complete the following condition(s) in <b>{$a}</b> lesson before you can proceed.';
$string['completiondetail:reachend'] = 'Go through the activity to the end';
$string['completiondetail:timespent'] = 'Spend at least {$a} on this activity';
$string['completionendreached'] = 'Require end reached';
$string['completionendreached_desc'] = 'Student must reach the end of lesson page to complete this activity';
$string['completiontimespentdesc'] = 'Student must do this activity for at least {$a}';
$string['completiontimespentgroup'] = 'Require time spent';
$string['conditionsfordependency'] = 'Condition(s) for the dependency';
$string['configintro'] = 'The values set here define the default values that are used in the settings form when creating a new lesson activity. Settings specified as advanced are only shown when the \'Show more...\' link is clicked.';
$string['configmaxanswers'] = 'Default maximum number of answers per page';
$string['configmediaclose'] = 'Displays a close button as part of the popup generated for a linked media file';
$string['configmediaheight'] = 'Sets the height of the popup displayed for a linked media file';
$string['configmediawidth'] = 'Sets the width of the popup displayed for a linked media file';
$string['configpassword_desc'] = 'Whether a password is required in order to access the lesson.';
$string['configslideshowbgcolor'] = 'Background colour for the slideshow if it is enabled';
$string['configslideshowheight'] = 'Sets the height of the slideshow if it is enabled';
$string['configslideshowwidth'] = 'Sets the width of the slideshow if it is enabled';
$string['configtimelimit_desc'] = 'If a time limit is set, a warning is displayed at the beginning of the lesson and there is a countdown timer. If set to zero, then there is no time limit.';
$string['confirmdelete'] = 'Delete page';
$string['confirmdeletionofthispage'] = 'Confirm deletion of this page';
$string['congratulations'] = 'Congratulations - end of lesson reached';
$string['continue'] = 'Continue';
$string['continuetoanswer'] = 'Continue to change answers.';
$string['continuetonextpage'] = 'Continue to next page.';
$string['correctanswerjump'] = 'Correct answer jump';
$string['correctanswerscore'] = 'Correct answer score';
$string['correctresponse'] = 'Correct response';
$string['createaquestionpage'] = 'Create a question page';
$string['credit'] = 'Credit';
$string['customscoring'] = 'Custom scoring';
$string['customscoring_help'] = 'If enabled, a whole number value (positive or negative) may be entered for each answer.';
$string['deadline'] = 'Deadline';
$string['defaultessayresponse'] = 'Your essay will be graded by your teacher.';
$string['deleteallattempts'] = 'Delete all lesson attempts';
$string['deletedefaults'] = 'Deleted {$a} x lesson default';
$string['deletedpage'] = 'Deleted page';
$string['deletepagenamed'] = 'Delete page: {$a}';
$string['deleting'] = 'Deleting';
$string['deletingpage'] = 'Deleting page: {$a}';
$string['dependencyon'] = 'Dependent on';
$string['dependencyon_help'] = 'This setting allows access to this lesson to be dependent upon a student\'s performance in another lesson in the same course. Any combination of time spent, completed or "grade better than" may be used.';
$string['description'] = 'Description';
$string['deselectallattempts'] = 'Deselect all attempts';
$string['detailedstats'] = 'Detailed statistics';
$string['didnotanswerquestion'] = 'Did not answer this question.';
$string['didnotreceivecredit'] = 'Did not receive credit';
$string['disabled'] = 'Disabled';
$string['displaydefaultfeedback'] = 'Use default feedback';
$string['displaydefaultfeedback_help'] = 'If enabled, when a response is not found for a particular question, the default response of "That\'s the correct answer" or "That\'s the wrong answer" will be shown.';
$string['displayinleftmenu'] = 'Display in menu?';
$string['displayleftif'] = 'Minimum grade to display menu';
$string['displayleftif_help'] = 'This setting determines whether a student must obtain a certain grade before viewing the lesson menu. This forces the student to go through the entire lesson on their first attempt, then after obtaining the required grade they can use the menu for review.';
$string['displayleftmenu'] = 'Display menu';
$string['displayleftmenu_help'] = 'If enabled, a menu allowing users to navigate through the list of pages is displayed.';
$string['displaymode'] = 'Display mode';
$string['displayofgrade'] = 'Display of grade (for students only)';
$string['displayreview'] = 'Provide option to try a question again';
$string['displayreview_help'] = 'If enabled, when a question is answered incorrectly, the student is given the option to try it again for no point credit, or continue with the lesson. If the student clicks to move on to another question then the selected (wrong) answer will be followed. By default wrong answer jumps are set to "this page" and have a score of 0, so it is recommended that you set the wrong answer jump to a different page to avoid confusion with your students.';
$string['displayscorewithessays'] = '<p>You earned {$a->score} out of {$a->tempmaxgrade} for the automatically graded questions.</p>
<p>Your {$a->essayquestions} essay question(s) will be graded and added into your final score at a later date.</p>
<p>Your current grade without the essay question(s) is {$a->score} out of {$a->grade}.</p>';
$string['displayscorewithoutessays'] = 'Your score is {$a->score} (out of {$a->grade}).';
$string['duplicatepagenamed'] = 'Duplicate page: {$a}';
$string['edit'] = 'Edit';
$string['editbranchtable'] = 'Editing a content page';
$string['editcluster'] = 'Editing a cluster';
$string['editendofcluster'] = 'Editing an end of cluster page';
$string['editendofbranch'] = 'Editing an end of branch page';
$string['editessay'] = 'Editing an Essay question page';
$string['editingquestionpage'] = 'Editing {$a} question page';
$string['editlesson'] = 'Edit lesson';
$string['editinglesson'] = 'Editing lesson';
$string['editlessonsettings'] = 'Edit lesson settings';
$string['editmatching'] = 'Editing a Matching question page';
$string['editmultichoice'] = 'Editing a Multichoice question page';
$string['editnumerical'] = 'Editing a Numerical question page';
$string['editoverride'] = 'Edit override';
$string['editpage'] = 'Edit page contents';
$string['editpagecontent'] = 'Edit page contents';
$string['editquestion'] = 'Editing a question page';
$string['editshortanswer'] = 'Editing a Short answer question page';
$string['edittruefalse'] = 'Editing a True/false question page';
$string['email'] = 'Email';
$string['emailallgradedessays'] = 'Send essay graded notifications';
$string['emailgradedessays'] = 'Send essay graded notifications';
$string['emailsuccess'] = 'Notifications sent successfully';
$string['enabled'] = 'Enabled';
$string['endofbranch'] = 'End of branch';
$string['endofcluster'] = 'End of cluster';
$string['endofclustertitle'] = 'End of cluster';
$string['endoflesson'] = 'End of lesson';
$string['enteredthis'] = 'entered this.';
$string['enterpassword'] = 'Please enter the password:';
$string['emptypassword'] = 'Password cannot be empty';
$string['eolstudentoutoftime'] = 'Attention: You ran out of time for this lesson. Your last answer may not have counted if it was answered after the time was up.';
$string['eolstudentoutoftimenoanswers'] = 'You did not answer any questions. You have received a 0 for this lesson.';
$string['essay'] = 'Essay';
$string['essayemailmessage2'] = '<p>Essay prompt: {$a->question}</p><p>Your response: <em>{$a->response}</em></p><p>Grader\'s comments: <em>{$a->comment}</em></p><p>You have received {$a->earned} out of {$a->outof} for this essay question.</p><p>Your grade for the {$a->lesson} lesson has been changed to {$a->newgrade}&#37;.</p>';
$string['essayemailmessagesmall'] = '<p>You have received {$a->earned} out of {$a->outof} for this essay question.</p><p>Your grade for the {$a->lesson} lesson has been changed to {$a->newgrade}&#37;.</p>';
$string['essayemailsubject'] = 'Grade available for lesson question';
$string['essaynotgradedyet'] = 'This essay has not been graded yet';
$string['essayresponses'] = 'Essay responses';
$string['essays'] = 'Essays';
$string['essayscore'] = 'Essay score';
$string['eventcontentpageviewed'] = 'Content page viewed';
$string['eventessayassessed'] = 'Essay assessed';
$string['eventessayattemptviewed'] = 'Essay attempt viewed';
$string['eventhighscoreadded'] = 'Lesson high score added';
$string['eventhighscoresviewed'] = 'Lesson high scores viewed';
$string['eventlessonended'] = 'Lesson ended';
$string['eventlessonrestarted'] = 'Lesson restarted';
$string['eventlessonresumed'] = 'Lesson resumed';
$string['eventlessonstarted'] = 'Lesson started';
$string['eventoverridecreated'] = 'Lesson override created';
$string['eventoverridedeleted'] = 'Lesson override deleted';
$string['eventoverrideupdated'] = 'Lesson override updated';
$string['eventpagecreated'] = 'Page created';
$string['eventpagemoved'] = 'Page moved';
$string['eventpageupdated'] = 'Page updated';
$string['eventpagedeleted'] = 'Page deleted';
$string['eventquestionanswered'] = 'Question answered';
$string['eventquestionviewed'] = 'Question viewed';
$string['false'] = 'False';
$string['fileformat'] = 'File format';
$string['finalwrong'] = 'Not quite.';
$string['finish'] = 'Finish';
$string['firstanswershould'] = 'First answer should jump to the "Correct" page';
$string['firstwrong'] = 'You have answered incorrectly. Would you like to attempt the question again? (If you now answer the question correctly, it will not count towards your final score.)';
$string['flowcontrol'] = 'Flow control';
$string['fractionsaddwrong'] = 'The positive grades you have chosen do not add up to 100%<br />Instead, they add up to {$a}%<br />Do you want to go back and fix this question?';
$string['fractionsnomax'] = 'One of the answers should be 100%, so that it is<br />possible to get a full grade for this question.<br />Do you want to go back and fix this question?';
$string['full'] = 'Expanded';
$string['general'] = 'General';
$string['gotoendoflesson'] = 'Go to the end of the lesson';
$string['gradebetterthan'] = 'Grade better than (&#37;)';
$string['gradebetterthanerror'] = 'Earn a grade better than {$a} percent';
$string['graded'] = 'Graded';
$string['gradeessay'] = 'Grade essay questions ({$a->notgradedcount} not graded and {$a->notsentcount} not sent)';
$string['gradeis'] = 'Grade is {$a}';
$string['gradeoptions'] = 'Grade options';
$string['groupoverrides'] = 'Group overrides';
$string['groupoverridesdeleted'] = 'Group overrides deleted';
$string['groupsnone'] = 'No groups you can access.';
$string['handlingofretakes'] = 'Handling of re-takes';
$string['handlingofretakes_help'] = 'If re-takes are allowed, this setting specifies whether the grade for the lesson is the mean or maximum of all attempts.';
$string['havenotgradedyet'] = 'Have not graded yet.';
$string['here'] = 'here';
$string['highscore'] = 'High score';
$string['hightime'] = 'High time';
$string['checkbranchtable'] = 'Check content page';
$string['checkedthisone'] = 'Checked this one.';
$string['checknavigation'] = 'Check navigation';
$string['checkquestion'] = 'Check question';
$string['importcount'] = 'Importing {$a} questions';
$string['importquestions'] = 'Import questions';
$string['importquestions_help'] = 'This feature enables questions in a variety of formats to be imported via text file.';
$string['inactiveoverridehelp'] = '* This override is inactive because the user\'s access to the activity is restricted. This can be due to group or role assignments, other access restrictions, or the activity being hidden.';
$string['insertedpage'] = 'Inserted page';
$string['indicator:cognitivedepth'] = 'Lesson cognitive';
$string['indicator:cognitivedepth_help'] = 'This indicator is based on the cognitive depth reached by the student in a Lesson activity.';
$string['indicator:cognitivedepthdef'] = 'Lesson cognitive';
$string['indicator:cognitivedepthdef_help'] = 'The participant has reached this percentage of the cognitive engagement offered by the Lesson activities during this analysis interval (Levels = No view, View, Submit, View feedback, Comment on feedback, Resubmit after viewing feedback)';
$string['indicator:cognitivedepthdef_link'] = 'Learning_analytics_indicators#Cognitive_depth';
$string['indicator:socialbreadth'] = 'Lesson social';
$string['indicator:socialbreadth_help'] = 'This indicator is based on the social breadth reached by the student in a Lesson activity.';
$string['indicator:socialbreadthdef'] = 'Lesson social';
$string['indicator:socialbreadthdef_help'] = 'The participant has reached this percentage of the social engagement offered by the Lesson activities during this analysis interval (Levels = No participation, Participant alone, Participant with others)';
$string['indicator:socialbreadthdef_link'] = 'Learning_analytics_indicators#Social_breadth';
$string['invalidfile'] = 'Invalid file';
$string['invalidid'] = 'No course module ID or lesson ID were passed';
$string['invalidlessonid'] = 'lesson ID was incorrect';
$string['invalidoverrideid'] = 'Invalid override id';
$string['invalidpageid'] = 'Invalid page ID';
$string['jump'] = 'Jump';
$string['jumps'] = 'Jumps';
$string['jumps_help'] = 'Each answer (for questions) or description (for content pages) has a corresponding jump. The jump can be relative, such as this page or next page, or absolute, specifying any one of the pages in the lesson.';
$string['jumpsto'] = 'Jumps to <em>{$a}</em>';
$string['leftduringtimedsession'] = 'You have left during a timed lesson.';
$string['leftduringtimed'] = 'You have left during a timed lesson.<br />Please click on Continue to restart the lesson.';
$string['leftduringtimednoretake'] = 'You have left during a timed lesson and you are<br />not allowed to retake or continue the lesson.';
$string['lesson:addinstance'] = 'Add a new lesson';
$string['lesson:grade'] = 'Grade lesson essay questions';
$string['lessonclosed'] = 'This lesson closed on {$a}.';
$string['lessonbeingpreviewed'] = 'Lesson is currently being previewed.';
$string['lessoncloses'] = 'Lesson closes';
$string['lessoneventcloses'] = '{$a} closes';
$string['lesson:edit'] = 'Edit a lesson activity';
$string['lessonformating'] = 'Lesson formatting';
$string['lesson:manage'] = 'Manage a lesson activity';
$string['lesson:manageoverrides'] = 'Manage lesson overrides';
$string['lesson:view'] = 'View lesson activity';
$string['lesson:viewreports'] = 'View lesson reports';
$string['lessonmenu'] = 'Lesson menu';
$string['lessonnotready'] = 'This lesson is not ready to be taken. Please contact your {$a}.';
$string['lessonnotready2'] = 'This lesson is not ready to be taken.';
$string['lessonopen'] = 'This lesson will be open on {$a}.';
$string['lessonopens'] = 'Lesson opens';
$string['lessoneventopens'] = '{$a} opens';
$string['lessonpagelinkingbroken'] = 'First page not found. Lesson page linking must be broken. Please contact an admin.';
$string['lessonstats'] = 'Lesson statistics';
$string['linkedmedia'] = 'Linked media';
$string['loginfail'] = 'Login failed, please try again...';
$string['lowscore'] = 'Low score';
$string['lowtime'] = 'Low time';
$string['manageoverrides'] = 'Manage overrides';
$string['manualgrading'] = 'Grade essays';
$string['matchesanswer'] = 'Matches with answer';
$string['matching'] = 'Matching';
$string['matchingpair'] = 'Matching pair {$a}';
$string['maxgrade'] = 'Maximum grade';
$string['maxgrade_help'] = 'This setting specifies the maximum grade for the lesson. If set to 0, the lesson does not appear in the grades pages.';
$string['maximumnumberofanswersbranches'] = 'Maximum number of answers';
$string['maximumnumberofanswersbranches_help'] = 'This setting specifies the maximum number of answers that may be used in the lesson. If only true/false questions are used, it can be set to 2. The setting may be changed at any time, since it only affects what the teacher sees, not the data.';
$string['maximumnumberofattempts'] = 'Maximum number of attempts per question';
$string['maximumnumberofattempts_help'] = 'This setting specifies the maximum number of attempts allowed for each question. If answered incorrectly repeatedly, when the maximum is reached, the next page of the lesson is displayed.';
$string['maximumnumberofattemptsreached'] = 'Maximum number of attempts reached - Moving to next page';
$string['mediaclose'] = 'Show close button';
$string['mediafile'] = 'Linked media';
$string['mediafile_help'] = 'A media file may be uploaded for use in the lesson. A \'Click here to view\' link will then be displayed in a block called \'Linked media\' on each page of the lesson.';
$string['mediafilepopup'] = 'Click here to view';
$string['mediaheight'] = 'Popup window height';
$string['mediawidth'] = 'Popup window width';
$string['messageprovider:graded_essay'] = 'Lesson essay graded notification';
$string['minimumnumberofquestions'] = 'Minimum number of questions';
$string['minimumnumberofquestions_help'] = 'This setting specifies the minimum number of questions that will be used to calculate a grade for the activity.';
$string['missingname'] = 'Please enter a nickname';
$string['modattempts'] = 'Allow student review';
$string['modattempts_help'] = 'If enabled, students can navigate through the lesson again from the start.';
$string['modattemptsnoteacher'] = 'Student review only works for students.';
$string['modulename'] = 'Lesson';
$string['modulename_help'] = 'The lesson activity module enables a teacher to deliver content and/or practice activities in interesting and flexible ways. A teacher can use the lesson to create a linear set of content pages or instructional activities that offer a variety of paths or options for the learner. In either case, teachers can choose to increase engagement and ensure understanding by including a variety of questions, such as multiple choice, matching and short answer. Depending on the student\'s choice of answer and how the teacher develops the lesson, students may progress to the next page, be taken back to a previous page or redirected down a different path entirely.
A lesson may be graded, with the grade recorded in the gradebook.
Lessons may be used
* For self-directed learning of a new topic
* For scenarios or simulations/decision-making exercises
* For differentiated revision, with different sets of revision questions depending upon answers given to initial questions';
$string['modulename_link'] = 'mod/lesson/view';
$string['modulenameplural'] = 'Lessons';
$string['move'] = 'Move page';
$string['movedpage'] = 'Moved page';
$string['movepagehere'] = 'Move page to here';
$string['movepagenamed'] = 'Move page: {$a}';
$string['moving'] = 'Moving page: {$a}';
$string['multianswer'] = 'Multiple-answer';
$string['multianswer_help'] = 'Tick the checkbox if more than one response is a correct answer.';
$string['multichoice'] = 'Multichoice';
$string['multipleanswer'] = 'Multiple answer';
$string['nameapproved'] = 'Name approved';
$string['namereject'] = 'Sorry, your name has been rejected by the filter.<br />Please try another name.';
$string['new'] = 'new';
$string['nextpage'] = 'Next page';
$string['noanswer'] = 'One or more questions have no answer given. Please go back and submit an answer.';
$string['noattemptrecordsfound'] = 'No attempt records found: no grade given';
$string['nobranchtablefound'] = 'No content page found';
$string['noclose'] = 'No close date';
$string['nocommentyet'] = 'No comment yet.';
$string['nocoursemods'] = 'No activities found';
$string['nocredit'] = 'No credit';
$string['nodeadline'] = 'No deadline';
$string['noessayquestionsfound'] = 'No essay questions found in this lesson.';
$string['nohighscores'] = 'No high scores';
$string['nolessonattempts'] = 'No attempts have been made on this lesson.';
$string['nolessonattemptsgroup'] = 'No attempts have been made by {$a} group members on this lesson.';
$string['none'] = 'None';
$string['nooneansweredcorrectly'] = 'No one answered correctly.';
$string['nooneansweredthisquestion'] = 'No one answered this question.';
$string['nooneenteredthis'] = 'No one entered this.';
$string['noonehasanswered'] = 'No one has answered an essay question yet.';
$string['noonehasansweredgroup'] = 'No one in {$a} has answered an essay question yet.';
$string['noonecheckedthis'] = 'No one checked this.';
$string['noopen'] = 'No open date';
$string['nooverridedata'] = 'You must override at least one of the lesson settings.';
$string['nooverridecreated'] = 'No overrides created.';
$string['noretake'] = 'You are not allowed to retake this lesson.';
$string['normal'] = 'Normal - follow lesson path';
$string['notcompleted'] = 'Not completed';
$string['notcompletedwithdate'] = 'Not completed ({$a})';
$string['notyetcompleted'] = 'Lesson has been started, but not yet completed';
$string['notdefined'] = 'Not defined';
$string['notenoughsubquestions'] = 'Not enough sub-questions have been defined!';
$string['notenoughtimespent'] = 'You completed this lesson in {$a->timespent}, which is less than the required time of {$a->timerequired}. You might need to attempt the lesson again.';
$string['notgraded'] = 'Not graded';
$string['notitle'] = 'No title';
$string['numberofcorrectanswers'] = 'Number of correct answers: {$a}';
$string['numberofcorrectanswersheader'] = 'Number of correct answers';
$string['numberofcorrectmatches'] = 'Number of correct matches: {$a}';
$string['numberofpagestoshow'] = 'Number of pages to show';
$string['numberofpagestoshow_help'] = 'This setting specifies the number of pages shown in a lesson. It is only applicable for lessons with pages shown in a random order (when "Action after correct answer" is set to "Show an unseen page" or "Show an unanswered page"). If set to zero, then all pages are shown.';
$string['numberofpagesviewed'] = 'Number of questions answered: {$a}';
$string['numberofpagesviewedheader'] = 'Number of questions answered';
$string['numberofpagesviewednotice'] = 'Number of questions answered: {$a->nquestions} (You should answer at least {$a->minquestions})';
$string['numerical'] = 'Numerical';
$string['numericanswer_help'] = 'You can specify a single number, or a range of numbers by using colon. For example 2:5 means any answer between 2 and 5 and including 2 and 5 is correct.';
$string['numericanswer'] = 'Numeric answer';
$string['offlinedatamessage'] = 'You have worked on this attempt using a mobile device. Data was last saved to this site {$a} ago. Please check that you do not have any unsaved work.';
$string['ongoing'] = 'Display ongoing score';
$string['ongoing_help'] = 'If enabled, each page will display the student\'s current points earned out of the total possible thus far.';
$string['ongoingcustom'] = 'You have earned {$a->score} point(s) out of {$a->currenthigh} point(s) thus far.';
$string['ongoingnormal'] = 'You have answered {$a->correct} correctly out of {$a->viewed} attempts.';
$string['onpostperpage'] = 'Only one posting per grade';
$string['openafterclose'] = 'You have specified an open date after the close date';
$string['options'] = 'Options';
$string['or'] = 'OR';
$string['ordered'] = 'Ordered';
$string['other'] = 'Other';
$string['outof'] = 'Out of {$a}';
$string['override'] = 'Override';
$string['overridedeletegroupsure'] = 'Are you sure you want to delete the override for group {$a}?';
$string['overridedeleteusersure'] = 'Are you sure you want to delete the override for user {$a}?';
$string['overridegroup'] = 'Override group';
$string['overridegroupeventname'] = '{$a->lesson} - {$a->group}';
$string['overrides'] = 'Overrides';
$string['overrideuser'] = 'Override user';
$string['overrideusereventname'] = '{$a->lesson} - Override';
$string['overview'] = 'Overview';
$string['overview_help'] = 'A lesson is made up of a number of pages and optionally content pages. A page contains some content and usually ends with a question. Associated with each answer to the question is a jump. The jump can be relative, such as this page or next page, or absolute, specifying any one of the pages in the lesson. A content page is a page containing a set of links to other pages in the lesson, for example a Table of Contents.';
$string['page'] = 'Page: {$a}';
$string['page-mod-lesson-x'] = 'Any lesson page';
$string['page-mod-lesson-view'] = 'View or preview lesson page';
$string['page-mod-lesson-edit'] = 'Edit lesson page';
$string['pageanswers'] = 'Page answers';
$string['pagecontents'] = 'Page contents';
$string['pageresponses'] = 'Page responses';
$string['pages'] = 'Pages';
$string['pagetitle'] = 'Page title';
$string['password'] = 'Password';
$string['passwordprotectedlesson'] = '{$a} is a password protected lesson.';
$string['pleaseenteryouranswerinthebox'] = 'Please enter your answer in the box';
$string['pleasecheckoneanswer'] = 'Please check one answer';
$string['pleasecheckoneormoreanswers'] = 'Please check one or more answers';
$string['pleasematchtheabovepairs'] = 'Please match the above pairs';
$string['pluginadministration'] = 'Lesson administration';
$string['pluginname'] = 'Lesson';
$string['pointsearned'] = 'Points earned';
$string['postprocesserror'] = 'Error occurred during post-processing!';
$string['postsuccess'] = 'Post successful';
$string['practice'] = 'Practice lesson';
$string['practice_help'] = 'A practice lesson does not appear in the gradebook.';
$string['preprocesserror'] = 'Error occurred during pre-processing!';
$string['prerequisiteisobsolete'] = 'The prerequisite lesson option is due to be removed. Please use access restrictions instead.';
$string['prerequisitelesson'] = 'Prerequisite lesson';
$string['preview'] = 'Preview';
$string['previewlesson'] = 'Preview {$a}';
$string['previewpagenamed'] = 'Preview page: {$a}';
$string['previouspage'] = 'Previous page';
$string['privacy:metadata:attempts:userid'] = 'The user ID';
$string['privacy:metadata:attempts:pageid'] = 'The page ID';
$string['privacy:metadata:attempts:answerid'] = 'The answer ID';
$string['privacy:metadata:attempts:retry'] = 'The attempt number';
$string['privacy:metadata:attempts:correct'] = 'Whether the attempt was correct';
$string['privacy:metadata:attempts:useranswer'] = 'Details about the user\'s answer';
$string['privacy:metadata:attempts:timeseen'] = 'The time when the attempt was made';
$string['privacy:metadata:attempts'] = 'A record of page attempts';
$string['privacy:metadata:grades:userid'] = 'The user ID';
$string['privacy:metadata:grades:grade'] = 'The grade given';
$string['privacy:metadata:grades:completed'] = 'The date when the grade was given';
$string['privacy:metadata:grades'] = 'A record of the grades for each lesson';
$string['privacy:metadata:timer:userid'] = 'The user ID';
$string['privacy:metadata:timer:starttime'] = 'The date when the attempt started';
$string['privacy:metadata:timer:lessontime'] = 'The last moment when we recorded activity';
$string['privacy:metadata:timer:completed'] = 'Whether the attempt is complete';
$string['privacy:metadata:timer:timemodifiedoffline'] = 'The last moment when we recorded activity from the mobile app';
$string['privacy:metadata:timer'] = 'A record of a lesson attempt';
$string['privacy:metadata:branch:userid'] = 'The user ID';
$string['privacy:metadata:branch:pageid'] = 'The page ID';
$string['privacy:metadata:branch:retry'] = 'The attempt number';
$string['privacy:metadata:branch:flag'] = 'Whether the next page was calculated randomely';
$string['privacy:metadata:branch:timeseen'] = 'The time when the page was viewed';
$string['privacy:metadata:branch:nextpageid'] = 'The next page ID';
$string['privacy:metadata:branch'] = 'A record of the pages viewed';
$string['privacy:metadata:overrides:userid'] = 'The user ID';
$string['privacy:metadata:overrides:available'] = 'The time when the lesson may be attempted';
$string['privacy:metadata:overrides:deadline'] = 'The deadline for completing the lesson.';
$string['privacy:metadata:overrides:timelimit'] = 'Time limit to complete the lesson, in seconds.';
$string['privacy:metadata:overrides:review'] = 'Whether trying a question again is allowed';
$string['privacy:metadata:overrides:maxattempts'] = 'The maximium number of attempts';
$string['privacy:metadata:overrides:retake'] = 'Whether re-takes are allowed';
$string['privacy:metadata:overrides:password'] = 'The password to access the lesson';
$string['privacy:metadata:overrides'] = 'A record of overrides per lesson';
$string['privacy:metadata:userpref:lessonview'] = 'The preferred display mode when editing lessons';
$string['privacy:path:essayresponses'] = 'Essay responses';
$string['privacy:path:essayanswers'] = 'Essay answers';
$string['privacy:path:pages'] = 'Pages';
$string['processerror'] = 'Error occurred during processing!';
$string['progressbar'] = 'Progress bar';
$string['progressbar_help'] = 'If enabled, a bar is displayed at the bottom of lesson pages showing approximate percentage of completion.';
$string['progresscompleted'] = 'You have completed {$a}% of the lesson';
$string['progressbarteacherwarning'] = 'Progress bar does not display for {$a}';
$string['progressbarteacherwarning2'] = 'You will not see the progress bar because you can edit this lesson';
$string['qtype'] = 'Page type';
$string['question'] = 'Question';
$string['questionoption'] = 'Question';
$string['questiontype'] = 'Question type';
$string['randombranch'] = 'Random content page';
$string['randompageinbranch'] = 'Random question within a content page';
$string['rank'] = 'Rank';
$string['rawgrade'] = 'Raw grade';
$string['receivedcredit'] = 'Received credit';
$string['redisplaypage'] = 'Redisplay page';
$string['removeallgroupoverrides'] = 'Delete all group overrides';
$string['removealluseroverrides'] = 'Delete all user overrides';
$string['report'] = 'Report';
$string['reports'] = 'Reports';
$string['response'] = 'Response';
$string['retakesallowed'] = 'Re-takes allowed';
$string['retakesallowed_help'] = 'If enabled, students can attempt the lesson more than once.';
$string['returnto'] = 'Return to {$a}';
$string['returntocourse'] = 'Return to the course';
$string['reverttodefaults'] = 'Revert to lesson defaults';
$string['review'] = 'Review';
$string['reviewlesson'] = 'Review lesson';
$string['reviewquestionback'] = 'Yes, I\'d like to try again';
$string['reviewquestioncontinue'] = 'No, I just want to go on to the next question';
$string['sanitycheckfailed'] = 'Sanity check failed: This attempt has been deleted';
$string['save'] = 'Save';
$string['savechanges'] = 'Save changes';
$string['savechangesandeol'] = 'Save all changes and go to the end of the lesson.';
$string['saveoverrideandstay'] = 'Save and enter another override';
$string['savepage'] = 'Save page';
$string['score'] = 'Score';
$string['score_help'] = 'If custom scoring is enabled in the lesson settings, a whole number value (positive or negative) may be entered for each answer.';
$string['scores'] = 'Scores';
$string['search:activity'] = 'Lesson - activity information';
$string['secondpluswrong'] = 'Not quite. Would you like to try again?';
$string['selectaqtype'] = 'Select a question type';
$string['selectallattempts'] = 'Select all attempts';
$string['selectreport'] = 'Select report';
$string['sent'] = 'Sent';
$string['shortanswer'] = 'Short answer';
$string['showanunansweredpage'] = 'Show an unanswered page';
$string['showanunseenpage'] = 'Show an unseen page';
$string['singleanswer'] = 'Single answer';
$string['skip'] = 'Skip navigation';
$string['slideshow'] = 'Slideshow';
$string['slideshow_help'] = 'If enabled, the lesson is displayed as a slideshow, with a fixed width and height.';
$string['slideshowbgcolor'] = 'Slideshow background colour';
$string['slideshowheight'] = 'Slideshow height';
$string['slideshowwidth'] = 'Slideshow width';
$string['startlesson'] = 'Start lesson';
$string['studentattemptlesson'] = '{$a->lastname}, {$a->firstname}\'s attempt number {$a->attempt}';
$string['studentname'] = '{$a} Name';
$string['studentoneminwarning'] = 'Warning: You have 1 minute or less to finish the lesson.';
$string['studentoutoftimeforreview'] = 'Attention: You ran out of time for reviewing this lesson';
$string['studentresponse'] = '{$a}\'s response';
$string['submit'] = 'Submit';
$string['submitname'] = 'Submit name';
$string['teacherjumpwarning'] = 'A {$a->cluster} jump or an {$a->unseen} jump is being used in this lesson. The next page jump will be used instead. Log in as a student to test these jumps.';
$string['teacherongoingwarning'] = 'The ongoing score is only displayed for the student. Log in as a student to test the ongoing score.';
$string['teachertimerwarning'] = 'Timer only works for students. Test the timer by logging in as a student.';
$string['thatsthecorrectanswer'] = 'That\'s the correct answer';
$string['thatsthewronganswer'] = 'That\'s the wrong answer';
$string['thefollowingpagesjumptothispage'] = 'The following pages jump to this page';
$string['thispage'] = 'This page';
$string['timeisup'] = 'Time is up';
$string['timelimit'] = 'Time limit';
$string['timelimit_help'] = 'If enabled, a warning about the time limit is displayed at the beginning of the lesson and a countdown timer is displayed. Any answers given after the time has elapsed are not graded.';
$string['timelimitwarning'] = 'You have {$a} to finish the lesson.';
$string['timeremaining'] = 'Time remaining';
$string['timespenterror'] = 'Spend at least {$a} minutes in the lesson';
$string['timespentminutes'] = 'Time spent (minutes)';
$string['timetaken'] = 'Time taken';
$string['totalpagesviewedheader'] = 'Number of pages viewed';
$string['true'] = 'True';
$string['truefalse'] = 'True/false';
$string['unabledtosavefile'] = 'The file you uploaded could not be saved';
$string['unknownqtypesnotimported'] = '{$a} questions with unsupported question types were not imported';
$string['unseenpageinbranch'] = 'Unseen question within a content page';
$string['unsupportedqtype'] = 'Unsupported question type ({$a})!';
$string['updatepagenamed'] = 'Update page: {$a}';
$string['updatedpage'] = 'Updated page';
$string['updatefailed'] = 'Update failed';
$string['usemaximum'] = 'Use maximum';
$string['usemean'] = 'Use mean';
$string['usepassword'] = 'Password protected lesson';
$string['usepassword_help'] = 'If enabled, a password is required in order to access the lesson.';
$string['useroverrides'] = 'User overrides';
$string['useroverridesdeleted'] = 'User overrides deleted';
$string['usersnone'] = 'No students have access to this lesson';
$string['viewessayanswers'] = 'View essay answers';
$string['viewgrades'] = 'View grades';
$string['viewreports'] = 'View {$a->attempts} completed {$a->student} attempts';
$string['viewreports2'] = 'View {$a} completed attempts';
$string['warning'] = 'Warning';
$string['welldone'] = 'Well done!';
$string['whatdofirst'] = 'What would you like to do first?';
$string['withselectedattempts'] = 'With selected attempts...';
$string['wronganswerjump'] = 'Wrong answer jump';
$string['wronganswerscore'] = 'Wrong answer score';
$string['wrongresponse'] = 'Wrong response';
$string['youhaveseen'] = 'You have seen more than one page of this lesson already.<br />Do you want to start at the last page you saw?';
$string['youranswer'] = 'Your answer';
$string['yourcurrentgradeis'] = 'Your current grade is {$a}';
$string['yourcurrentgradeisoutof'] = 'Your current grade is {$a->grade} out of {$a->total}';
$string['youshouldview'] = 'You should answer at least: {$a}';
// Deprecated since 4.3.
$string['completiontimespent'] = 'Student must do this activity at least for';
// Deprecated since Moodle 4.4.
$string['grade'] = 'Grade';
+143
View File
@@ -0,0 +1,143 @@
<?php
// 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/>.
/**
* Handles lesson actions
*
* ACTIONS handled are:
* confirmdelete
* delete
* move
* moveit
* duplicate
* @package mod_lesson
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
require_once("../../config.php");
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
$id = required_param('id', PARAM_INT); // Course Module ID
$action = required_param('action', PARAM_ALPHA); // Action
$pageid = required_param('pageid', PARAM_INT);
$cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST));
require_login($course, false, $cm);
$url = new moodle_url('/mod/lesson/lesson.php', array('id'=>$id,'action'=>$action));
$PAGE->set_url($url);
$context = context_module::instance($cm->id);
require_capability('mod/lesson:edit', $context);
require_sesskey();
$lessonoutput = $PAGE->get_renderer('mod_lesson');
/// Process the action
switch ($action) {
case 'confirmdelete':
$PAGE->navbar->add(get_string($action, 'lesson'));
$thispage = $lesson->load_page($pageid);
echo $lessonoutput->header($lesson, $cm, '', false, null, get_string('deletingpage', 'lesson', format_string($thispage->title)));
echo $OUTPUT->heading(get_string("deletingpage", "lesson", format_string($thispage->title)));
// print the jumps to this page
$params = array("lessonid" => $lesson->id, "pageid" => $pageid);
if ($answers = $DB->get_records_select("lesson_answers", "lessonid = :lessonid AND jumpto = :pageid + 1", $params)) {
echo $OUTPUT->heading(get_string("thefollowingpagesjumptothispage", "lesson"));
echo "<p align=\"center\">\n";
foreach ($answers as $answer) {
if (!$title = $DB->get_field("lesson_pages", "title", array("id" => $answer->pageid))) {
throw new \moodle_exception('cannotfindpagetitle', 'lesson');
}
echo $title."<br />\n";
}
}
echo $OUTPUT->confirm(get_string("confirmdeletionofthispage","lesson"),"lesson.php?action=delete&id=$cm->id&pageid=$pageid","view.php?id=$cm->id");
break;
case 'move':
$PAGE->navbar->add(get_string($action, 'lesson'));
$title = $DB->get_field("lesson_pages", "title", array("id" => $pageid));
echo $lessonoutput->header($lesson, $cm, '', false, null, get_string('moving', 'lesson', format_String($title)));
$headinglevel = $PAGE->activityheader->get_heading_level();
echo $OUTPUT->heading(get_string("moving", "lesson", format_string($title)), $headinglevel);
$params = array ("lessonid" => $lesson->id, "prevpageid" => 0);
if (!$page = $DB->get_record_select("lesson_pages", "lessonid = :lessonid AND prevpageid = :prevpageid", $params)) {
throw new \moodle_exception('cannotfindfirstpage', 'lesson');
}
echo html_writer::start_tag('div', array('class' => 'move-page'));
echo html_writer::start_tag('div', array('class' => 'available-position'));
$moveurl = "lesson.php?id=$cm->id&sesskey=".sesskey()."&action=moveit&pageid=$pageid&after=0";
echo html_writer::link($moveurl, get_string("movepagehere", "lesson"));
echo html_writer::end_tag('div');
while (true) {
if ($page->id != $pageid) {
if (!$title = trim(format_string($page->title))) {
$title = "<< ".get_string("notitle", "lesson")." >>";
}
echo html_writer::tag('div', $title, array('class' => 'page'));
echo html_writer::start_tag('div', array('class' => 'available-position'));
$moveurl = "lesson.php?id=$cm->id&sesskey=".sesskey()."&action=moveit&pageid=$pageid&after={$page->id}";
echo html_writer::link($moveurl, get_string("movepagehere", "lesson"));
echo html_writer::end_tag('div');
}
if ($page->nextpageid) {
if (!$page = $DB->get_record("lesson_pages", array("id" => $page->nextpageid))) {
throw new \moodle_exception('cannotfindnextpage', 'lesson');
}
} else {
// last page reached
break;
}
}
echo html_writer::end_tag('div');
break;
case 'delete':
$thispage = $lesson->load_page($pageid);
$thispage->delete();
redirect("$CFG->wwwroot/mod/lesson/edit.php?id=$cm->id");
break;
case 'moveit':
$after = (int)required_param('after', PARAM_INT); // target page
$lesson->resort_pages($pageid, $after);
redirect("$CFG->wwwroot/mod/lesson/edit.php?id=$cm->id");
break;
case 'duplicate':
$lesson->duplicate_page($pageid);
redirect(new moodle_url('/mod/lesson/edit.php', array('id' => $cm->id)));
break;
default:
throw new \moodle_exception('unknowaction');
break;
}
echo $lessonoutput->footer();
+1742
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+98
View File
@@ -0,0 +1,98 @@
<?php
// 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/>.
/**
* This file plays the mediafile set in lesson settings.
*
* If there is a way to use the resource class instead of this code, please change to do so
*
*
* @package mod_lesson
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
require_once('../../config.php');
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
$id = required_param('id', PARAM_INT); // Course Module ID
$printclose = optional_param('printclose', 0, PARAM_INT);
$cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST), $cm);
require_login($course, false, $cm);
// Apply overrides.
$lesson->update_effective_access($USER->id);
$context = $lesson->context;
$canmanage = $lesson->can_manage();
$url = new moodle_url('/mod/lesson/mediafile.php', array('id'=>$id));
if ($printclose !== '') {
$url->param('printclose', $printclose);
}
$PAGE->set_url($url);
$PAGE->set_pagelayout('popup');
$PAGE->set_title($course->shortname);
$lessonoutput = $PAGE->get_renderer('mod_lesson');
// Get the mimetype
$mimetype = mimeinfo("type", $lesson->mediafile);
if ($printclose) { // this is for framesets
if ($lesson->mediaclose) {
echo $lessonoutput->header($lesson, $cm);
echo $OUTPUT->box('<form><div><input type="button" onclick="top.close();" value="'.get_string("closewindow").'" /></div></form>', 'lessonmediafilecontrol');
echo $lessonoutput->footer();
}
exit();
}
// Check access restrictions.
if ($timerestriction = $lesson->get_time_restriction_status()) { // Deadline restrictions.
echo $lessonoutput->header($lesson, $cm, '', false, null, get_string('notavailable'));
echo $lessonoutput->lesson_inaccessible(get_string($timerestriction->reason, 'lesson', userdate($timerestriction->time)));
echo $lessonoutput->footer();
exit();
} else if ($passwordrestriction = $lesson->get_password_restriction_status(null)) { // Password protected lesson code.
echo $lessonoutput->header($lesson, $cm, '', false, null, get_string('passwordprotectedlesson', 'lesson', format_string($lesson->name)));
echo $lessonoutput->login_prompt($lesson, $userpassword !== '');
echo $lessonoutput->footer();
exit();
} else if ($dependenciesrestriction = $lesson->get_dependencies_restriction_status()) { // Check for dependencies.
echo $lessonoutput->header($lesson, $cm, '', false, null, get_string('completethefollowingconditions', 'lesson', format_string($lesson->name)));
echo $lessonoutput->dependancy_errors($dependenciesrestriction->dependentlesson, $dependenciesrestriction->errors);
echo $lessonoutput->footer();
exit();
}
echo $lessonoutput->header($lesson, $cm);
// print the embedded media html code
echo $OUTPUT->box(lesson_get_media_html($lesson, $context));
if ($lesson->mediaclose) {
echo '<div class="lessonmediafilecontrol">';
echo $OUTPUT->close_window_button();
echo '</div>';
}
echo $lessonoutput->footer();
+479
View File
@@ -0,0 +1,479 @@
<?php
// 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/>.
/**
* Form to define a new instance of lesson or edit an instance.
* It is used from /course/modedit.php.
*
* @package mod_lesson
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or late
**/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/course/moodleform_mod.php');
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
class mod_lesson_mod_form extends moodleform_mod {
protected $course = null;
public function __construct($current, $section, $cm, $course) {
$this->course = $course;
parent::__construct($current, $section, $cm, $course);
}
/**
* Old syntax of class constructor. Deprecated in PHP7.
*
* @deprecated since Moodle 3.1
*/
public function mod_lesson_mod_form($current, $section, $cm, $course) {
debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
self::__construct($current, $section, $cm, $course);
}
function definition() {
global $CFG, $COURSE, $DB, $OUTPUT;
$mform = $this->_form;
$lessonconfig = get_config('mod_lesson');
$mform->addElement('header', 'general', get_string('general', 'form'));
/** Legacy slideshow width element to maintain backwards compatibility */
$mform->addElement('hidden', 'width');
$mform->setType('width', PARAM_INT);
$mform->setDefault('width', $lessonconfig->slideshowwidth);
/** Legacy slideshow height element to maintain backwards compatibility */
$mform->addElement('hidden', 'height');
$mform->setType('height', PARAM_INT);
$mform->setDefault('height', $lessonconfig->slideshowheight);
/** Legacy slideshow background color element to maintain backwards compatibility */
$mform->addElement('hidden', 'bgcolor');
$mform->setType('bgcolor', PARAM_TEXT);
$mform->setDefault('bgcolor', $lessonconfig->slideshowbgcolor);
/** Legacy media popup width element to maintain backwards compatibility */
$mform->addElement('hidden', 'mediawidth');
$mform->setType('mediawidth', PARAM_INT);
$mform->setDefault('mediawidth', $lessonconfig->mediawidth);
/** Legacy media popup height element to maintain backwards compatibility */
$mform->addElement('hidden', 'mediaheight');
$mform->setType('mediaheight', PARAM_INT);
$mform->setDefault('mediaheight', $lessonconfig->mediaheight);
/** Legacy media popup close button element to maintain backwards compatibility */
$mform->addElement('hidden', 'mediaclose');
$mform->setType('mediaclose', PARAM_BOOL);
$mform->setDefault('mediaclose', $lessonconfig->mediaclose);
$mform->addElement('text', 'name', get_string('name'), array('size'=>'64'));
if (!empty($CFG->formatstringstriptags)) {
$mform->setType('name', PARAM_TEXT);
} else {
$mform->setType('name', PARAM_CLEANHTML);
}
$mform->addRule('name', null, 'required', null, 'client');
$mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$this->standard_intro_elements();
// Appearance.
$mform->addElement('header', 'appearancehdr', get_string('appearance'));
$filemanageroptions = array();
$filemanageroptions['filetypes'] = '*';
$filemanageroptions['maxbytes'] = $this->course->maxbytes;
$filemanageroptions['subdirs'] = 0;
$filemanageroptions['maxfiles'] = 1;
$mform->addElement('filemanager', 'mediafile', get_string('mediafile', 'lesson'), null, $filemanageroptions);
$mform->addHelpButton('mediafile', 'mediafile', 'lesson');
$mform->setAdvanced('mediafile', $lessonconfig->mediafile_adv);
$mform->addElement('selectyesno', 'progressbar', get_string('progressbar', 'lesson'));
$mform->addHelpButton('progressbar', 'progressbar', 'lesson');
$mform->setDefault('progressbar', $lessonconfig->progressbar);
$mform->setAdvanced('progressbar', $lessonconfig->progressbar_adv);
$mform->addElement('selectyesno', 'ongoing', get_string('ongoing', 'lesson'));
$mform->addHelpButton('ongoing', 'ongoing', 'lesson');
$mform->setDefault('ongoing', $lessonconfig->ongoing);
$mform->setAdvanced('ongoing', $lessonconfig->ongoing_adv);
$mform->addElement('selectyesno', 'displayleft', get_string('displayleftmenu', 'lesson'));
$mform->addHelpButton('displayleft', 'displayleftmenu', 'lesson');
$mform->setDefault('displayleft', $lessonconfig->displayleftmenu);
$mform->setAdvanced('displayleft', $lessonconfig->displayleftmenu_adv);
$options = array();
for($i = 100; $i >= 0; $i--) {
$options[$i] = $i.'%';
}
$mform->addElement('select', 'displayleftif', get_string('displayleftif', 'lesson'), $options);
$mform->addHelpButton('displayleftif', 'displayleftif', 'lesson');
$mform->setDefault('displayleftif', $lessonconfig->displayleftif);
$mform->setAdvanced('displayleftif', $lessonconfig->displayleftif_adv);
$mform->addElement('selectyesno', 'slideshow', get_string('slideshow', 'lesson'));
$mform->addHelpButton('slideshow', 'slideshow', 'lesson');
$mform->setDefault('slideshow', $lessonconfig->slideshow);
$mform->setAdvanced('slideshow', $lessonconfig->slideshow_adv);
$numbers = array();
for ($i = 20; $i > 1; $i--) {
$numbers[$i] = $i;
}
$mform->addElement('select', 'maxanswers', get_string('maximumnumberofanswersbranches', 'lesson'), $numbers);
$mform->setDefault('maxanswers', $lessonconfig->maxanswers);
$mform->setAdvanced('maxanswers', $lessonconfig->maxanswers_adv);
$mform->setType('maxanswers', PARAM_INT);
$mform->addHelpButton('maxanswers', 'maximumnumberofanswersbranches', 'lesson');
$mform->addElement('selectyesno', 'feedback', get_string('displaydefaultfeedback', 'lesson'));
$mform->addHelpButton('feedback', 'displaydefaultfeedback', 'lesson');
$mform->setDefault('feedback', $lessonconfig->defaultfeedback);
$mform->setAdvanced('feedback', $lessonconfig->defaultfeedback_adv);
// Get the modules.
if ($mods = get_course_mods($COURSE->id)) {
$modinstances = array();
foreach ($mods as $mod) {
// Get the module name and then store it in a new array.
if ($module = get_coursemodule_from_instance($mod->modname, $mod->instance, $COURSE->id)) {
// Exclude this lesson, if it's already been saved.
if (!isset($this->_cm->id) || $this->_cm->id != $mod->id) {
$modinstances[$mod->id] = get_string('pluginname', $mod->modname) . ' - ' . format_string(
$module->name,
true,
[
'context' => $this->context,
],
);
}
}
}
asort($modinstances); // Sort by module name.
$modinstances=array(0=>get_string('none'))+$modinstances;
$mform->addElement('select', 'activitylink', get_string('activitylink', 'lesson'), $modinstances);
$mform->addHelpButton('activitylink', 'activitylink', 'lesson');
$mform->setDefault('activitylink', 0);
$mform->setAdvanced('activitylink', $lessonconfig->activitylink_adv);
}
// Availability.
$mform->addElement('header', 'availabilityhdr', get_string('availability'));
$mform->addElement('date_time_selector', 'available', get_string('available', 'lesson'), array('optional'=>true));
$mform->setDefault('available', 0);
$mform->addElement('date_time_selector', 'deadline', get_string('deadline', 'lesson'), array('optional'=>true));
$mform->setDefault('deadline', 0);
// Time limit.
$mform->addElement('duration', 'timelimit', get_string('timelimit', 'lesson'),
array('optional' => true));
$mform->addHelpButton('timelimit', 'timelimit', 'lesson');
$mform->setAdvanced('timelimit', $lessonconfig->timelimit_adv);
$mform->setDefault('timelimit', $lessonconfig->timelimit);
$mform->addElement('selectyesno', 'usepassword', get_string('usepassword', 'lesson'));
$mform->addHelpButton('usepassword', 'usepassword', 'lesson');
$mform->setDefault('usepassword', $lessonconfig->password);
$mform->setAdvanced('usepassword', $lessonconfig->password_adv);
$mform->addElement('passwordunmask', 'password', get_string('password', 'lesson'));
$mform->setDefault('password', '');
$mform->setAdvanced('password', $lessonconfig->password_adv);
$mform->setType('password', PARAM_RAW);
$mform->hideIf('password', 'usepassword', 'eq', 0);
$mform->hideIf('passwordunmask', 'usepassword', 'eq', 0);
// Dependent on.
if ($this->current && isset($this->current->dependency) && $this->current->dependency) {
$mform->addElement('header', 'dependencyon', get_string('prerequisitelesson', 'lesson'));
$mform->addElement('static', 'warningobsolete',
get_string('warning', 'lesson'),
get_string('prerequisiteisobsolete', 'lesson'));
$options = array(0 => get_string('none'));
if ($lessons = get_all_instances_in_course('lesson', $COURSE)) {
foreach ($lessons as $lesson) {
if ($lesson->id != $this->_instance) {
$options[$lesson->id] = format_string($lesson->name, true);
}
}
}
$mform->addElement('select', 'dependency', get_string('dependencyon', 'lesson'), $options);
$mform->addHelpButton('dependency', 'dependencyon', 'lesson');
$mform->setDefault('dependency', 0);
$mform->addElement('text', 'timespent', get_string('timespentminutes', 'lesson'));
$mform->setDefault('timespent', 0);
$mform->setType('timespent', PARAM_INT);
$mform->disabledIf('timespent', 'dependency', 'eq', 0);
$mform->addElement('checkbox', 'completed', get_string('completed', 'lesson'));
$mform->setDefault('completed', 0);
$mform->disabledIf('completed', 'dependency', 'eq', 0);
$mform->addElement('text', 'gradebetterthan', get_string('gradebetterthan', 'lesson'));
$mform->setDefault('gradebetterthan', 0);
$mform->setType('gradebetterthan', PARAM_INT);
$mform->disabledIf('gradebetterthan', 'dependency', 'eq', 0);
} else {
$mform->addElement('hidden', 'dependency', 0);
$mform->setType('dependency', PARAM_INT);
$mform->addElement('hidden', 'timespent', 0);
$mform->setType('timespent', PARAM_INT);
$mform->addElement('hidden', 'completed', 0);
$mform->setType('completed', PARAM_INT);
$mform->addElement('hidden', 'gradebetterthan', 0);
$mform->setType('gradebetterthan', PARAM_INT);
$mform->setConstants(array('dependency' => 0, 'timespent' => 0,
'completed' => 0, 'gradebetterthan' => 0));
}
// Allow to enable offline lessons only if the Mobile services are enabled.
if ($CFG->enablemobilewebservice) {
$mform->addElement('selectyesno', 'allowofflineattempts', get_string('allowofflineattempts', 'lesson'));
$mform->addHelpButton('allowofflineattempts', 'allowofflineattempts', 'lesson');
$mform->setDefault('allowofflineattempts', 0);
$mform->setAdvanced('allowofflineattempts');
$mform->disabledIf('allowofflineattempts', 'timelimit[number]', 'neq', 0);
$mform->addElement('static', 'allowofflineattemptswarning', '',
$OUTPUT->notification(get_string('allowofflineattempts_help', 'lesson'), 'warning'));
$mform->setAdvanced('allowofflineattemptswarning');
} else {
$mform->addElement('hidden', 'allowofflineattempts', 0);
$mform->setType('allowofflineattempts', PARAM_INT);
}
// Flow control.
$mform->addElement('header', 'flowcontrol', get_string('flowcontrol', 'lesson'));
$mform->addElement('selectyesno', 'modattempts', get_string('modattempts', 'lesson'));
$mform->addHelpButton('modattempts', 'modattempts', 'lesson');
$mform->setDefault('modattempts', $lessonconfig->modattempts);
$mform->setAdvanced('modattempts', $lessonconfig->modattempts_adv);
$mform->addElement('selectyesno', 'review', get_string('displayreview', 'lesson'));
$mform->addHelpButton('review', 'displayreview', 'lesson');
$mform->setDefault('review', $lessonconfig->displayreview);
$mform->setAdvanced('review', $lessonconfig->displayreview_adv);
$numbers = array('0' => get_string('unlimited'));
for ($i = 10; $i > 0; $i--) {
$numbers[$i] = $i;
}
$mform->addElement('select', 'maxattempts', get_string('maximumnumberofattempts', 'lesson'), $numbers);
$mform->addHelpButton('maxattempts', 'maximumnumberofattempts', 'lesson');
$mform->setDefault('maxattempts', $lessonconfig->maximumnumberofattempts);
$mform->setAdvanced('maxattempts', $lessonconfig->maximumnumberofattempts_adv);
$defaultnextpages = array();
$defaultnextpages[0] = get_string('normal', 'lesson');
$defaultnextpages[LESSON_UNSEENPAGE] = get_string('showanunseenpage', 'lesson');
$defaultnextpages[LESSON_UNANSWEREDPAGE] = get_string('showanunansweredpage', 'lesson');
$mform->addElement('select', 'nextpagedefault', get_string('actionaftercorrectanswer', 'lesson'), $defaultnextpages);
$mform->addHelpButton('nextpagedefault', 'actionaftercorrectanswer', 'lesson');
$mform->setDefault('nextpagedefault', $lessonconfig->defaultnextpage);
$mform->setAdvanced('nextpagedefault', $lessonconfig->defaultnextpage_adv);
$numbers = array();
for ($i = 100; $i >= 0; $i--) {
$numbers[$i] = $i;
}
$mform->addElement('select', 'maxpages', get_string('numberofpagestoshow', 'lesson'), $numbers);
$mform->addHelpButton('maxpages', 'numberofpagestoshow', 'lesson');
$mform->setDefault('maxpages', $lessonconfig->numberofpagestoshow);
$mform->setAdvanced('maxpages', $lessonconfig->numberofpagestoshow_adv);
// Grade.
$this->standard_grading_coursemodule_elements();
// No header here, so that the following settings are displayed in the grade section.
$mform->addElement('selectyesno', 'practice', get_string('practice', 'lesson'));
$mform->addHelpButton('practice', 'practice', 'lesson');
$mform->setDefault('practice', $lessonconfig->practice);
$mform->setAdvanced('practice', $lessonconfig->practice_adv);
$mform->addElement('selectyesno', 'custom', get_string('customscoring', 'lesson'));
$mform->addHelpButton('custom', 'customscoring', 'lesson');
$mform->setDefault('custom', $lessonconfig->customscoring);
$mform->setAdvanced('custom', $lessonconfig->customscoring_adv);
$mform->addElement('selectyesno', 'retake', get_string('retakesallowed', 'lesson'));
$mform->addHelpButton('retake', 'retakesallowed', 'lesson');
$mform->setDefault('retake', $lessonconfig->retakesallowed);
$mform->setAdvanced('retake', $lessonconfig->retakesallowed_adv);
$options = array();
$options[0] = get_string('usemean', 'lesson');
$options[1] = get_string('usemaximum', 'lesson');
$mform->addElement('select', 'usemaxgrade', get_string('handlingofretakes', 'lesson'), $options);
$mform->addHelpButton('usemaxgrade', 'handlingofretakes', 'lesson');
$mform->setDefault('usemaxgrade', $lessonconfig->handlingofretakes);
$mform->setAdvanced('usemaxgrade', $lessonconfig->handlingofretakes_adv);
$mform->hideIf('usemaxgrade', 'retake', 'eq', '0');
$numbers = array();
for ($i = 100; $i >= 0; $i--) {
$numbers[$i] = $i;
}
$mform->addElement('select', 'minquestions', get_string('minimumnumberofquestions', 'lesson'), $numbers);
$mform->addHelpButton('minquestions', 'minimumnumberofquestions', 'lesson');
$mform->setDefault('minquestions', $lessonconfig->minimumnumberofquestions);
$mform->setAdvanced('minquestions', $lessonconfig->minimumnumberofquestions_adv);
//-------------------------------------------------------------------------------
$this->standard_coursemodule_elements();
//-------------------------------------------------------------------------------
// buttons
$this->add_action_buttons();
}
/**
* Enforce defaults here
*
* @param array $defaultvalues Form defaults
* @return void
**/
public function data_preprocessing(&$defaultvalues) {
if (isset($defaultvalues['conditions'])) {
$conditions = unserialize_object($defaultvalues['conditions']);
$defaultvalues['timespent'] = $conditions->timespent ?? 0;
$defaultvalues['completed'] = !empty($conditions->completed);
$defaultvalues['gradebetterthan'] = $conditions->gradebetterthan ?? 0;
}
// Set up the completion checkbox which is not part of standard data.
$suffix = $this->get_suffix();
$completiontimespentenabledel = 'completiontimespentenabled' . $suffix;
$completiontimespentel = 'completiontimespent' . $suffix;
$defaultvalues[$completiontimespentenabledel] = !empty($defaultvalues[$completiontimespentel]) ? 1 : 0;
if ($this->current->instance) {
// Editing existing instance - copy existing files into draft area.
$draftitemid = file_get_submitted_draft_itemid('mediafile');
file_prepare_draft_area($draftitemid, $this->context->id, 'mod_lesson', 'mediafile', 0, array('subdirs'=>0, 'maxbytes' => $this->course->maxbytes, 'maxfiles' => 1));
$defaultvalues['mediafile'] = $draftitemid;
}
}
/**
* Enforce validation rules here
*
* @param object $data Post data to validate
* @return array
**/
function validation($data, $files) {
$errors = parent::validation($data, $files);
// Check open and close times are consistent.
if ($data['available'] != 0 && $data['deadline'] != 0 &&
$data['deadline'] < $data['available']) {
$errors['deadline'] = get_string('closebeforeopen', 'lesson');
}
if (!empty($data['usepassword']) && empty($data['password'])) {
$errors['password'] = get_string('emptypassword', 'lesson');
}
return $errors;
}
/**
* Display module-specific activity completion rules.
* Part of the API defined by moodleform_mod
* @return array Array of string IDs of added items, empty array if none
*/
public function add_completion_rules() {
$mform = $this->_form;
$suffix = $this->get_suffix();
$completionendreachedel = 'completionendreached' . $suffix;
$mform->addElement(
'checkbox', $completionendreachedel,
'',
get_string('completionendreached', 'lesson')
);
// Enable this completion rule by default.
$mform->setDefault($completionendreachedel, 1);
$group = [];
$completiontimespentenabledel = 'completiontimespentenabled' . $suffix;
$group[] =& $mform->createElement(
'checkbox',
$completiontimespentenabledel,
'',
get_string('completiontimespentgroup', 'lesson')
);
$completiontimespentel = 'completiontimespent' . $suffix;
$group[] =& $mform->createElement('duration', $completiontimespentel, '', ['optional' => false]);
$completiontimespentgroupel = 'completiontimespentgroup' . $suffix;
$mform->addGroup($group, $completiontimespentgroupel, '', ' ', false);
$mform->hideIf($completiontimespentel . '[number]', $completiontimespentenabledel, 'notchecked');
$mform->hideIf($completiontimespentel . '[timeunit]', $completiontimespentenabledel, 'notchecked');
return [$completionendreachedel, $completiontimespentgroupel];
}
/**
* Called during validation. Indicates whether a module-specific completion rule is selected.
*
* @param array $data Input data (not yet validated)
* @return bool True if one or more rules is enabled, false if none are.
*/
public function completion_rule_enabled($data) {
$suffix = $this->get_suffix();
return !empty($data['completionendreached' . $suffix]) || $data['completiontimespent' . $suffix] > 0;
}
/**
* Allows module to modify the data returned by form get_data().
* This method is also called in the bulk activity completion form.
*
* Only available on moodleform_mod.
*
* @param stdClass $data the form data to be modified.
*/
public function data_postprocessing($data) {
parent::data_postprocessing($data);
// Turn off completion setting if the checkbox is not ticked.
if (!empty($data->completionunlocked)) {
$suffix = $this->get_suffix();
$completion = $data->{'completion' . $suffix};
$autocompletion = !empty($completion) && $completion == COMPLETION_TRACKING_AUTOMATIC;
if (empty($data->{'completiontimespentenabled' . $suffix}) || !$autocompletion) {
$data->{'completiontimespent' . $suffix} = 0;
}
if (empty($data->{'completionendreached' . $suffix}) || !$autocompletion) {
$data->{'completionendreached' . $suffix} = 0;
}
}
}
}
+297
View File
@@ -0,0 +1,297 @@
<?php
// 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/>.
/**
* Settings form for overrides in the lesson module.
*
* @package mod_lesson
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/formslib.php');
require_once($CFG->dirroot . '/mod/lesson/mod_form.php');
/**
* Form for editing settings overrides.
*
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class lesson_override_form extends moodleform {
/** @var object course module object. */
protected $cm;
/** @var object the lesson settings object. */
protected $lesson;
/** @var context the lesson context. */
protected $context;
/** @var bool editing group override (true) or user override (false). */
protected $groupmode;
/** @var int groupid, if provided. */
protected $groupid;
/** @var int userid, if provided. */
protected $userid;
/**
* Constructor.
* @param moodle_url $submiturl the form action URL.
* @param object $cm course module object.
* @param object $lesson the lesson settings object.
* @param object $context the lesson context.
* @param bool $groupmode editing group override (true) or user override (false).
* @param object $override the override being edited, if it already exists.
*/
public function __construct($submiturl, $cm, $lesson, $context, $groupmode, $override) {
$this->cm = $cm;
$this->lesson = $lesson;
$this->context = $context;
$this->groupmode = $groupmode;
$this->groupid = empty($override->groupid) ? 0 : $override->groupid;
$this->userid = empty($override->userid) ? 0 : $override->userid;
parent::__construct($submiturl, null, 'post');
}
/**
* Define this form - called by the parent constructor
*/
protected function definition() {
global $DB;
$cm = $this->cm;
$mform = $this->_form;
$mform->addElement('header', 'override', get_string('override', 'lesson'));
$lessongroupmode = groups_get_activity_groupmode($cm);
$accessallgroups = ($lessongroupmode == NOGROUPS) || has_capability('moodle/site:accessallgroups', $this->context);
if ($this->groupmode) {
// Group override.
if ($this->groupid) {
// There is already a groupid, so freeze the selector.
$groupchoices = [
$this->groupid => format_string(groups_get_group_name($this->groupid), true, ['context' => $this->context]),
];
$mform->addElement('select', 'groupid',
get_string('overridegroup', 'lesson'), $groupchoices);
$mform->freeze('groupid');
} else {
// Prepare the list of groups.
// Only include the groups the current can access.
$groups = $accessallgroups ? groups_get_all_groups($cm->course) : groups_get_activity_allowed_groups($cm);
if (empty($groups)) {
// Generate an error.
$link = new moodle_url('/mod/lesson/overrides.php', array('cmid' => $cm->id));
throw new \moodle_exception('groupsnone', 'lesson', $link);
}
$groupchoices = array();
foreach ($groups as $group) {
if ($group->visibility != GROUPS_VISIBILITY_NONE) {
$groupchoices[$group->id] = format_string($group->name, true, ['context' => $this->context]);
}
}
unset($groups);
if (count($groupchoices) == 0) {
$groupchoices[0] = get_string('none');
}
$mform->addElement('select', 'groupid',
get_string('overridegroup', 'lesson'), $groupchoices);
$mform->addRule('groupid', get_string('required'), 'required', null, 'client');
}
} else {
// User override.
if ($this->userid) {
// There is already a userid, so freeze the selector.
$user = $DB->get_record('user', array('id' => $this->userid));
$userchoices = array();
$userchoices[$this->userid] = fullname($user);
$mform->addElement('select', 'userid',
get_string('overrideuser', 'lesson'), $userchoices);
$mform->freeze('userid');
} else {
// Prepare the list of users.
$users = [];
list($sort) = users_order_by_sql('u');
// Get the list of appropriate users, depending on whether and how groups are used.
$userfieldsapi = \core_user\fields::for_name();
$userfields = 'u.id, u.email, ' . $userfieldsapi->get_sql('u', false, '', '', false)->selects;
$groupids = 0;
if (!$accessallgroups) {
$groups = groups_get_activity_allowed_groups($cm);
$groupids = array_keys($groups);
}
$users = get_enrolled_users($this->context, '',
$groupids, $userfields, $sort);
// Filter users based on any fixed restrictions (groups, profile).
$info = new \core_availability\info_module($cm);
$users = $info->filter_user_list($users);
if (empty($users)) {
// Generate an error.
$link = new moodle_url('/mod/lesson/overrides.php', array('cmid' => $cm->id));
throw new \moodle_exception('usersnone', 'lesson', $link);
}
$userchoices = array();
// TODO Does not support custom user profile fields (MDL-70456).
$canviewemail = in_array('email', \core_user\fields::get_identity_fields($this->context, false));
foreach ($users as $id => $user) {
if (empty($invalidusers[$id]) || (!empty($override) &&
$id == $override->userid)) {
if ($canviewemail) {
$userchoices[$id] = fullname($user) . ', ' . $user->email;
} else {
$userchoices[$id] = fullname($user);
}
}
}
unset($users);
if (count($userchoices) == 0) {
$userchoices[0] = get_string('none');
}
$mform->addElement('searchableselector', 'userid',
get_string('overrideuser', 'lesson'), $userchoices);
$mform->addRule('userid', get_string('required'), 'required', null, 'client');
}
}
// Password.
// This field has to be above the date and timelimit fields,
// otherwise browsers will clear it when those fields are changed.
$mform->addElement('passwordunmask', 'password', get_string('usepassword', 'lesson'));
$mform->setType('password', PARAM_TEXT);
$mform->addHelpButton('password', 'usepassword', 'lesson');
$mform->setDefault('password', $this->lesson->password);;
// Open and close dates.
$mform->addElement('date_time_selector', 'available', get_string('available', 'lesson'), array('optional' => true));
$mform->setDefault('available', $this->lesson->available);
$mform->addElement('date_time_selector', 'deadline', get_string('deadline', 'lesson'), array('optional' => true));
$mform->setDefault('deadline', $this->lesson->deadline);
// Lesson time limit.
$mform->addElement('duration', 'timelimit',
get_string('timelimit', 'lesson'), array('optional' => true));
if ($this->lesson->timelimit != 0) {
$mform->setDefault('timelimit', 0);
} else {
$mform->setDefault('timelimit', $this->lesson->timelimit);
}
// Try a question again.
$mform->addElement('selectyesno', 'review', get_string('displayreview', 'lesson'));
$mform->addHelpButton('review', 'displayreview', 'lesson');
$mform->setDefault('review', $this->lesson->review);
// Number of attempts.
$numbers = ['0' => get_string('unlimited')];
for ($i = 10; $i > 0; $i--) {
$numbers[$i] = $i;
}
$mform->addElement('select', 'maxattempts', get_string('maximumnumberofattempts', 'lesson'), $numbers);
$mform->addHelpButton('maxattempts', 'maximumnumberofattempts', 'lesson');
$mform->setDefault('maxattempts', $this->lesson->maxattempts);
// Retake allowed.
$mform->addElement('selectyesno', 'retake', get_string('retakesallowed', 'lesson'));
$mform->addHelpButton('retake', 'retakesallowed', 'lesson');
$mform->setDefault('retake', $this->lesson->retake);
// Submit buttons.
$mform->addElement('submit', 'resetbutton',
get_string('reverttodefaults', 'lesson'));
$buttonarray = array();
$buttonarray[] = $mform->createElement('submit', 'submitbutton',
get_string('save', 'lesson'));
$buttonarray[] = $mform->createElement('submit', 'againbutton',
get_string('saveoverrideandstay', 'lesson'));
$buttonarray[] = $mform->createElement('cancel');
$mform->addGroup($buttonarray, 'buttonbar', '', array(' '), false);
$mform->closeHeaderBefore('buttonbar');
}
/**
* Validate the submitted form data.
*
* @param array $data array of ("fieldname"=>value) of submitted data
* @param array $files array of uploaded files "element_name"=>tmp_file_path
* @return array of "element_name"=>"error_description" if there are errors
*/
public function validation($data, $files) {
$errors = parent::validation($data, $files);
$mform =& $this->_form;
$lesson = $this->lesson;
if ($mform->elementExists('userid')) {
if (empty($data['userid'])) {
$errors['userid'] = get_string('required');
}
}
if ($mform->elementExists('groupid')) {
if (empty($data['groupid'])) {
$errors['groupid'] = get_string('required');
}
}
// Ensure that the dates make sense.
if (!empty($data['available']) && !empty($data['deadline'])) {
if ($data['deadline'] < $data['available'] ) {
$errors['deadline'] = get_string('closebeforeopen', 'lesson');
}
}
// Ensure that at least one lesson setting was changed.
$changed = false;
$keys = array('available', 'deadline', 'review', 'timelimit', 'maxattempts', 'retake', 'password');
foreach ($keys as $key) {
if ($data[$key] != $lesson->{$key}) {
$changed = true;
break;
}
}
if (!$changed) {
$errors['available'] = get_string('nooverridedata', 'lesson');
}
return $errors;
}
}
+106
View File
@@ -0,0 +1,106 @@
<?php
// 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/>.
/**
* This page handles deleting lesson overrides
*
* @package mod_lesson
* @copyright 2015 Jean-Michel vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../config.php');
require_once($CFG->dirroot.'/mod/lesson/lib.php');
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
require_once($CFG->dirroot.'/mod/lesson/override_form.php');
$overrideid = required_param('id', PARAM_INT);
$confirm = optional_param('confirm', false, PARAM_BOOL);
if (! $override = $DB->get_record('lesson_overrides', array('id' => $overrideid))) {
throw new \moodle_exception('invalidoverrideid', 'lesson');
}
$lesson = new lesson($DB->get_record('lesson', array('id' => $override->lessonid), '*', MUST_EXIST));
if (! $cm = get_coursemodule_from_instance("lesson", $lesson->id, $lesson->course)) {
throw new \moodle_exception('invalidcoursemodule');
}
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
$context = context_module::instance($cm->id);
require_login($course, false, $cm);
// Check the user has the required capabilities to modify an override.
require_capability('mod/lesson:manageoverrides', $context);
if ($override->groupid) {
if (!groups_group_visible($override->groupid, $course, $cm)) {
throw new \moodle_exception('invalidoverrideid', 'lesson');
}
} else {
if (!groups_user_groups_visible($course, $override->userid, $cm)) {
throw new \moodle_exception('invalidoverrideid', 'lesson');
}
}
$url = new moodle_url('/mod/lesson/overridedelete.php', array('id' => $override->id));
$confirmurl = new moodle_url($url, array('id' => $override->id, 'confirm' => 1));
$cancelurl = new moodle_url('/mod/lesson/overrides.php', array('cmid' => $cm->id));
if (!empty($override->userid)) {
$cancelurl->param('mode', 'user');
}
// If confirm is set (PARAM_BOOL) then we have confirmation of intention to delete.
if ($confirm) {
require_sesskey();
$lesson->delete_override($override->id);
redirect($cancelurl);
}
// Prepare the page to show the confirmation form.
$stroverride = get_string('override', 'lesson');
$title = get_string('deletecheck', null, $stroverride);
$PAGE->set_url($url);
$PAGE->set_pagelayout('admin');
$PAGE->add_body_class('limitedwidth');
$PAGE->navbar->add($title);
$PAGE->set_title($title);
$PAGE->set_heading($course->fullname);
echo $OUTPUT->header();
echo $OUTPUT->heading(format_string($lesson->name, true, array('context' => $context)));
if ($override->groupid) {
$group = $DB->get_record('groups', array('id' => $override->groupid), 'id, name');
$confirmstr = get_string("overridedeletegroupsure", "lesson", format_string($group->name, true, ['context' => $context]));
} else {
$userfieldsapi = \core_user\fields::for_name();
$namefields = $userfieldsapi->get_sql('', false, '', '', false)->selects;
$user = $DB->get_record('user', array('id' => $override->userid),
'id, ' . $namefields);
$confirmstr = get_string("overridedeleteusersure", "lesson", fullname($user));
}
echo $OUTPUT->confirm($confirmstr, $confirmurl, $cancelurl);
echo $OUTPUT->footer();
+247
View File
@@ -0,0 +1,247 @@
<?php
// 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/>.
/**
* This page handles editing and creation of lesson overrides
*
* @package mod_lesson
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../config.php');
require_once($CFG->dirroot.'/mod/lesson/lib.php');
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
require_once($CFG->dirroot.'/mod/lesson/override_form.php');
$cmid = optional_param('cmid', 0, PARAM_INT);
$overrideid = optional_param('id', 0, PARAM_INT);
$action = optional_param('action', null, PARAM_ALPHA);
$reset = optional_param('reset', false, PARAM_BOOL);
$override = null;
if ($overrideid) {
if (! $override = $DB->get_record('lesson_overrides', array('id' => $overrideid))) {
throw new \moodle_exception('invalidoverrideid', 'lesson');
}
$lesson = new lesson($DB->get_record('lesson', array('id' => $override->lessonid), '*', MUST_EXIST));
list($course, $cm) = get_course_and_cm_from_instance($lesson, 'lesson');
} else if ($cmid) {
list($course, $cm) = get_course_and_cm_from_cmid($cmid, 'lesson');
$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST));
} else {
throw new \moodle_exception('invalidcoursemodule');
}
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
$url = new moodle_url('/mod/lesson/overrideedit.php');
if ($action) {
$url->param('action', $action);
}
if ($overrideid) {
$url->param('id', $overrideid);
} else {
$url->param('cmid', $cmid);
}
$PAGE->set_url($url);
require_login($course, false, $cm);
$context = context_module::instance($cm->id);
// Add or edit an override.
require_capability('mod/lesson:manageoverrides', $context);
if ($overrideid) {
// Editing an override.
$data = clone $override;
if ($override->groupid) {
if (!groups_group_visible($override->groupid, $course, $cm)) {
throw new \moodle_exception('invalidoverrideid', 'lesson');
}
} else {
if (!groups_user_groups_visible($course, $override->userid, $cm)) {
throw new \moodle_exception('invalidoverrideid', 'lesson');
}
}
} else {
// Creating a new override.
$data = new stdClass();
}
// Merge lesson defaults with data.
$keys = array('available', 'deadline', 'review', 'timelimit', 'maxattempts', 'retake', 'password');
foreach ($keys as $key) {
if (!isset($data->{$key}) || $reset) {
$data->{$key} = $lesson->{$key};
}
}
// True if group-based override.
$groupmode = !empty($data->groupid) || ($action === 'addgroup' && empty($overrideid));
// If we are duplicating an override, then clear the user/group and override id
// since they will change.
if ($action === 'duplicate') {
$override->id = $data->id = null;
$override->userid = $data->userid = null;
$override->groupid = $data->groupid = null;
}
$overridelisturl = new moodle_url('/mod/lesson/overrides.php', array('cmid' => $cm->id));
if (!$groupmode) {
$overridelisturl->param('mode', 'user');
}
// Setup the form.
$mform = new lesson_override_form($url, $cm, $lesson, $context, $groupmode, $override);
$mform->set_data($data);
if ($mform->is_cancelled()) {
redirect($overridelisturl);
} else if (optional_param('resetbutton', 0, PARAM_ALPHA)) {
$url->param('reset', true);
redirect($url);
} else if ($fromform = $mform->get_data()) {
// Process the data.
$fromform->lessonid = $lesson->id;
// Replace unchanged values with null.
foreach ($keys as $key) {
if ($fromform->{$key} == $lesson->{$key}) {
$fromform->{$key} = null;
}
}
// See if we are replacing an existing override.
$userorgroupchanged = false;
if (empty($override->id)) {
$userorgroupchanged = true;
} else if (!empty($fromform->userid)) {
$userorgroupchanged = $fromform->userid !== $override->userid;
} else {
$userorgroupchanged = $fromform->groupid !== $override->groupid;
}
if ($userorgroupchanged) {
$conditions = array(
'lessonid' => $lesson->id,
'userid' => empty($fromform->userid) ? null : $fromform->userid,
'groupid' => empty($fromform->groupid) ? null : $fromform->groupid);
if ($oldoverride = $DB->get_record('lesson_overrides', $conditions)) {
// There is an old override, so we merge any new settings on top of
// the older override.
foreach ($keys as $key) {
if (is_null($fromform->{$key})) {
$fromform->{$key} = $oldoverride->{$key};
}
}
$lesson->delete_override($oldoverride->id);
}
}
// Set the common parameters for one of the events we may be triggering.
$params = array(
'context' => $context,
'other' => array(
'lessonid' => $lesson->id
)
);
if (!empty($override->id)) {
$fromform->id = $override->id;
$DB->update_record('lesson_overrides', $fromform);
$cachekey = $groupmode ? "{$fromform->lessonid}_g_{$fromform->groupid}" : "{$fromform->lessonid}_u_{$fromform->userid}";
cache::make('mod_lesson', 'overrides')->delete($cachekey);
// Determine which override updated event to fire.
$params['objectid'] = $override->id;
if (!$groupmode) {
$params['relateduserid'] = $fromform->userid;
$event = \mod_lesson\event\user_override_updated::create($params);
} else {
$params['other']['groupid'] = $fromform->groupid;
$event = \mod_lesson\event\group_override_updated::create($params);
}
// Trigger the override updated event.
$event->trigger();
} else {
unset($fromform->id);
$fromform->id = $DB->insert_record('lesson_overrides', $fromform);
$cachekey = $groupmode ? "{$fromform->lessonid}_g_{$fromform->groupid}" : "{$fromform->lessonid}_u_{$fromform->userid}";
cache::make('mod_lesson', 'overrides')->delete($cachekey);
// Determine which override created event to fire.
$params['objectid'] = $fromform->id;
if (!$groupmode) {
$params['relateduserid'] = $fromform->userid;
$event = \mod_lesson\event\user_override_created::create($params);
} else {
$params['other']['groupid'] = $fromform->groupid;
$event = \mod_lesson\event\group_override_created::create($params);
}
// Trigger the override created event.
$event->trigger();
}
if ($groupmode) {
// Priorities may have shifted, so we need to update all of the calendar events for group overrides.
lesson_update_events($lesson);
} else {
// User override. We only need to update the calendar event for this user override.
lesson_update_events($lesson, $fromform);
}
if (!empty($fromform->submitbutton)) {
redirect($overridelisturl);
}
// The user pressed the 'again' button, so redirect back to this page.
$url->remove_params('cmid');
$url->param('action', 'duplicate');
$url->param('id', $fromform->id);
redirect($url);
}
// Print the form.
$pagetitle = get_string('editoverride', 'lesson');
$PAGE->navbar->add($pagetitle);
$PAGE->set_pagelayout('admin');
$PAGE->set_secondary_active_tab('mod_lesson_useroverrides');
$PAGE->add_body_class('limitedwidth');
$PAGE->set_title($pagetitle);
$PAGE->set_heading($course->fullname);
echo $OUTPUT->header();
echo $OUTPUT->heading(format_string($lesson->name, true, array('context' => $context)));
$mform->display();
echo $OUTPUT->footer();
+353
View File
@@ -0,0 +1,353 @@
<?php
// 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/>.
/**
* This page handles listing of lesson overrides
*
* @package mod_lesson
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../config.php');
require_once($CFG->dirroot.'/mod/lesson/lib.php');
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
require_once($CFG->dirroot.'/mod/lesson/override_form.php');
$cmid = required_param('cmid', PARAM_INT);
$mode = optional_param('mode', '', PARAM_ALPHA); // One of 'user' or 'group', default is 'group'.
list($course, $cm) = get_course_and_cm_from_cmid($cmid, 'lesson');
$lesson = $DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST);
require_login($course, false, $cm);
$context = context_module::instance($cm->id);
// Check the user has the required capabilities to list overrides.
require_capability('mod/lesson:manageoverrides', $context);
$lessongroupmode = groups_get_activity_groupmode($cm);
$accessallgroups = ($lessongroupmode == NOGROUPS) || has_capability('moodle/site:accessallgroups', $context);
// Get the course groups that the current user can access.
$groups = $accessallgroups ? groups_get_all_groups($cm->course) : groups_get_activity_allowed_groups($cm);
// Default mode is "group", unless there are no groups.
if ($mode != "user" and $mode != "group") {
if (!empty($groups)) {
$mode = "group";
} else {
$mode = "user";
}
}
$groupmode = ($mode == "group");
$url = new moodle_url('/mod/lesson/overrides.php', array('cmid' => $cm->id, 'mode' => $mode));
$PAGE->set_url($url);
// Display a list of overrides.
$PAGE->set_pagelayout('admin');
$PAGE->add_body_class('limitedwidth');
$PAGE->set_title(get_string('overrides', 'lesson'));
$PAGE->set_heading($course->fullname);
$PAGE->activityheader->set_attrs([
'hidecompletion' => true,
'description' => ''
]);
navigation_node::override_active_url(new moodle_url('/mod/lesson/overrides.php', ['cmid' => $cmid]));
$renderer = $PAGE->get_renderer('mod_lesson');
echo $OUTPUT->header();
// Delete orphaned group overrides.
$sql = 'SELECT o.id
FROM {lesson_overrides} o
LEFT JOIN {groups} g ON o.groupid = g.id
WHERE o.groupid IS NOT NULL
AND g.id IS NULL
AND o.lessonid = ?';
$params = array($lesson->id);
$orphaned = $DB->get_records_sql($sql, $params);
if (!empty($orphaned)) {
$DB->delete_records_list('lesson_overrides', 'id', array_keys($orphaned));
}
$overrides = [];
// Fetch all overrides.
if ($groupmode) {
$colname = get_string('group');
// To filter the result by the list of groups that the current user has access to.
if ($groups) {
$params = ['lessonid' => $lesson->id];
list($insql, $inparams) = $DB->get_in_or_equal(array_keys($groups), SQL_PARAMS_NAMED);
$params += $inparams;
$sql = "SELECT o.*, g.name
FROM {lesson_overrides} o
JOIN {groups} g ON o.groupid = g.id
WHERE o.lessonid = :lessonid AND g.id $insql
ORDER BY g.name";
$overrides = $DB->get_records_sql($sql, $params);
}
} else {
$colname = get_string('user');
list($sort, $params) = users_order_by_sql('u');
$params['lessonid'] = $lesson->id;
$userfieldsapi = \core_user\fields::for_name();
if ($accessallgroups) {
$sql = 'SELECT o.*, ' . $userfieldsapi->get_sql('u', false, '', '', false)->selects . '
FROM {lesson_overrides} o
JOIN {user} u ON o.userid = u.id
WHERE o.lessonid = :lessonid
ORDER BY ' . $sort;
$overrides = $DB->get_records_sql($sql, $params);
} else if ($groups) {
list($insql, $inparams) = $DB->get_in_or_equal(array_keys($groups), SQL_PARAMS_NAMED);
$params += $inparams;
$sql = 'SELECT o.*, ' . $userfieldsapi->get_sql('u', false, '', '', false)->selects . '
FROM {lesson_overrides} o
JOIN {user} u ON o.userid = u.id
JOIN {groups_members} gm ON u.id = gm.userid
WHERE o.lessonid = :lessonid AND gm.groupid ' . $insql . '
ORDER BY ' . $sort;
$overrides = $DB->get_records_sql($sql, $params);
}
}
$overrides = $DB->get_records_sql($sql, $params);
$canoverride = true;
$errormessage = '';
if ($groupmode) {
if (empty($groups)) {
// There are no groups.
$canoverride = false;
$errormessage = get_string('groupsnone', 'lesson');
}
} else {
$users = array();
// See if there are any users in the lesson.
if ($accessallgroups) {
$users = get_enrolled_users($context, '', 0, 'u.id');
$nousermessage = get_string('usersnone', 'lesson');
} else if ($groups) {
$enrolledjoin = get_enrolled_join($context, 'u.id');
list($ingroupsql, $ingroupparams) = $DB->get_in_or_equal(array_keys($groups), SQL_PARAMS_NAMED);
$params = $enrolledjoin->params + $ingroupparams;
$sql = "SELECT u.id
FROM {user} u
JOIN {groups_members} gm ON gm.userid = u.id
{$enrolledjoin->joins}
WHERE gm.groupid $ingroupsql
AND {$enrolledjoin->wheres}
ORDER BY $sort";
$users = $DB->get_records_sql($sql, $params);
$nousermessage = get_string('usersnone', 'lesson');
} else {
$nousermessage = get_string('groupsnone', 'lesson');
}
$info = new \core_availability\info_module($cm);
$users = $info->filter_user_list($users);
if (empty($users)) {
// There are no users.
$canoverride = false;
$errormessage = $nousermessage;
}
}
// Initialise table.
$table = new html_table();
$table->headspan = array(1, 2, 1);
$table->colclasses = array('colname', 'colsetting', 'colvalue', 'colaction');
$table->head = array(
$colname,
get_string('overrides', 'lesson'),
get_string('action'),
);
$userurl = new moodle_url('/user/view.php', array());
$groupurl = new moodle_url('/group/overview.php', array('id' => $cm->course));
$overridedeleteurl = new moodle_url('/mod/lesson/overridedelete.php');
$overrideediturl = new moodle_url('/mod/lesson/overrideedit.php');
$hasinactive = false; // Whether there are any inactive overrides.
foreach ($overrides as $override) {
$fields = array();
$values = array();
$active = true;
// Check for inactive overrides.
if (!$groupmode) {
if (!is_enrolled($context, $override->userid)) {
// User not enrolled.
$active = false;
} else if (!\core_availability\info_module::is_user_visible($cm, $override->userid)) {
// User cannot access the module.
$active = false;
}
}
// Format available.
if (isset($override->available)) {
$fields[] = get_string('lessonopens', 'lesson');
$values[] = $override->available > 0 ?
userdate($override->available) : get_string('noopen', 'lesson');
}
// Format deadline.
if (isset($override->deadline)) {
$fields[] = get_string('lessoncloses', 'lesson');
$values[] = $override->deadline > 0 ?
userdate($override->deadline) : get_string('noclose', 'lesson');
}
// Format timelimit.
if (isset($override->timelimit)) {
$fields[] = get_string('timelimit', 'lesson');
$values[] = $override->timelimit > 0 ?
format_time($override->timelimit) : get_string('none', 'lesson');
}
// Format option to try a question again.
if (isset($override->review)) {
$fields[] = get_string('displayreview', 'lesson');
$values[] = $override->review ?
get_string('yes') : get_string('no');
}
// Format number of attempts.
if (isset($override->maxattempts)) {
$fields[] = get_string('maximumnumberofattempts', 'lesson');
$values[] = $override->maxattempts > 0 ?
$override->maxattempts : get_string('unlimited');
}
// Format retake allowed.
if (isset($override->retake)) {
$fields[] = get_string('retakesallowed', 'lesson');
$values[] = $override->retake ?
get_string('yes') : get_string('no');
}
// Format password.
if (isset($override->password)) {
$fields[] = get_string('usepassword', 'lesson');
$values[] = $override->password !== '' ?
get_string('enabled', 'lesson') : get_string('none', 'lesson');
}
// Icons.
$iconstr = '';
// Edit.
$editurlstr = $overrideediturl->out(true, array('id' => $override->id));
$iconstr = '<a title="' . get_string('edit') . '" href="'. $editurlstr . '">' .
$OUTPUT->pix_icon('t/edit', get_string('edit')) . '</a> ';
// Duplicate.
$copyurlstr = $overrideediturl->out(true,
array('id' => $override->id, 'action' => 'duplicate'));
$iconstr .= '<a title="' . get_string('copy') . '" href="' . $copyurlstr . '">' .
$OUTPUT->pix_icon('t/copy', get_string('copy')) . '</a> ';
// Delete.
$deleteurlstr = $overridedeleteurl->out(true,
array('id' => $override->id, 'sesskey' => sesskey()));
$iconstr .= '<a title="' . get_string('delete') . '" href="' . $deleteurlstr . '">' .
$OUTPUT->pix_icon('t/delete', get_string('delete')) . '</a> ';
if ($groupmode) {
$usergroupstr = '<a href="' . $groupurl->out(true, ['group' => $override->groupid]) . '" >' .
format_string($override->name, true, ['context' => $context]) . '</a>';
} else {
$usergroupstr = '<a href="' . $userurl->out(true,
array('id' => $override->userid)) . '" >' . fullname($override) . '</a>';
}
$class = '';
if (!$active) {
$class = "dimmed_text";
$usergroupstr .= '*';
$hasinactive = true;
}
$usergroupcell = new html_table_cell();
$usergroupcell->rowspan = count($fields);
$usergroupcell->text = $usergroupstr;
$actioncell = new html_table_cell();
$actioncell->rowspan = count($fields);
$actioncell->text = $iconstr;
for ($i = 0; $i < count($fields); ++$i) {
$row = new html_table_row();
$row->attributes['class'] = $class;
if ($i == 0) {
$row->cells[] = $usergroupcell;
}
$cell1 = new html_table_cell();
$cell1->text = $fields[$i];
$row->cells[] = $cell1;
$cell2 = new html_table_cell();
$cell2->text = $values[$i];
$row->cells[] = $cell2;
if ($i == 0) {
$row->cells[] = $actioncell;
}
$table->data[] = $row;
}
}
$overrideselect = new \mod_lesson\output\override_action_menu($cm->id, $url, $canoverride);
echo $renderer->render($overrideselect);
// Output the table and button.
echo html_writer::start_tag('div', array('id' => 'lessonoverrides'));
if (count($table->data)) {
echo html_writer::table($table);
}
// No overrides to be displayed.
if (!$overrides) {
echo $OUTPUT->notification(get_string('nooverridecreated', 'lesson'), 'info', false);
}
if ($hasinactive) {
echo $OUTPUT->notification(get_string('inactiveoverridehelp', 'lesson'), 'dimmed_text');
}
echo html_writer::start_tag('div', array('class' => 'buttons'));
if (!empty($errormessage)) {
echo $OUTPUT->notification($errormessage, 'error');
}
echo html_writer::end_tag('div');
echo html_writer::end_tag('div');
// Finish the page.
echo $OUTPUT->footer();
+373
View File
@@ -0,0 +1,373 @@
<?php
// 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/>.
/**
* Branch Table
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
defined('MOODLE_INTERNAL') || die();
/** Branch Table page */
define("LESSON_PAGE_BRANCHTABLE", "20");
class lesson_page_type_branchtable extends lesson_page {
protected $type = lesson_page::TYPE_STRUCTURE;
protected $typeid = LESSON_PAGE_BRANCHTABLE;
protected $typeidstring = 'branchtable';
protected $string = null;
protected $jumpto = null;
public function get_typeid() {
return $this->typeid;
}
public function get_typestring() {
if ($this->string===null) {
$this->string = get_string($this->typeidstring, 'lesson');
}
return $this->string;
}
/**
* 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) {
if ($answer->answer === '') {
// show only jumps for real branches (==have description)
continue;
}
$jumps[] = $this->get_jump_name($answer->jumpto);
}
} else {
// We get here is the lesson was created on a Moodle 1.9 site and
// the lesson contains question pages without any answers.
$jumps[] = $this->get_jump_name($this->properties->nextpageid);
}
return $jumps;
}
public static function get_jumptooptions($firstpage, lesson $lesson) {
global $DB, $PAGE;
$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");
$jump[LESSON_UNSEENBRANCHPAGE] = get_string("unseenpageinbranch", "lesson");
$jump[LESSON_RANDOMPAGE] = get_string("randompageinbranch", "lesson");
$jump[LESSON_RANDOMBRANCH] = get_string("randombranch", "lesson");
if (!$firstpage) {
if (!$apageid = $DB->get_field("lesson_pages", "id", array("lessonid" => $lesson->id, "prevpageid" => 0))) {
throw new \moodle_exception('cannotfindfirstpage', 'lesson');
}
while (true) {
if ($apageid) {
$title = $DB->get_field("lesson_pages", "title", array("id" => $apageid));
$jump[$apageid] = $title;
$apageid = $DB->get_field("lesson_pages", "nextpageid", array("id" => $apageid));
} else {
// last page reached
break;
}
}
}
return $jump;
}
public function get_idstring() {
return $this->typeidstring;
}
public function display($renderer, $attempt) {
global $PAGE, $CFG;
$output = '';
$options = new stdClass;
$options->para = false;
$options->noclean = true;
if ($this->lesson->slideshow) {
$output .= $renderer->slideshow_start($this->lesson);
}
// The heading level depends on whether the theme's activity header displays a heading (usually the activity name).
$headinglevel = $PAGE->activityheader->get_heading_level();
$output .= $renderer->heading(format_string($this->properties->title), $headinglevel);
$output .= $renderer->box($this->get_contents(), 'contents');
$buttons = array();
$i = 0;
foreach ($this->get_answers() as $answer) {
if ($answer->answer === '') {
// not a branch!
continue;
}
$params = array();
$params['id'] = $PAGE->cm->id;
$params['pageid'] = $this->properties->id;
$params['sesskey'] = sesskey();
$params['jumpto'] = $answer->jumpto;
$url = new moodle_url('/mod/lesson/continue.php', $params);
$buttons[] = $renderer->single_button($url, strip_tags(format_text($answer->answer, FORMAT_MOODLE, $options)));
$i++;
}
// Set the orientation
if ($this->properties->layout) {
$buttonshtml = $renderer->box(implode("\n", $buttons), 'branchbuttoncontainer horizontal');
} else {
$buttonshtml = $renderer->box(implode("\n", $buttons), 'branchbuttoncontainer vertical');
}
$output .= $buttonshtml;
if ($this->lesson->slideshow) {
$output .= $renderer->slideshow_end();
}
// Trigger an event: content page viewed.
$eventparams = array(
'context' => context_module::instance($PAGE->cm->id),
'objectid' => $this->properties->id
);
$event = \mod_lesson\event\content_page_viewed::create($eventparams);
$event->trigger();
return $output;
}
public function check_answer() {
global $USER, $DB, $PAGE, $CFG;
$result = parent::check_answer();
require_sesskey();
$newpageid = optional_param('jumpto', null, PARAM_INT);
// going to insert into lesson_branch
if ($newpageid == LESSON_RANDOMBRANCH) {
$branchflag = 1;
} else {
$branchflag = 0;
}
if ($grades = $DB->get_records("lesson_grades", array("lessonid" => $this->lesson->id, "userid" => $USER->id), "grade DESC")) {
$retries = count($grades);
} else {
$retries = 0;
}
// First record this page in lesson_branch. This record may be needed by lesson_unseen_branch_jump.
$branch = new stdClass;
$branch->lessonid = $this->lesson->id;
$branch->userid = $USER->id;
$branch->pageid = $this->properties->id;
$branch->retry = $retries;
$branch->flag = $branchflag;
$branch->timeseen = time();
$branch->nextpageid = 0; // Next page id will be set later.
$branch->id = $DB->insert_record("lesson_branch", $branch);
// this is called when jumping to random from a branch table
$context = context_module::instance($PAGE->cm->id);
if($newpageid == LESSON_UNSEENBRANCHPAGE) {
if (has_capability('mod/lesson:manage', $context)) {
$newpageid = LESSON_NEXTPAGE;
} else {
$newpageid = lesson_unseen_question_jump($this->lesson, $USER->id, $this->properties->id); // this may return 0
}
}
// convert jumpto page into a proper page id
if ($newpageid == 0) {
$newpageid = $this->properties->id;
} elseif ($newpageid == LESSON_NEXTPAGE) {
if (!$newpageid = $this->nextpageid) {
// no nextpage go to end of lesson
$newpageid = LESSON_EOL;
}
} elseif ($newpageid == LESSON_PREVIOUSPAGE) {
$newpageid = $this->prevpageid;
} elseif ($newpageid == LESSON_RANDOMPAGE) {
$newpageid = lesson_random_question_jump($this->lesson, $this->properties->id);
} elseif ($newpageid == LESSON_RANDOMBRANCH) {
$newpageid = lesson_unseen_branch_jump($this->lesson, $USER->id);
}
// Update record to set nextpageid.
$branch->nextpageid = $newpageid;
$DB->update_record("lesson_branch", $branch);
// This will force to redirect to the newpageid.
$result->inmediatejump = true;
$result->newpageid = $newpageid;
return $result;
}
public function display_answers(html_table $table) {
$answers = $this->get_answers();
$options = new stdClass;
$options->noclean = true;
$options->para = false;
$i = 1;
foreach ($answers as $answer) {
if ($answer->answer === '') {
// not a branch!
continue;
}
$cells = array();
$cells[] = '<label>' . get_string('branch', 'lesson') . ' ' . $i . '</label>: ';
$cells[] = format_text($answer->answer, $answer->answerformat, $options);
$table->data[] = new html_table_row($cells);
$cells = array();
$cells[] = '<label>' . get_string('jump', 'lesson') . ' ' . $i . '</label>: ';
$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;
}
public function get_grayout() {
return 1;
}
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
$formattextdefoptions->context = $answerpage->context;
foreach ($answers as $answer) {
$data = "<input type=\"button\" class=\"btn btn-secondary\" name=\"$answer->id\" " .
"value=\"".s(strip_tags(format_text($answer->answer, FORMAT_MOODLE, $formattextdefoptions)))."\" " .
"disabled=\"disabled\"> ";
$data .= get_string('jumpsto', 'lesson', $this->get_jump_name($answer->jumpto));
$answerdata->answers[] = array($data, "");
$answerpage->answerdata = $answerdata;
}
return $answerpage;
}
public function update($properties, $context = null, $maxbytes = null) {
if (empty($properties->display)) {
$properties->display = '0';
}
if (empty($properties->layout)) {
$properties->layout = '0';
}
return parent::update($properties);
}
public function add_page_link($previd) {
global $PAGE, $CFG;
$addurl = new moodle_url('/mod/lesson/editpage.php', array('id'=>$PAGE->cm->id, 'pageid'=>$previd, 'qtype'=>LESSON_PAGE_BRANCHTABLE));
return array('addurl'=>$addurl, 'type'=>LESSON_PAGE_BRANCHTABLE, 'name'=>get_string('addabranchtable', 'lesson'));
}
protected function get_displayinmenublock() {
return true;
}
public function is_unseen($param) {
global $USER, $DB;
if (is_array($param)) {
$seenpages = $param;
$branchpages = $this->lesson->get_sub_pages_of($this->properties->id, array(LESSON_PAGE_BRANCHTABLE, LESSON_PAGE_ENDOFBRANCH));
foreach ($branchpages as $branchpage) {
if (array_key_exists($branchpage->id, $seenpages)) { // check if any of the pages have been viewed
return false;
}
}
return true;
} else {
$nretakes = $param;
if (!$DB->count_records("lesson_attempts", array("pageid"=>$this->properties->id, "userid"=>$USER->id, "retry"=>$nretakes))) {
return true;
}
return false;
}
}
}
class lesson_add_page_form_branchtable extends lesson_add_page_form_base {
public $qtype = LESSON_PAGE_BRANCHTABLE;
public $qtypestring = 'branchtable';
protected $standard = false;
public function custom_definition() {
global $PAGE, $CFG;
$mform = $this->_form;
$lesson = $this->_customdata['lesson'];
$firstpage = optional_param('firstpage', false, PARAM_BOOL);
$jumptooptions = lesson_page_type_branchtable::get_jumptooptions($firstpage, $lesson);
if ($this->_customdata['edit']) {
$mform->setDefault('qtypeheading', get_string('editbranchtable', 'lesson'));
} else {
$mform->setDefault('qtypeheading', get_string('addabranchtable', 'lesson'));
}
$mform->addElement('hidden', 'firstpage');
$mform->setType('firstpage', PARAM_BOOL);
$mform->setDefault('firstpage', $firstpage);
$mform->addElement('hidden', 'qtype');
$mform->setType('qtype', PARAM_INT);
$mform->addElement('text', 'title', get_string("pagetitle", "lesson"), ['size' => 70, 'maxlength' => 255]);
$mform->addRule('title', null, 'required', null, 'client');
$mform->addRule('title', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
if (!empty($CFG->formatstringstriptags)) {
$mform->setType('title', PARAM_TEXT);
} else {
$mform->setType('title', PARAM_CLEANHTML);
}
$this->editoroptions = array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes);
$mform->addElement('editor', 'contents_editor', get_string("pagecontents", "lesson"), null, $this->editoroptions);
$mform->setType('contents_editor', PARAM_RAW);
$mform->addElement('checkbox', 'layout', null, get_string("arrangebuttonshorizontally", "lesson"));
$mform->setDefault('layout', true);
$mform->addElement('checkbox', 'display', null, get_string("displayinleftmenu", "lesson"));
$mform->setDefault('display', true);
for ($i = 0; $i < $lesson->maxanswers; $i++) {
$mform->addElement('header', 'headeranswer'.$i, get_string('branch', 'lesson').' '.($i+1));
$this->add_answer($i, get_string("description", "lesson"), $i == 0);
$mform->addElement('select', 'jumpto['.$i.']', get_string("jump", "lesson"), $jumptooptions);
if ($i === 0) {
$mform->setDefault('jumpto['.$i.']', 0);
} else {
$mform->setDefault('jumpto['.$i.']', LESSON_NEXTPAGE);
}
}
}
}
+206
View File
@@ -0,0 +1,206 @@
<?php
// 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/>.
/**
* Cluster
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
defined('MOODLE_INTERNAL') || die();
/** Start of Cluster page */
define("LESSON_PAGE_CLUSTER", "30");
class lesson_page_type_cluster extends lesson_page {
protected $type = lesson_page::TYPE_STRUCTURE;
protected $typeidstring = 'cluster';
protected $typeid = LESSON_PAGE_CLUSTER;
protected $string = null;
protected $jumpto = null;
public function display($renderer, $attempt) {
return '';
}
public function get_typeid() {
return $this->typeid;
}
public function get_typestring() {
if ($this->string===null) {
$this->string = get_string($this->typeidstring, 'lesson');
}
return $this->string;
}
public function get_idstring() {
return $this->typeidstring;
}
public function get_grayout() {
return 1;
}
public function callback_on_view($canmanage, $redirect = true) {
global $USER;
if (!$canmanage) {
// Get the next page in the lesson cluster jump
return (int) $this->lesson->cluster_jump($this->properties->id);
} else {
// get the next page
return (int) $this->properties->nextpageid;
}
}
public function override_next_page() {
global $USER;
return $this->lesson->cluster_jump($this->properties->id);
}
public function add_page_link($previd) {
global $PAGE, $CFG;
$addurl = new moodle_url('/mod/lesson/editpage.php', array('id'=>$PAGE->cm->id, 'pageid'=>$previd, 'sesskey'=>sesskey(), 'qtype'=>LESSON_PAGE_CLUSTER));
return array('addurl'=>$addurl, 'type'=>LESSON_PAGE_CLUSTER, 'name'=>get_string('addcluster', 'lesson'));
}
public function valid_page_and_view(&$validpages, &$pageviews) {
$validpages[$this->properties->id] = 1; // add the cluster page as a valid page
foreach ($this->lesson->get_sub_pages_of($this->properties->id, array(LESSON_PAGE_ENDOFCLUSTER)) as $subpage) {
if (in_array($subpage->id, $pageviews)) {
unset($pageviews[array_search($subpage->id, $pageviews)]); // remove it
// since the user did see one page in the cluster, add the cluster pageid to the viewedpageids
if (!in_array($this->properties->id, $pageviews)) {
$pageviews[] = $this->properties->id;
}
}
}
return $this->properties->nextpageid;
}
/**
* Creates answers within the database for this cluster page. Usually only ever
* called when creating a new page instance.
* @param object $properties
* @return array
*/
public function create_answers($properties) {
global $DB;
$newanswer = new stdClass;
$newanswer->lessonid = $this->lesson->id;
$newanswer->pageid = $this->properties->id;
$newanswer->timecreated = $this->properties->timecreated;
if (isset($properties->jumpto[0])) {
$newanswer->jumpto = $properties->jumpto[0];
}
$newanswer->id = $DB->insert_record('lesson_answers', $newanswer);
$answers = [$newanswer->id => new lesson_page_answer($newanswer)];
$this->answers = $answers;
return $answers;
}
}
class lesson_add_page_form_cluster extends lesson_add_page_form_base {
public $qtype = LESSON_PAGE_CLUSTER;
public $qtypestring = 'cluster';
protected $standard = false;
public function custom_definition() {
global $PAGE, $CFG;
$mform = $this->_form;
$lesson = $this->_customdata['lesson'];
$jumptooptions = lesson_page_type_branchtable::get_jumptooptions(optional_param('firstpage', false, PARAM_BOOL), $lesson);
$mform->addElement('hidden', 'firstpage');
$mform->setType('firstpage', PARAM_BOOL);
$mform->addElement('hidden', 'qtype');
$mform->setType('qtype', PARAM_TEXT);
$mform->addElement('text', 'title', get_string("pagetitle", "lesson"), ['size' => 70, 'maxlength' => 255]);
$mform->addRule('title', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
if (!empty($CFG->formatstringstriptags)) {
$mform->setType('title', PARAM_TEXT);
} else {
$mform->setType('title', PARAM_CLEANHTML);
}
$this->editoroptions = array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes);
$mform->addElement('editor', 'contents_editor', get_string("pagecontents", "lesson"), null, $this->editoroptions);
$mform->setType('contents_editor', PARAM_RAW);
$this->add_jumpto(0);
}
public function construction_override($pageid, lesson $lesson) {
global $PAGE, $CFG, $DB;
require_sesskey();
$timenow = time();
if ($pageid == 0) {
if ($lesson->has_pages()) {
if (!$page = $DB->get_record("lesson_pages", array("prevpageid" => 0, "lessonid" => $lesson->id))) {
throw new \moodle_exception('cannotfindpagerecord', 'lesson');
}
} else {
// This is the ONLY page
$page = new stdClass;
$page->id = 0;
}
} else {
if (!$page = $DB->get_record("lesson_pages", array("id" => $pageid))) {
throw new \moodle_exception('cannotfindpagerecord', 'lesson');
}
}
$newpage = new stdClass;
$newpage->lessonid = $lesson->id;
$newpage->prevpageid = $pageid;
if ($pageid != 0) {
$newpage->nextpageid = $page->nextpageid;
} else {
$newpage->nextpageid = $page->id;
}
$newpage->qtype = $this->qtype;
$newpage->timecreated = $timenow;
$newpage->title = get_string("clustertitle", "lesson");
$newpage->contents = get_string("clustertitle", "lesson");
$newpageid = $DB->insert_record("lesson_pages", $newpage);
// update the linked list...
if ($pageid != 0) {
$DB->set_field("lesson_pages", "nextpageid", $newpageid, array("id" => $pageid));
}
if ($pageid == 0) {
$page->nextpageid = $page->id;
}
if ($page->nextpageid) {
// the new page is not the last page
$DB->set_field("lesson_pages", "prevpageid", $newpageid, array("id" => $page->nextpageid));
}
// ..and the single "answer"
$newanswer = new stdClass;
$newanswer->lessonid = $lesson->id;
$newanswer->pageid = $newpageid;
$newanswer->timecreated = $timenow;
$newanswer->jumpto = LESSON_CLUSTERJUMP;
$newanswerid = $DB->insert_record("lesson_answers", $newanswer);
$lesson->add_message(get_string('addedcluster', 'lesson'), 'notifysuccess');
redirect($CFG->wwwroot.'/mod/lesson/edit.php?id='.$PAGE->cm->id);
}
}
+229
View File
@@ -0,0 +1,229 @@
<?php
// 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/>.
/**
* End of branch table
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
defined('MOODLE_INTERNAL') || die();
/** End of Branch page */
define("LESSON_PAGE_ENDOFBRANCH", "21");
class lesson_page_type_endofbranch extends lesson_page {
protected $type = lesson_page::TYPE_STRUCTURE;
protected $typeidstring = 'endofbranch';
protected $typeid = LESSON_PAGE_ENDOFBRANCH;
protected $string = null;
protected $jumpto = null;
public function display($renderer, $attempt) {
return '';
}
public function get_typeid() {
return $this->typeid;
}
public function get_typestring() {
if ($this->string===null) {
$this->string = get_string($this->typeidstring, 'lesson');
}
return $this->string;
}
public function get_idstring() {
return $this->typeidstring;
}
public function callback_on_view($canmanage, $redirect = true) {
return (int) $this->redirect_to_first_answer($canmanage, $redirect);
}
public function redirect_to_first_answer($canmanage, $redirect) {
global $USER, $PAGE;
$answers = $this->get_answers();
$answer = array_shift($answers);
$jumpto = $answer->jumpto;
if ($jumpto == LESSON_RANDOMBRANCH) {
$jumpto = lesson_unseen_branch_jump($this->lesson, $USER->id);
} elseif ($jumpto == LESSON_CLUSTERJUMP) {
if (!$canmanage) {
$jumpto = $this->lesson->cluster_jump($this->properties->id);
} else {
if ($this->properties->nextpageid == 0) {
$jumpto = LESSON_EOL;
} else {
$jumpto = $this->properties->nextpageid;
}
}
} else if ($answer->jumpto == LESSON_NEXTPAGE) {
if ($this->properties->nextpageid == 0) {
$jumpto = LESSON_EOL;
} else {
$jumpto = $this->properties->nextpageid;
}
} else if ($jumpto == 0) {
$jumpto = $this->properties->id;
} else if ($jumpto == LESSON_PREVIOUSPAGE) {
$jumpto = $this->properties->prevpageid;
}
if ($redirect) {
redirect(new moodle_url('/mod/lesson/view.php', array('id' => $PAGE->cm->id, 'pageid' => $jumpto)));
die;
}
return $jumpto;
}
public function get_grayout() {
return 1;
}
public function add_page_link($previd) {
global $PAGE, $CFG;
if ($previd != 0) {
$addurl = new moodle_url('/mod/lesson/editpage.php', array('id'=>$PAGE->cm->id, 'pageid'=>$previd, 'sesskey'=>sesskey(), 'qtype'=>LESSON_PAGE_ENDOFBRANCH));
return array('addurl'=>$addurl, 'type'=>LESSON_PAGE_ENDOFBRANCH, 'name'=>get_string('addanendofbranch', 'lesson'));
}
return false;
}
public function valid_page_and_view(&$validpages, &$pageviews) {
return $this->properties->nextpageid;
}
/**
* Creates answers within the database for this end of cluster page. Usually only ever
* called when creating a new page instance.
* @param object $properties
* @return array
*/
public function create_answers($properties) {
global $DB;
$newanswer = new stdClass;
$newanswer->lessonid = $this->lesson->id;
$newanswer->pageid = $this->properties->id;
$newanswer->timecreated = $this->properties->timecreated;
if (isset($properties->jumpto[0])) {
$newanswer->jumpto = $properties->jumpto[0];
}
$newanswer->id = $DB->insert_record('lesson_answers', $newanswer);
$answers = [$newanswer->id => new lesson_page_answer($newanswer)];
$this->answers = $answers;
return $answers;
}
}
class lesson_add_page_form_endofbranch extends lesson_add_page_form_base {
public $qtype = LESSON_PAGE_ENDOFBRANCH;
public $qtypestring = 'endofbranch';
protected $standard = false;
public function custom_definition() {
global $PAGE, $CFG;
$mform = $this->_form;
$lesson = $this->_customdata['lesson'];
$jumptooptions = lesson_page_type_branchtable::get_jumptooptions(optional_param('firstpage', false, PARAM_BOOL), $lesson);
$mform->addElement('hidden', 'firstpage');
$mform->setType('firstpage', PARAM_BOOL);
$mform->addElement('hidden', 'qtype');
$mform->setType('qtype', PARAM_TEXT);
$mform->addElement('text', 'title', get_string("pagetitle", "lesson"), ['size' => 70, 'maxlength' => 255]);
$mform->addRule('title', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
if (!empty($CFG->formatstringstriptags)) {
$mform->setType('title', PARAM_TEXT);
} else {
$mform->setType('title', PARAM_CLEANHTML);
}
$this->editoroptions = array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes);
$mform->addElement('editor', 'contents_editor', get_string("pagecontents", "lesson"), null, $this->editoroptions);
$mform->setType('contents_editor', PARAM_RAW);
$this->add_jumpto(0);
}
public function construction_override($pageid, lesson $lesson) {
global $DB, $CFG, $PAGE;
require_sesskey();
// first get the preceeding page
$timenow = time();
// the new page is not the first page (end of branch always comes after an existing page)
if (!$page = $DB->get_record("lesson_pages", array("id" => $pageid))) {
throw new \moodle_exception('cannotfindpagerecord', 'lesson');
}
// chain back up to find the (nearest branch table)
$btpage = clone($page);
$btpageid = $btpage->id;
while (($btpage->qtype != LESSON_PAGE_BRANCHTABLE) && ($btpage->prevpageid > 0)) {
$btpageid = $btpage->prevpageid;
if (!$btpage = $DB->get_record("lesson_pages", array("id" => $btpageid))) {
throw new \moodle_exception('cannotfindpagerecord', 'lesson');
}
}
if ($btpage->qtype == LESSON_PAGE_BRANCHTABLE) {
$newpage = new stdClass;
$newpage->lessonid = $lesson->id;
$newpage->prevpageid = $pageid;
$newpage->nextpageid = $page->nextpageid;
$newpage->qtype = $this->qtype;
$newpage->timecreated = $timenow;
$newpage->title = get_string("endofbranch", "lesson");
$newpage->contents = get_string("endofbranch", "lesson");
$newpageid = $DB->insert_record("lesson_pages", $newpage);
// update the linked list...
$DB->set_field("lesson_pages", "nextpageid", $newpageid, array("id" => $pageid));
if ($page->nextpageid) {
// the new page is not the last page
$DB->set_field("lesson_pages", "prevpageid", $newpageid, array("id" => $page->nextpageid));
}
// ..and the single "answer"
$newanswer = new stdClass;
$newanswer->lessonid = $lesson->id;
$newanswer->pageid = $newpageid;
$newanswer->timecreated = $timenow;
$newanswer->jumpto = $btpageid;
$newanswerid = $DB->insert_record("lesson_answers", $newanswer);
$lesson->add_message(get_string('addedanendofbranch', 'lesson'), 'notifysuccess');
} else {
$lesson->add_message(get_string('nobranchtablefound', 'lesson'));
}
redirect($CFG->wwwroot."/mod/lesson/edit.php?id=".$PAGE->cm->id);
}
}
+195
View File
@@ -0,0 +1,195 @@
<?php
// 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/>.
/**
* End of cluster
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
defined('MOODLE_INTERNAL') || die();
/** End of Cluster page */
define("LESSON_PAGE_ENDOFCLUSTER", "31");
class lesson_page_type_endofcluster extends lesson_page {
protected $type = lesson_page::TYPE_STRUCTURE;
protected $typeidstring = 'endofcluster';
protected $typeid = LESSON_PAGE_ENDOFCLUSTER;
protected $string = null;
protected $jumpto = null;
public function display($renderer, $attempt) {
return '';
}
public function get_typeid() {
return $this->typeid;
}
public function get_typestring() {
if ($this->string===null) {
$this->string = get_string($this->typeidstring, 'lesson');
}
return $this->string;
}
public function get_idstring() {
return $this->typeidstring;
}
public function callback_on_view($canmanage, $redirect = true) {
return (int) $this->redirect_to_next_page($canmanage, $redirect);
}
public function redirect_to_next_page($canmanage, $redirect) {
global $PAGE;
if ($this->properties->nextpageid == 0) {
$nextpageid = LESSON_EOL;
} else {
$nextpageid = $this->properties->nextpageid;
}
if ($redirect) {
redirect(new moodle_url('/mod/lesson/view.php', array('id' => $PAGE->cm->id, 'pageid' => $nextpageid)));
die;
}
return $nextpageid;
}
public function get_grayout() {
return 1;
}
public function override_next_page() {
global $DB;
$jump = $DB->get_field("lesson_answers", "jumpto", array("pageid" => $this->properties->id, "lessonid" => $this->lesson->id));
if ($jump == LESSON_NEXTPAGE) {
if ($this->properties->nextpageid == 0) {
return LESSON_EOL;
} else {
return $this->properties->nextpageid;
}
} else {
return $jump;
}
}
public function add_page_link($previd) {
global $PAGE, $CFG;
if ($previd != 0) {
$addurl = new moodle_url('/mod/lesson/editpage.php', array('id'=>$PAGE->cm->id, 'pageid'=>$previd, 'sesskey'=>sesskey(), 'qtype'=>LESSON_PAGE_ENDOFCLUSTER));
return array('addurl'=>$addurl, 'type'=>LESSON_PAGE_ENDOFCLUSTER, 'name'=>get_string('addendofcluster', 'lesson'));
}
return false;
}
public function valid_page_and_view(&$validpages, &$pageviews) {
return $this->properties->nextpageid;
}
/**
* Creates answers within the database for this end of cluster page. Usually only ever
* called when creating a new page instance.
* @param object $properties
* @return array
*/
public function create_answers($properties) {
global $DB;
$newanswer = new stdClass;
$newanswer->lessonid = $this->lesson->id;
$newanswer->pageid = $this->properties->id;
$newanswer->timecreated = $this->properties->timecreated;
if (isset($properties->jumpto[0])) {
$newanswer->jumpto = $properties->jumpto[0];
}
$newanswer->id = $DB->insert_record('lesson_answers', $newanswer);
$answers = [$newanswer->id => new lesson_page_answer($newanswer)];
$this->answers = $answers;
return $answers;
}
}
class lesson_add_page_form_endofcluster extends lesson_add_page_form_base {
public $qtype = LESSON_PAGE_ENDOFCLUSTER;
public $qtypestring = 'endofcluster';
protected $standard = false;
public function custom_definition() {
global $PAGE, $CFG;
$mform = $this->_form;
$lesson = $this->_customdata['lesson'];
$jumptooptions = lesson_page_type_branchtable::get_jumptooptions(optional_param('firstpage', false, PARAM_BOOL), $lesson);
$mform->addElement('hidden', 'firstpage');
$mform->setType('firstpage', PARAM_BOOL);
$mform->addElement('hidden', 'qtype');
$mform->setType('qtype', PARAM_TEXT);
$mform->addElement('text', 'title', get_string("pagetitle", "lesson"), ['size' => 70, 'maxlength' => 255]);
$mform->addRule('title', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
if (!empty($CFG->formatstringstriptags)) {
$mform->setType('title', PARAM_TEXT);
} else {
$mform->setType('title', PARAM_CLEANHTML);
}
$this->editoroptions = array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes);
$mform->addElement('editor', 'contents_editor', get_string("pagecontents", "lesson"), null, $this->editoroptions);
$mform->setType('contents_editor', PARAM_RAW);
$this->add_jumpto(0);
}
public function construction_override($pageid, lesson $lesson) {
global $CFG, $PAGE, $DB;
require_sesskey();
$timenow = time();
// the new page is not the first page (end of cluster always comes after an existing page)
if (!$page = $DB->get_record("lesson_pages", array("id" => $pageid))) {
throw new \moodle_exception('cannotfindpages', 'lesson');
}
// could put code in here to check if the user really can insert an end of cluster
$newpage = new stdClass;
$newpage->lessonid = $lesson->id;
$newpage->prevpageid = $pageid;
$newpage->nextpageid = $page->nextpageid;
$newpage->qtype = $this->qtype;
$newpage->timecreated = $timenow;
$newpage->title = get_string("endofclustertitle", "lesson");
$newpage->contents = get_string("endofclustertitle", "lesson");
$newpageid = $DB->insert_record("lesson_pages", $newpage);
// update the linked list...
$DB->set_field("lesson_pages", "nextpageid", $newpageid, array("id" => $pageid));
if ($page->nextpageid) {
// the new page is not the last page
$DB->set_field("lesson_pages", "prevpageid", $newpageid, array("id" => $page->nextpageid));
}
// ..and the single "answer"
$newanswer = new stdClass;
$newanswer->lessonid = $lesson->id;
$newanswer->pageid = $newpageid;
$newanswer->timecreated = $timenow;
$newanswer->jumpto = LESSON_NEXTPAGE;
$newanswerid = $DB->insert_record("lesson_answers", $newanswer);
$lesson->add_message(get_string('addedendofcluster', 'lesson'), 'notifysuccess');
redirect($CFG->wwwroot.'/mod/lesson/edit.php?id='.$PAGE->cm->id);
}
}
+441
View File
@@ -0,0 +1,441 @@
<?php
// 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/>.
/**
* Essay
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
defined('MOODLE_INTERNAL') || die();
/** Essay question type */
define("LESSON_PAGE_ESSAY", "10");
class lesson_page_type_essay extends lesson_page {
protected $type = lesson_page::TYPE_QUESTION;
protected $typeidstring = 'essay';
protected $typeid = LESSON_PAGE_ESSAY;
protected $string = null;
public function get_typeid() {
return $this->typeid;
}
public function get_typestring() {
if ($this->string===null) {
$this->string = get_string($this->typeidstring, 'lesson');
}
return $this->string;
}
public function get_idstring() {
return $this->typeidstring;
}
/**
* Unserialize attempt useranswer and add missing responseformat if needed
* for compatibility with old records.
*
* @param string $useranswer serialized object
* @return object
*/
public static function extract_useranswer($useranswer) {
$essayinfo = unserialize_object($useranswer);
if (!isset($essayinfo->responseformat)) {
$essayinfo->response = text_to_html($essayinfo->response ?? '', false, false);
$essayinfo->responseformat = FORMAT_HTML;
}
return $essayinfo;
}
public function display($renderer, $attempt) {
global $PAGE, $CFG, $USER;
$context = context_module::instance($PAGE->cm->id);
$options = array(
'contents' => $this->get_contents(),
'lessonid' => $this->lesson->id,
'attemptid' => $attempt ? $attempt->id : null,
'editoroptions' => array(
'maxbytes' => $PAGE->course->maxbytes,
'context' => $context,
'noclean' => true,
'maxfiles' => EDITOR_UNLIMITED_FILES,
'enable_filemanagement' => false
)
);
$mform = new lesson_display_answer_form_essay($CFG->wwwroot.'/mod/lesson/continue.php', $options);
$data = new stdClass;
$data->id = $PAGE->cm->id;
$data->pageid = $this->properties->id;
if (isset($USER->modattempts[$this->lesson->id])) {
$essayinfo = self::extract_useranswer($attempt->useranswer);
$data->answer = $essayinfo->answer;
}
$data = file_prepare_standard_editor($data, 'answer', $options['editoroptions'],
$context, 'mod_lesson', 'essay_answers');
$mform->set_data($data);
// Trigger an event question viewed.
$eventparams = array(
'context' => context_module::instance($PAGE->cm->id),
'objectid' => $this->properties->id,
'other' => array(
'pagetype' => $this->get_typestring()
)
);
$event = \mod_lesson\event\question_viewed::create($eventparams);
$event->trigger();
return $mform->display();
}
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;
if (isset($properties->jumpto[0])) {
$newanswer->jumpto = $properties->jumpto[0];
}
if (isset($properties->score[0])) {
$newanswer->score = $properties->score[0];
}
$newanswer->id = $DB->insert_record("lesson_answers", $newanswer);
$answers = array($newanswer->id => new lesson_page_answer($newanswer));
$this->answers = $answers;
return $answers;
}
/**
* Overridden function
*
* @param object $attempt
* @param object $result
* @return array
*/
public function on_after_write_attempt($attempt, $result) {
global $PAGE;
if ($formdata = $result->postdata) {
// Save any linked files if we are using an editor.
$editoroptions = array(
'maxbytes' => $PAGE->course->maxbytes,
'context' => context_module::instance($PAGE->cm->id),
'noclean' => true, 'maxfiles' => EDITOR_UNLIMITED_FILES,
'enable_filemanagement' => false,
);
$formdata = file_postupdate_standard_editor($formdata, 'answer', $editoroptions,
$editoroptions['context'], 'mod_lesson', 'essay_answers', $attempt->id);
// Update the student response to have the modified link.
$useranswer = unserialize_object($attempt->useranswer);
$useranswer->answer = $formdata->answer;
$useranswer->answerformat = $formdata->answerformat;
$attempt->useranswer = serialize($useranswer);
$result->studentanswer = $formdata->answer;
$result->studentanswerformat = $formdata->answerformat;
return [$attempt, $result];
}
return parent::on_after_write_attempt($attempt, $result);
}
/**
* Custom formats the answer to display
*
* @param string $answer
* @param context $context
* @param int $answerformat
* @param array $options Optional param for additional options.
* @return string Returns formatted string
*/
public function format_answer($answer, $context, $answerformat, $options = []) {
$answer = file_rewrite_pluginfile_urls($answer, 'pluginfile.php', $context->id,
'mod_lesson', 'essay_answers', $options->attemptid);
return parent::format_answer($answer, $context, $answerformat, $options);
}
public function check_answer() {
global $PAGE, $CFG;
$result = parent::check_answer();
$result->isessayquestion = true;
$context = context_module::instance($PAGE->cm->id);
$options = array(
'contents' => $this->get_contents(),
'editoroptions' => array(
'maxbytes' => $PAGE->course->maxbytes,
'context' => $context,
'noclean' => true,
'maxfiles' => EDITOR_UNLIMITED_FILES,
'enable_filemanagement' => false,
)
);
$mform = new lesson_display_answer_form_essay($CFG->wwwroot.'/mod/lesson/continue.php', $options);
$data = $mform->get_data();
require_sesskey();
if (!$data) {
$result->inmediatejump = true;
$result->newpageid = $this->properties->id;
return $result;
}
if (is_array($data->answer_editor) && strlen($data->answer_editor['text'])) {
$studentanswer = $data->answer_editor['text']; // Will be reset later.
$studentanswerformat = $data->answer_editor['format']; // Will be reset later.
} else {
$studentanswer = isset($data->answer) ? $data->answer : '';
$studentanswerformat = FORMAT_HTML;
}
if (trim($studentanswer) === '') {
$result->noanswer = true;
return $result;
}
$answers = $this->get_answers();
foreach ($answers as $answer) {
$result->answerid = $answer->id;
$result->newpageid = $answer->jumpto;
}
$userresponse = new stdClass;
$userresponse->sent=0;
$userresponse->graded = 0;
$userresponse->score = 0;
$userresponse->answer = $studentanswer;
$userresponse->answerformat = $studentanswerformat;
$userresponse->response = '';
$userresponse->responseformat = FORMAT_HTML;
$result->userresponse = serialize($userresponse);
$result->studentanswerformat = $studentanswerformat;
$result->studentanswer = $studentanswer;
$result->postdata = $data;
return $result;
}
public function update($properties, $context = null, $maxbytes = null) {
global $DB, $PAGE;
$answers = $this->get_answers();
$properties->id = $this->properties->id;
$properties->lessonid = $this->lesson->id;
$properties->timemodified = time();
$properties = file_postupdate_standard_editor($properties, 'contents', array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes), context_module::instance($PAGE->cm->id), 'mod_lesson', 'page_contents', $properties->id);
$DB->update_record("lesson_pages", $properties);
// Trigger an event: page updated.
\mod_lesson\event\page_updated::create_from_lesson_page($this, $context)->trigger();
if (!array_key_exists(0, $this->answers)) {
$this->answers[0] = new stdClass;
$this->answers[0]->lessonid = $this->lesson->id;
$this->answers[0]->pageid = $this->id;
$this->answers[0]->timecreated = $this->timecreated;
}
if (isset($properties->jumpto[0])) {
$this->answers[0]->jumpto = $properties->jumpto[0];
}
if (isset($properties->score[0])) {
$this->answers[0]->score = $properties->score[0];
}
if (!isset($this->answers[0]->id)) {
$this->answers[0]->id = $DB->insert_record("lesson_answers", $this->answers[0]);
} else {
$DB->update_record("lesson_answers", $this->answers[0]->properties());
}
return true;
}
public function stats(array &$pagestats, $tries) {
$temp = $this->lesson->get_last_attempt($tries);
$essayinfo = self::extract_useranswer($temp->useranswer);
if ($essayinfo->graded) {
if (isset($pagestats[$temp->pageid])) {
$essaystats = $pagestats[$temp->pageid];
$essaystats->totalscore += $essayinfo->score;
$essaystats->total++;
$pagestats[$temp->pageid] = $essaystats;
} else {
$essaystats = new stdClass();
$essaystats->totalscore = $essayinfo->score;
$essaystats->total = 1;
$pagestats[$temp->pageid] = $essaystats;
}
}
return true;
}
public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) {
global $PAGE, $DB;
$formattextdefoptions = new stdClass();
$formattextdefoptions->noclean = true;
$formattextdefoptions->para = false;
$formattextdefoptions->context = $answerpage->context;
$answers = $this->get_answers();
$context = context_module::instance($PAGE->cm->id);
foreach ($answers as $answer) {
$hasattempts = $DB->record_exists('lesson_attempts', ['answerid' => $answer->id]);
if ($useranswer != null) {
$essayinfo = self::extract_useranswer($useranswer->useranswer);
$essayinfo->answer = file_rewrite_pluginfile_urls($essayinfo->answer, 'pluginfile.php',
$context->id, 'mod_lesson', 'essay_answers', $useranswer->id);
if ($essayinfo->response == null) {
$answerdata->response = get_string("nocommentyet", "lesson");
} else {
$essayinfo->response = file_rewrite_pluginfile_urls($essayinfo->response, 'pluginfile.php',
$answerpage->context->id, 'mod_lesson', 'essay_responses', $useranswer->id);
$answerdata->response = format_text($essayinfo->response, $essayinfo->responseformat, $formattextdefoptions);
}
if (isset($pagestats[$this->properties->id])) {
$percent = $pagestats[$this->properties->id]->totalscore / $pagestats[$this->properties->id]->total * 100;
$percent = round($percent, 2);
$percent = get_string("averagescore", "lesson").": ". $percent ."%";
} else {
// dont think this should ever be reached....
$percent = get_string("nooneansweredthisquestion", "lesson");
}
if ($essayinfo->graded) {
if ($this->lesson->custom) {
$answerdata->score = get_string("pointsearned", "lesson").": " . $essayinfo->score;
} elseif ($essayinfo->score) {
$answerdata->score = get_string("receivedcredit", "lesson");
} else {
$answerdata->score = get_string("didnotreceivecredit", "lesson");
}
} else {
$answerdata->score = get_string("havenotgradedyet", "lesson");
}
} else {
$essayinfo = new stdClass();
if ($hasattempts && has_capability('mod/lesson:grade', $answerpage->context)) {
$essayinfo->answer = html_writer::link(new moodle_url("/mod/lesson/essay.php",
['id' => $PAGE->cm->id]), get_string("viewessayanswers", "lesson"));
} else {
$essayinfo->answer = "";
}
$essayinfo->answerformat = null;
}
// The essay question has been graded.
if (isset($pagestats[$this->properties->id])) {
$avescore = $pagestats[$this->properties->id]->totalscore / $pagestats[$this->properties->id]->total;
$avescore = round($avescore, 2);
$avescore = get_string("averagescore", "lesson").": ". $avescore ;
} else {
$avescore = $hasattempts ? get_string("essaynotgradedyet", "lesson") :
get_string("nooneansweredthisquestion", "lesson");
}
// This is the student's answer so it should be cleaned.
$answerdata->answers[] = array(format_text($essayinfo->answer, $essayinfo->answerformat,
array('para' => true, 'context' => $answerpage->context)), $avescore);
$answerpage->answerdata = $answerdata;
}
return $answerpage;
}
public function is_unanswered($nretakes) {
global $DB, $USER;
if (!$DB->count_records("lesson_attempts", array('pageid'=>$this->properties->id, 'userid'=>$USER->id, 'retry'=>$nretakes))) {
return true;
}
return false;
}
public function requires_manual_grading() {
return true;
}
public function get_earnedscore($answers, $attempt) {
$essayinfo = self::extract_useranswer($attempt->useranswer);
return $essayinfo->score;
}
}
class lesson_add_page_form_essay extends lesson_add_page_form_base {
public $qtype = 'essay';
public $qtypestring = 'essay';
public function custom_definition() {
$this->add_jumpto(0);
$this->add_score(0, null, 1);
}
}
class lesson_display_answer_form_essay extends moodleform {
public function definition() {
global $USER, $OUTPUT;
$mform = $this->_form;
$contents = $this->_customdata['contents'];
$editoroptions = $this->_customdata['editoroptions'];
$hasattempt = false;
$attrs = '';
$useranswer = '';
$useranswerraw = '';
if (isset($this->_customdata['lessonid'])) {
$lessonid = $this->_customdata['lessonid'];
if (isset($USER->modattempts[$lessonid]->useranswer) && !empty($USER->modattempts[$lessonid]->useranswer)) {
$attrs = array('disabled' => 'disabled');
$hasattempt = true;
$useranswertemp = lesson_page_type_essay::extract_useranswer($USER->modattempts[$lessonid]->useranswer);
$useranswer = htmlspecialchars_decode($useranswertemp->answer, ENT_QUOTES);
$useranswerraw = $useranswertemp->answer;
}
}
// Disable shortforms.
$mform->setDisableShortforms();
$mform->addElement('header', 'pageheader');
$mform->addElement('html', $OUTPUT->container($contents, 'contents'));
$options = new stdClass;
$options->para = false;
$options->noclean = true;
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'pageid');
$mform->setType('pageid', PARAM_INT);
if ($hasattempt) {
$mform->addElement('hidden', 'answer', $useranswerraw);
$mform->setType('answer', PARAM_RAW);
$mform->addElement('html', $OUTPUT->container(get_string('youranswer', 'lesson'), 'youranswer'));
$useranswer = file_rewrite_pluginfile_urls($useranswer, 'pluginfile.php', $editoroptions['context']->id,
'mod_lesson', 'essay_answers', $this->_customdata['attemptid']);
$mform->addElement('html', $OUTPUT->container($useranswer, 'reviewessay'));
$this->add_action_buttons(null, get_string("nextpage", "lesson"));
} else {
$mform->addElement('editor', 'answer_editor', get_string('youranswer', 'lesson'), null, $editoroptions);
$mform->setType('answer_editor', PARAM_RAW);
$this->add_action_buttons(null, get_string("submit", "lesson"));
}
}
}
+605
View File
@@ -0,0 +1,605 @@
<?php
// 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/>.
/**
* Matching
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
defined('MOODLE_INTERNAL') || die();
/** Matching question type */
define("LESSON_PAGE_MATCHING", "5");
class lesson_page_type_matching extends lesson_page {
protected $type = lesson_page::TYPE_QUESTION;
protected $typeid = LESSON_PAGE_MATCHING;
protected $typeidstring = 'matching';
protected $string = null;
public function get_typeid() {
return $this->typeid;
}
public function get_typestring() {
if ($this->string===null) {
$this->string = get_string($this->typeidstring, 'lesson');
}
return $this->string;
}
public function get_idstring() {
return $this->typeidstring;
}
public function display($renderer, $attempt) {
global $USER, $CFG, $PAGE;
$mform = $this->make_answer_form($attempt);
$data = new stdClass;
$data->id = $PAGE->cm->id;
$data->pageid = $this->properties->id;
$mform->set_data($data);
// Trigger an event question viewed.
$eventparams = array(
'context' => context_module::instance($PAGE->cm->id),
'objectid' => $this->properties->id,
'other' => array(
'pagetype' => $this->get_typestring()
)
);
$event = \mod_lesson\event\question_viewed::create($eventparams);
$event->trigger();
return $mform->display();
}
protected function make_answer_form($attempt=null) {
global $USER, $CFG;
// don't shuffle answers (could be an option??)
$getanswers = array_slice($this->get_answers(), 2);
$answers = array();
foreach ($getanswers as $getanswer) {
$answers[$getanswer->id] = $getanswer;
}
// Calculate the text for the dropdown, keyed by the non formatted version.
$responses = array();
foreach ($answers as $answer) {
// Get all the response.
if ($answer->response != null) {
$responses[trim($answer->response)] = format_text(trim($answer->response));
}
}
// Now shuffle the answers to randomise the order of the items in the dropdown.
$responseoptions = ['' => get_string('choosedots')];
if (!empty($responses)) {
$keys = array_keys($responses);
shuffle($keys);
foreach ($keys as $key) {
$responseoptions[$key] = $responses[$key];
}
}
if (isset($USER->modattempts[$this->lesson->id]) && !empty($attempt->useranswer)) {
$useranswers = explode(',', $attempt->useranswer);
$t = 0;
} else {
$useranswers = array();
}
$action = $CFG->wwwroot.'/mod/lesson/continue.php';
$params = array('answers'=>$answers, 'useranswers'=>$useranswers, 'responseoptions'=>$responseoptions, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents());
$mform = new lesson_display_answer_form_matching($action, $params);
return $mform;
}
public function create_answers($properties) {
global $DB, $PAGE;
// now add the answers
$newanswer = new stdClass;
$newanswer->lessonid = $this->lesson->id;
$newanswer->pageid = $this->properties->id;
$newanswer->timecreated = $this->properties->timecreated;
$cm = get_coursemodule_from_instance('lesson', $this->lesson->id, $this->lesson->course);
$context = context_module::instance($cm->id);
// Check for duplicate response format.
$duplicateresponse = array();
if (is_array($properties->response_editor) && // If there are response_editors to iterate.
is_array(reset($properties->response_editor))) { // And they come split into text & format array.
foreach ($properties->response_editor as $response) { // Iterate over all them.
$duplicateresponse[] = $response['text']; // Picking the text only. This pagetype is that way.
}
$properties->response_editor = $duplicateresponse;
}
$answers = array();
// need to add two to offset correct response and wrong response
$this->lesson->maxanswers = $this->lesson->maxanswers + 2;
for ($i = 0; $i < $this->lesson->maxanswers; $i++) {
$answer = clone($newanswer);
if (!empty($properties->answer_editor[$i]) && is_array($properties->answer_editor[$i])) {
$answer->answer = $properties->answer_editor[$i]['text'];
$answer->answerformat = $properties->answer_editor[$i]['format'];
}
if (!empty($properties->response_editor[$i])) {
$answer->response = $properties->response_editor[$i];
$answer->responseformat = 0;
}
if (isset($properties->jumpto[$i])) {
$answer->jumpto = $properties->jumpto[$i];
}
if ($this->lesson->custom && isset($properties->score[$i])) {
$answer->score = $properties->score[$i];
}
if (isset($answer->answer) && $answer->answer != '') {
$answer->id = $DB->insert_record("lesson_answers", $answer);
$this->save_answers_files($context, $PAGE->course->maxbytes,
$answer, $properties->answer_editor[$i]);
$answers[$answer->id] = new lesson_page_answer($answer);
} else if ($i < 2) {
$answer->id = $DB->insert_record("lesson_answers", $answer);
$answers[$answer->id] = new lesson_page_answer($answer);
} else {
break;
}
}
$this->answers = $answers;
return $answers;
}
public function check_answer() {
global $CFG, $PAGE;
$formattextdefoptions = new stdClass();
$formattextdefoptions->noclean = true;
$formattextdefoptions->para = false;
$result = parent::check_answer();
$mform = $this->make_answer_form();
$data = $mform->get_data();
require_sesskey();
if (!$data) {
$result->inmediatejump = true;
$result->newpageid = $this->properties->id;
return $result;
}
$response = $data->response;
$getanswers = $this->get_answers();
foreach ($getanswers as $key => $answer) {
$getanswers[$key] = parent::rewrite_answers_urls($answer);
}
$correct = array_shift($getanswers);
$wrong = array_shift($getanswers);
$answers = array();
foreach ($getanswers as $key => $answer) {
if ($answer->answer !== '' or $answer->response !== '') {
$answers[$answer->id] = $answer;
}
}
// get the user's exact responses for record keeping
$hits = 0;
$userresponse = array();
$result->studentanswerformat = FORMAT_HTML;
foreach ($response as $id => $value) {
if ($value == '') {
$result->noanswer = true;
return $result;
}
$userresponse[] = $value;
// Make sure the user's answer exists in question's answer
if (array_key_exists($id, $answers)) {
$answer = $answers[$id];
$result->studentanswer .= '<br />'.format_text($answer->answer, $answer->answerformat, $formattextdefoptions).' = '.$value;
if (trim($answer->response) == trim($value)) {
$hits++;
}
}
}
$result->userresponse = implode(",", $userresponse);
if ($hits == count($answers)) {
$result->correctanswer = true;
$result->response = format_text($correct->answer, $correct->answerformat, $formattextdefoptions);
$result->answerid = $correct->id;
$result->newpageid = $correct->jumpto;
} else {
$result->correctanswer = false;
$result->response = format_text($wrong->answer, $wrong->answerformat, $formattextdefoptions);
$result->answerid = $wrong->id;
$result->newpageid = $wrong->jumpto;
}
return $result;
}
public function option_description_string() {
return get_string("firstanswershould", "lesson");
}
public function display_answers(html_table $table) {
$answers = $this->get_answers();
$options = new stdClass;
$options->noclean = true;
$options->para = false;
$i = 1;
$n = 0;
foreach ($answers as $answer) {
$answer = parent::rewrite_answers_urls($answer);
if ($n < 2) {
if ($answer->answer != null) {
$cells = array();
if ($n == 0) {
$cells[] = '<label>' . get_string('correctresponse', 'lesson') . '</label>';
} else {
$cells[] = '<label>' . get_string('wrongresponse', 'lesson') . '</label>';
}
$cells[] = format_text($answer->answer, $answer->answerformat, $options);
$table->data[] = new html_table_row($cells);
}
if ($n == 0) {
$cells = array();
$cells[] = '<label>' . get_string('correctanswerscore', 'lesson') . '</label>: ';
$cells[] = $answer->score;
$table->data[] = new html_table_row($cells);
$cells = array();
$cells[] = '<label>' . get_string('correctanswerjump', 'lesson') . '</label>: ';
$cells[] = $this->get_jump_name($answer->jumpto);
$table->data[] = new html_table_row($cells);
} elseif ($n == 1) {
$cells = array();
$cells[] = '<label>' . get_string('wronganswerscore', 'lesson') . '</label>: ';
$cells[] = $answer->score;
$table->data[] = new html_table_row($cells);
$cells = array();
$cells[] = '<label>' . get_string('wronganswerjump', 'lesson') . '</label>: ';
$cells[] = $this->get_jump_name($answer->jumpto);
$table->data[] = new html_table_row($cells);
}
if ($n === 0){
$table->data[count($table->data)-1]->cells[0]->style = 'width:20%;';
}
$n++;
$i--;
} else {
$cells = array();
if ($this->lesson->custom && $answer->score > 0) {
// if the score is > 0, then it is correct
$cells[] = '<label class="correct">' . get_string('answer', 'lesson') . " {$i}</label>: \n";
} else if ($this->lesson->custom) {
$cells[] = '<label>' . get_string('answer', 'lesson') . " {$i}</label>: \n";
} else if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
$cells[] = '<label class="correct">' . get_string('answer', 'lesson') . " {$i}</label>: \n";
} else {
$cells[] = '<label>' . get_string('answer', 'lesson') . " {$i}</label>: \n";
}
$cells[] = format_text($answer->answer, $answer->answerformat, $options);
$table->data[] = new html_table_row($cells);
$cells = array();
$cells[] = '<label>' . get_string('matchesanswer', 'lesson') . " {$i}</label>: \n";
$cells[] = format_text($answer->response, $answer->responseformat, $options);
$table->data[] = new html_table_row($cells);
}
$i++;
}
return $table;
}
/**
* Updates the page and its answers
*
* @global moodle_database $DB
* @global moodle_page $PAGE
* @param stdClass $properties
* @return bool
*/
public function update($properties, $context = null, $maxbytes = null) {
global $DB, $PAGE;
$answers = $this->get_answers();
$properties->id = $this->properties->id;
$properties->lessonid = $this->lesson->id;
$properties->timemodified = time();
$properties = file_postupdate_standard_editor($properties, 'contents', array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes), context_module::instance($PAGE->cm->id), 'mod_lesson', 'page_contents', $properties->id);
$DB->update_record("lesson_pages", $properties);
// Trigger an event: page updated.
\mod_lesson\event\page_updated::create_from_lesson_page($this, $context)->trigger();
// need to add two to offset correct response and wrong response
$this->lesson->maxanswers += 2;
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;
}
if (!empty($properties->answer_editor[$i]) && is_array($properties->answer_editor[$i])) {
$this->answers[$i]->answer = $properties->answer_editor[$i]['text'];
$this->answers[$i]->answerformat = $properties->answer_editor[$i]['format'];
}
if (!empty($properties->response_editor[$i])) {
$this->answers[$i]->response = $properties->response_editor[$i];
$this->answers[$i]->responseformat = 0;
}
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];
}
// we don't need to check for isset here because properties called it's own isset method.
if ($this->answers[$i]->answer != '') {
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());
}
// Save files in answers (no response_editor for matching questions).
$this->save_answers_files($context, $maxbytes, $this->answers[$i], $properties->answer_editor[$i]);
} else if ($i < 2) {
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());
}
// Save files in answers (no response_editor for matching questions).
$this->save_answers_files($context, $maxbytes, $this->answers[$i], $properties->answer_editor[$i]);
} else if (isset($this->answers[$i]->id)) {
$DB->delete_records('lesson_answers', array('id'=>$this->answers[$i]->id));
unset($this->answers[$i]);
}
}
return true;
}
public function stats(array &$pagestats, $tries) {
$temp = $this->lesson->get_last_attempt($tries);
if ($temp->correct) {
if (isset($pagestats[$temp->pageid]["correct"])) {
$pagestats[$temp->pageid]["correct"]++;
} else {
$pagestats[$temp->pageid]["correct"] = 1;
}
}
if (isset($pagestats[$temp->pageid]["total"])) {
$pagestats[$temp->pageid]["total"]++;
} else {
$pagestats[$temp->pageid]["total"] = 1;
}
return true;
}
public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) {
$answers = array();
foreach ($this->get_answers() as $answer) {
$answers[$answer->id] = $answer;
}
$formattextdefoptions = new stdClass;
$formattextdefoptions->para = false; //I'll use it widely in this page
foreach ($answers as $answer) {
if ($n == 0 && $useranswer != null && $useranswer->correct) {
if ($answer->response == null && $useranswer != null) {
$answerdata->response = get_string("thatsthecorrectanswer", "lesson");
} else {
$answerdata->response = $answer->response;
}
if ($this->lesson->custom) {
$answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
} else {
$answerdata->score = get_string("receivedcredit", "lesson");
}
} elseif ($n == 1 && $useranswer != null && !$useranswer->correct) {
if ($answer->response == null && $useranswer != null) {
$answerdata->response = get_string("thatsthewronganswer", "lesson");
} else {
$answerdata->response = $answer->response;
}
if ($this->lesson->custom) {
$answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
} else {
$answerdata->score = get_string("didnotreceivecredit", "lesson");
}
} elseif ($n > 1) {
$data = '<label class="accesshide" for="answer_' . $n . '">' . get_string('answer', 'lesson') . '</label>';
$data .= strip_tags(format_string($answer->answer)) . ' ';
if ($useranswer != null) {
$userresponse = explode(",", $useranswer->useranswer);
$data .= '<label class="accesshide" for="stu_answer_response_' . $n . '">' . get_string('matchesanswer', 'lesson') . '</label>';
$data .= "<select class=\"custom-select\" id=\"stu_answer_response_" . $n . "\" " .
"disabled=\"disabled\"><option selected=\"selected\">";
if (array_key_exists($i, $userresponse)) {
$data .= $userresponse[$i];
}
$data .= "</option></select>";
} else {
$data .= '<label class="accesshide" for="answer_response_' . $n . '">' . get_string('matchesanswer', 'lesson') . '</label>';
$data .= "<select class=\"custom-select\" id=\"answer_response_" . $n . "\" " .
"disabled=\"disabled\"><option selected=\"selected\">".strip_tags(format_string($answer->response))."</option></select>";
}
if ($n == 2) {
if (isset($pagestats[$this->properties->id])) {
if (!array_key_exists('correct', $pagestats[$this->properties->id])) {
$pagestats[$this->properties->id]["correct"] = 0;
}
$percent = $pagestats[$this->properties->id]["correct"] / $pagestats[$this->properties->id]["total"] * 100;
$percent = round($percent, 2);
$percent .= "% ".get_string("answeredcorrectly", "lesson");
} else {
$percent = get_string("nooneansweredthisquestion", "lesson");
}
} else {
$percent = '';
}
$answerdata->answers[] = array($data, $percent);
$i++;
}
$n++;
$answerpage->answerdata = $answerdata;
}
return $answerpage;
}
public function get_jumps() {
global $DB;
// The jumps for matching question type are stored in the 1st and 2nd answer record.
$jumps = array();
if ($answers = $DB->get_records("lesson_answers", array("lessonid" => $this->lesson->id, "pageid" => $this->properties->id), 'id', '*', 0, 2)) {
foreach ($answers as $answer) {
$jumps[] = $this->get_jump_name($answer->jumpto);
}
} else {
$jumps[] = $this->get_jump_name($this->properties->nextpageid);
}
return $jumps;
}
}
class lesson_add_page_form_matching extends lesson_add_page_form_base {
public $qtype = 'matching';
public $qtypestring = 'matching';
protected $answerformat = LESSON_ANSWER_HTML;
protected $responseformat = '';
public function custom_definition() {
$this->_form->addElement('header', 'correctresponse', get_string('correctresponse', 'lesson'));
$this->_form->addElement('editor', 'answer_editor[0]', get_string('correctresponse', 'lesson'),
array('rows' => '4', 'columns' => '80'),
array('noclean' => true, 'maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes' => $this->_customdata['maxbytes']));
$this->_form->setType('answer_editor[0]', PARAM_RAW);
$this->_form->setDefault('answer_editor[0]', array('text' => '', 'format' => FORMAT_HTML));
$this->add_jumpto(0, get_string('correctanswerjump','lesson'), LESSON_NEXTPAGE);
$this->add_score(0, get_string("correctanswerscore", "lesson"), 1);
$this->_form->addElement('header', 'wrongresponse', get_string('wrongresponse', 'lesson'));
$this->_form->addElement('editor', 'answer_editor[1]', get_string('wrongresponse', 'lesson'),
array('rows' => '4', 'columns' => '80'),
array('noclean' => true, 'maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes' => $this->_customdata['maxbytes']));
$this->_form->setType('answer_editor[1]', PARAM_RAW);
$this->_form->setDefault('answer_editor[1]', array('text' => '', 'format' => FORMAT_HTML));
$this->add_jumpto(1, get_string('wronganswerjump','lesson'), LESSON_THISPAGE);
$this->add_score(1, get_string("wronganswerscore", "lesson"), 0);
for ($i = 2; $i < $this->_customdata['lesson']->maxanswers+2; $i++) {
$this->_form->addElement('header', 'matchingpair'.($i-1), get_string('matchingpair', 'lesson', $i-1));
$this->add_answer($i, null, ($i < 4), LESSON_ANSWER_HTML);
$required = ($i < 4);
$label = get_string('matchesanswer','lesson');
$count = $i;
$this->_form->addElement('text', 'response_editor['.$count.']', $label, array('size'=>'50'));
$this->_form->setType('response_editor['.$count.']', PARAM_NOTAGS);
$this->_form->setDefault('response_editor['.$count.']', '');
if ($required) {
$this->_form->addRule('response_editor['.$count.']', get_string('required'), 'required', null, 'client');
}
}
}
}
class lesson_display_answer_form_matching extends moodleform {
public function definition() {
global $USER, $OUTPUT, $PAGE;
$mform = $this->_form;
$answers = $this->_customdata['answers'];
$useranswers = $this->_customdata['useranswers'];
$responseoptions = $this->_customdata['responseoptions'];
$lessonid = $this->_customdata['lessonid'];
$contents = $this->_customdata['contents'];
// Disable shortforms.
$mform->setDisableShortforms();
$mform->addElement('header', 'pageheader');
$mform->addElement('html', $OUTPUT->container($contents, 'contents'));
$hasattempt = false;
$disabled = '';
if (isset($useranswers) && !empty($useranswers)) {
$hasattempt = true;
$disabled = array('disabled' => 'disabled');
}
$options = new stdClass;
$options->para = false;
$options->noclean = true;
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'pageid');
$mform->setType('pageid', PARAM_INT);
$i = 0;
foreach ($answers as $answer) {
$mform->addElement('html', '<div class="answeroption">');
if ($answer->response != null) {
$responseid = 'response['.$answer->id.']';
if ($hasattempt) {
$responseid = 'response_'.$answer->id;
$mform->addElement('hidden', 'response['.$answer->id.']', htmlspecialchars($useranswers[$i], ENT_COMPAT));
// Temporary fixed until MDL-38885 gets integrated
$mform->setType('response', PARAM_TEXT);
}
$answer = lesson_page_type_matching::rewrite_answers_urls($answer);
$mform->addElement('select', $responseid, format_text($answer->answer,$answer->answerformat,$options), $responseoptions, $disabled);
$mform->setType($responseid, PARAM_TEXT);
if ($hasattempt) {
$mform->setDefault($responseid, htmlspecialchars(trim($useranswers[$i]), ENT_COMPAT));
} else {
$mform->setDefault($responseid, 'answeroption');
}
}
$mform->addElement('html', '</div>');
$i++;
}
if ($hasattempt) {
$this->add_action_buttons(null, get_string("nextpage", "lesson"));
} else {
$this->add_action_buttons(null, get_string("submit", "lesson"));
}
}
}
+579
View File
@@ -0,0 +1,579 @@
<?php
// 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/>.
/**
* Multichoice
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
defined('MOODLE_INTERNAL') || die();
/** Multichoice question type */
define("LESSON_PAGE_MULTICHOICE", "3");
class lesson_page_type_multichoice extends lesson_page {
protected $type = lesson_page::TYPE_QUESTION;
protected $typeidstring = 'multichoice';
protected $typeid = LESSON_PAGE_MULTICHOICE;
protected $string = null;
public function get_typeid() {
return $this->typeid;
}
public function get_typestring() {
if ($this->string===null) {
$this->string = get_string($this->typeidstring, 'lesson');
}
return $this->string;
}
public function get_idstring() {
return $this->typeidstring;
}
/**
* Gets an array of the jumps used by the answers of this page
*
* @return array
*/
public function get_jumps() {
global $DB;
$jumps = array();
if ($answers = $this->get_answers()) {
foreach ($answers as $answer) {
if ($answer->answer === '') {
// show only jumps for real branches (==have description)
continue;
}
$jumps[] = $this->get_jump_name($answer->jumpto);
}
} else {
// We get here is the lesson was created on a Moodle 1.9 site and
// the lesson contains question pages without any answers.
$jumps[] = $this->get_jump_name($this->properties->nextpageid);
}
return $jumps;
}
public function get_used_answers() {
$answers = $this->get_answers();
foreach ($answers as $key=>$answer) {
if ($answer->answer === '') {
unset($answers[$key]);
} else {
$answers[$key] = parent::rewrite_answers_urls($answer);
}
}
return $answers;
}
public function display($renderer, $attempt) {
global $CFG, $PAGE;
$answers = $this->get_used_answers();
shuffle($answers);
$action = $CFG->wwwroot.'/mod/lesson/continue.php';
$params = array('answers'=>$answers, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents(), 'attempt'=>$attempt);
if ($this->properties->qoption) {
$mform = new lesson_display_answer_form_multichoice_multianswer($action, $params);
} else {
$mform = new lesson_display_answer_form_multichoice_singleanswer($action, $params);
}
$data = new stdClass;
$data->id = $PAGE->cm->id;
$data->pageid = $this->properties->id;
$mform->set_data($data);
// Trigger an event question viewed.
$eventparams = array(
'context' => context_module::instance($PAGE->cm->id),
'objectid' => $this->properties->id,
'other' => array(
'pagetype' => $this->get_typestring()
)
);
$event = \mod_lesson\event\question_viewed::create($eventparams);
$event->trigger();
return $mform->display();
}
public function check_answer() {
global $DB, $CFG, $PAGE;
$result = parent::check_answer();
$formattextdefoptions = new stdClass();
$formattextdefoptions->noclean = true;
$formattextdefoptions->para = false;
$answers = $this->get_used_answers();
shuffle($answers);
$action = $CFG->wwwroot.'/mod/lesson/continue.php';
$params = array('answers'=>$answers, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents());
if ($this->properties->qoption) {
$mform = new lesson_display_answer_form_multichoice_multianswer($action, $params);
} else {
$mform = new lesson_display_answer_form_multichoice_singleanswer($action, $params);
}
$data = $mform->get_data();
require_sesskey();
if (!$data) {
$result->inmediatejump = true;
$result->newpageid = $this->properties->id;
return $result;
}
if ($this->properties->qoption) {
// Multianswer allowed, user's answer is an array
if (empty($data->answer) || !is_array($data->answer)) {
$result->noanswer = true;
return $result;
}
$studentanswers = array();
foreach ($data->answer as $key=>$value) {
$studentanswers[] = (int)$key;
}
// get what the user answered
$result->userresponse = implode(",", $studentanswers);
// get the answers in a set order, the id order
$answers = $this->get_used_answers();
$ncorrect = 0;
$nhits = 0;
$responses = array();
$correctanswerid = 0;
$wronganswerid = 0;
// store student's answers for displaying on feedback page
$result->studentanswer = '';
$result->studentanswerformat = FORMAT_HTML;
foreach ($answers as $answer) {
foreach ($studentanswers as $answerid) {
if ($answerid == $answer->id) {
$studentanswerarray[] = format_text($answer->answer, $answer->answerformat, $formattextdefoptions);
$responses[$answerid] = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
}
}
}
$result->studentanswer = implode(self::MULTIANSWER_DELIMITER, $studentanswerarray);
$correctpageid = null;
$wrongpageid = null;
// Iterate over all the possible answers.
foreach ($answers as $answer) {
if ($this->lesson->custom) {
$iscorrectanswer = $answer->score > 0;
} else {
$iscorrectanswer = $this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto);
}
// Iterate over all the student answers to check if he selected the current possible answer.
foreach ($studentanswers as $answerid) {
if ($answerid == $answer->id) {
if ($iscorrectanswer) {
$nhits++;
} else {
// Always jump to the page related to the student's first wrong answer.
if (!isset($wrongpageid)) {
// Leave in its "raw" state - will be converted into a proper page id later.
$wrongpageid = $answer->jumpto;
}
// Save the answer id for scoring.
if ($wronganswerid == 0) {
$wronganswerid = $answer->id;
}
}
}
}
if ($iscorrectanswer) {
$ncorrect++;
// Save the first jumpto page id, may be needed!
if (!isset($correctpageid)) {
// Leave in its "raw" state - will be converted into a proper page id later.
$correctpageid = $answer->jumpto;
}
// Save the answer id for scoring.
if ($correctanswerid == 0) {
$correctanswerid = $answer->id;
}
}
}
if ((count($studentanswers) == $ncorrect) and ($nhits == $ncorrect)) {
$result->correctanswer = true;
$result->response = implode(self::MULTIANSWER_DELIMITER, $responses);
$result->newpageid = $correctpageid;
$result->answerid = $correctanswerid;
} else {
$result->response = implode(self::MULTIANSWER_DELIMITER, $responses);
$result->newpageid = $wrongpageid;
$result->answerid = $wronganswerid;
}
} else {
// only one answer allowed
if (!isset($data->answerid) || (empty($data->answerid) && !is_int($data->answerid))) {
$result->noanswer = true;
return $result;
}
$result->answerid = $data->answerid;
if (!$answer = $DB->get_record("lesson_answers", array("id" => $result->answerid))) {
throw new \moodle_exception("Continue: answer record not found");
}
$answer = parent::rewrite_answers_urls($answer);
if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
$result->correctanswer = true;
}
if ($this->lesson->custom) {
if ($answer->score > 0) {
$result->correctanswer = true;
} else {
$result->correctanswer = false;
}
}
$result->newpageid = $answer->jumpto;
$result->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
$result->userresponse = format_text($answer->answer, $answer->answerformat, $formattextdefoptions);
$result->studentanswer = $result->userresponse;
}
return $result;
}
public function option_description_string() {
if ($this->properties->qoption) {
return " - ".get_string("multianswer", "lesson");
}
return parent::option_description_string();
}
public function display_answers(html_table $table) {
$answers = $this->get_used_answers();
$options = new stdClass;
$options->noclean = true;
$options->para = false;
$i = 1;
foreach ($answers as $answer) {
$answer = parent::rewrite_answers_urls($answer);
$cells = array();
if ($this->lesson->custom && $answer->score > 0) {
// if the score is > 0, then it is correct
$cells[] = '<label class="correct">' . get_string('answer', 'lesson') . " {$i}</label>: \n";
} else if ($this->lesson->custom) {
$cells[] = '<label>' . get_string('answer', 'lesson') . " {$i}</label>: \n";
} else if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
// underline correct answers
$cells[] = '<span class="correct">' . get_string('answer', 'lesson') . " {$i}</span>: \n";
} else {
$cells[] = '<label class="correct">' . get_string('answer', 'lesson') . " {$i}</label>: \n";
}
$cells[] = format_text($answer->answer, $answer->answerformat, $options);
$table->data[] = new html_table_row($cells);
$cells = array();
$cells[] = '<label>' . get_string('response', 'lesson') . " {$i} </label>:\n";
$cells[] = format_text($answer->response, $answer->responseformat, $options);
$table->data[] = new html_table_row($cells);
$cells = array();
$cells[] = '<label>' . get_string('score', 'lesson') . '</label>:';
$cells[] = $answer->score;
$table->data[] = new html_table_row($cells);
$cells = array();
$cells[] = '<label>' . get_string('jump', 'lesson') . '</label>:';
$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;
}
public function stats(array &$pagestats, $tries) {
$temp = $this->lesson->get_last_attempt($tries);
if ($this->properties->qoption) {
$userresponse = explode(",", $temp->useranswer);
foreach ($userresponse as $response) {
if (isset($pagestats[$temp->pageid][$response])) {
$pagestats[$temp->pageid][$response]++;
} else {
$pagestats[$temp->pageid][$response] = 1;
}
}
} else {
if (isset($pagestats[$temp->pageid][$temp->answerid])) {
$pagestats[$temp->pageid][$temp->answerid]++;
} else {
$pagestats[$temp->pageid][$temp->answerid] = 1;
}
}
if (isset($pagestats[$temp->pageid]["total"])) {
$pagestats[$temp->pageid]["total"]++;
} else {
$pagestats[$temp->pageid]["total"] = 1;
}
return true;
}
public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) {
$answers = $this->get_used_answers();
$formattextdefoptions = new stdClass;
$formattextdefoptions->para = false; //I'll use it widely in this page
$formattextdefoptions->context = $answerpage->context;
foreach ($answers as $answer) {
$answertext = format_text($answer->answer,$answer->answerformat,$formattextdefoptions);
$correctresponsetext = html_writer::div(get_string('correctresponse', 'lesson'), 'badge bg-success text-white');
if ($this->properties->qoption) {
if ($useranswer == null) {
$userresponse = array();
} else {
$userresponse = explode(",", $useranswer->useranswer);
}
if (in_array($answer->id, $userresponse)) {
// make checked
$checkboxelement = "<input readonly=\"readonly\" disabled=\"disabled\" name=\"answer[$i]\" checked=\"checked\" type=\"checkbox\" value=\"1\" />";
if (!isset($answerdata->response)) {
if ($answer->response == null) {
if ($useranswer->correct) {
$answerdata->response = get_string("thatsthecorrectanswer", "lesson");
} else {
$answerdata->response = get_string("thatsthewronganswer", "lesson");
}
} else {
$answerdata->response = $answer->response;
}
}
if (!isset($answerdata->score)) {
if ($this->lesson->custom) {
$answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
} elseif ($useranswer->correct) {
$answerdata->score = get_string("receivedcredit", "lesson");
} else {
$answerdata->score = get_string("didnotreceivecredit", "lesson");
}
}
} else {
// unchecked
$checkboxelement = "<input type=\"checkbox\" readonly=\"readonly\" name=\"answer[$i]\" value=\"0\" disabled=\"disabled\" />";
}
$answercontent = html_writer::label($checkboxelement . ' ' . $answertext, null);
if (($answer->score > 0 && $this->lesson->custom) || ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto) && !$this->lesson->custom)) {
$data = html_writer::div($answercontent, 'text-success') . $correctresponsetext;
} else {
$data = $answercontent;
}
} else {
if ($useranswer != null and $answer->id == $useranswer->answerid) {
// make checked
$checkboxelement = "<input readonly=\"readonly\" disabled=\"disabled\" name=\"answer[$i]\" checked=\"checked\" type=\"checkbox\" value=\"1\" />";
if ($answer->response == null) {
if ($useranswer->correct) {
$answerdata->response = get_string("thatsthecorrectanswer", "lesson");
} else {
$answerdata->response = get_string("thatsthewronganswer", "lesson");
}
} else {
$answerdata->response = $answer->response;
}
if ($this->lesson->custom) {
$answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
} elseif ($useranswer->correct) {
$answerdata->score = get_string("receivedcredit", "lesson");
} else {
$answerdata->score = get_string("didnotreceivecredit", "lesson");
}
} else {
// unchecked
$checkboxelement = "<input type=\"checkbox\" readonly=\"readonly\" name=\"answer[$i]\" value=\"0\" disabled=\"disabled\" />";
}
$answercontent = html_writer::label($checkboxelement . ' ' . $answertext, null);
if (($answer->score > 0 && $this->lesson->custom) || ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto) && !$this->lesson->custom)) {
$data = html_writer::div($answercontent, 'text-success') . $correctresponsetext;
} else {
$data = $answercontent;
}
}
if (isset($pagestats[$this->properties->id][$answer->id])) {
$percent = $pagestats[$this->properties->id][$answer->id] / $pagestats[$this->properties->id]["total"] * 100;
$percent = round($percent, 2);
$percent .= "% ".get_string("checkedthisone", "lesson");
} else {
$percent = get_string("noonecheckedthis", "lesson");
}
$answerdata->answers[] = array($data, $percent);
$answerpage->answerdata = $answerdata;
}
return $answerpage;
}
}
class lesson_add_page_form_multichoice extends lesson_add_page_form_base {
public $qtype = 'multichoice';
public $qtypestring = 'multichoice';
protected $answerformat = LESSON_ANSWER_HTML;
protected $responseformat = LESSON_ANSWER_HTML;
public function custom_definition() {
$this->_form->addElement('checkbox', 'qoption', get_string('options', 'lesson'), get_string('multianswer', 'lesson'));
$this->_form->setDefault('qoption', 0);
$this->_form->addHelpButton('qoption', 'multianswer', 'lesson');
for ($i = 0; $i < $this->_customdata['lesson']->maxanswers; $i++) {
$this->_form->addElement('header', 'answertitle'.$i, get_string('answer').' '.($i+1));
$this->add_answer($i, null, ($i<2), $this->get_answer_format());
$this->add_response($i);
$this->add_jumpto($i, null, ($i == 0 ? LESSON_NEXTPAGE : LESSON_THISPAGE));
$this->add_score($i, null, ($i===0)?1:0);
}
}
}
class lesson_display_answer_form_multichoice_singleanswer extends moodleform {
public function definition() {
global $USER, $OUTPUT;
$mform = $this->_form;
$answers = $this->_customdata['answers'];
$lessonid = $this->_customdata['lessonid'];
$contents = $this->_customdata['contents'];
if (array_key_exists('attempt', $this->_customdata)) {
$attempt = $this->_customdata['attempt'];
} else {
$attempt = new stdClass();
$attempt->answerid = null;
}
// Disable shortforms.
$mform->setDisableShortforms();
$mform->addElement('header', 'pageheader');
$mform->addElement('html', $OUTPUT->container($contents, 'contents'));
$hasattempt = false;
$disabled = '';
if (isset($USER->modattempts[$lessonid]) && !empty($USER->modattempts[$lessonid])) {
$hasattempt = true;
$disabled = array('disabled' => 'disabled');
}
$options = new stdClass;
$options->para = false;
$options->noclean = true;
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'pageid');
$mform->setType('pageid', PARAM_INT);
$i = 0;
foreach ($answers as $answer) {
$mform->addElement('html', '<div class="answeroption">');
$answer->answer = preg_replace('#>$#', '> ', $answer->answer);
$mform->addElement('radio','answerid',null,format_text($answer->answer, $answer->answerformat, $options),$answer->id, $disabled);
$mform->setType('answer'.$i, PARAM_INT);
if ($hasattempt && $answer->id == $USER->modattempts[$lessonid]->answerid) {
$mform->setDefault('answerid', $USER->modattempts[$lessonid]->answerid);
}
$mform->addElement('html', '</div>');
$i++;
}
if ($hasattempt) {
$this->add_action_buttons(null, get_string("nextpage", "lesson"));
} else {
$this->add_action_buttons(null, get_string("submit", "lesson"));
}
}
}
class lesson_display_answer_form_multichoice_multianswer extends moodleform {
public function definition() {
global $USER, $OUTPUT;
$mform = $this->_form;
$answers = $this->_customdata['answers'];
$lessonid = $this->_customdata['lessonid'];
$contents = $this->_customdata['contents'];
// Disable shortforms.
$mform->setDisableShortforms();
$mform->addElement('header', 'pageheader');
$mform->addElement('html', $OUTPUT->container($contents, 'contents'));
$hasattempt = false;
$disabled = '';
$useranswers = array();
if (isset($USER->modattempts[$lessonid]) && !empty($USER->modattempts[$lessonid])) {
$hasattempt = true;
$disabled = array('disabled' => 'disabled');
$useranswers = explode(',', $USER->modattempts[$lessonid]->useranswer);
}
$options = new stdClass;
$options->para = false;
$options->noclean = true;
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'pageid');
$mform->setType('pageid', PARAM_INT);
foreach ($answers as $answer) {
$mform->addElement('html', '<div class="answeroption">');
$answerid = 'answer['.$answer->id.']';
if ($hasattempt && in_array($answer->id, $useranswers)) {
$answerid = 'answer_'.$answer->id;
$mform->addElement('hidden', 'answer['.$answer->id.']', $answer->answer);
$mform->setType('answer['.$answer->id.']', PARAM_NOTAGS);
$mform->setDefault($answerid, true);
$mform->setDefault('answer['.$answer->id.']', true);
}
// NOTE: our silly checkbox supports only value '1' - we can not use it like the radiobox above!!!!!!
$answer->answer = preg_replace('#>$#', '> ', $answer->answer);
$mform->addElement('checkbox', $answerid, null, format_text($answer->answer, $answer->answerformat, $options), $disabled);
$mform->setType($answerid, PARAM_INT);
$mform->addElement('html', '</div>');
}
if ($hasattempt) {
$this->add_action_buttons(null, get_string("nextpage", "lesson"));
} else {
$this->add_action_buttons(null, get_string("submit", "lesson"));
}
}
}
+501
View File
@@ -0,0 +1,501 @@
<?php
// 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/>.
/**
* Numerical
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
defined('MOODLE_INTERNAL') || die();
/** Numerical question type */
define("LESSON_PAGE_NUMERICAL", "8");
use mod_lesson\local\numeric\helper;
class lesson_page_type_numerical extends lesson_page {
protected $type = lesson_page::TYPE_QUESTION;
protected $typeidstring = 'numerical';
protected $typeid = LESSON_PAGE_NUMERICAL;
protected $string = null;
public function get_typeid() {
return $this->typeid;
}
public function get_typestring() {
if ($this->string===null) {
$this->string = get_string($this->typeidstring, 'lesson');
}
return $this->string;
}
public function get_idstring() {
return $this->typeidstring;
}
public function display($renderer, $attempt) {
global $USER, $PAGE;
$mform = new lesson_display_answer_form_numerical(new moodle_url('/mod/lesson/continue.php'),
array('contents' => $this->get_contents(), 'lessonid' => $this->lesson->id));
$data = new stdClass;
$data->id = $PAGE->cm->id;
$data->pageid = $this->properties->id;
if (isset($USER->modattempts[$this->lesson->id])) {
$data->answer = s($attempt->useranswer);
}
$mform->set_data($data);
// Trigger an event question viewed.
$eventparams = array(
'context' => context_module::instance($PAGE->cm->id),
'objectid' => $this->properties->id,
'other' => array(
'pagetype' => $this->get_typestring()
)
);
$event = \mod_lesson\event\question_viewed::create($eventparams);
$event->trigger();
return $mform->display();
}
/**
* Creates answers for this page type.
*
* @param object $properties The answer properties.
*/
public function create_answers($properties) {
if (isset($properties->enableotheranswers) && $properties->enableotheranswers) {
$properties->response_editor = array_values($properties->response_editor);
$properties->jumpto = array_values($properties->jumpto);
$properties->score = array_values($properties->score);
$wrongresponse = end($properties->response_editor);
$wrongkey = key($properties->response_editor);
$properties->answer_editor[$wrongkey] = LESSON_OTHER_ANSWERS;
}
parent::create_answers($properties);
}
/**
* Update the answers for this page type.
*
* @param object $properties The answer properties.
* @param context $context The context for this module.
* @param int $maxbytes The maximum bytes for any uploades.
*/
public function update($properties, $context = null, $maxbytes = null) {
if ($properties->enableotheranswers) {
$properties->response_editor = array_values($properties->response_editor);
$properties->jumpto = array_values($properties->jumpto);
$properties->score = array_values($properties->score);
$wrongresponse = end($properties->response_editor);
$wrongkey = key($properties->response_editor);
$properties->answer_editor[$wrongkey] = LESSON_OTHER_ANSWERS;
}
parent::update($properties, $context, $maxbytes);
}
public function check_answer() {
$result = parent::check_answer();
$mform = new lesson_display_answer_form_numerical(new moodle_url('/mod/lesson/continue.php'),
array('contents' => $this->get_contents()));
$data = $mform->get_data();
require_sesskey();
$formattextdefoptions = new stdClass();
$formattextdefoptions->noclean = true;
$formattextdefoptions->para = false;
// set defaults
$result->response = '';
$result->newpageid = 0;
if (!isset($data->answer)) {
$result->noanswer = true;
return $result;
} else {
$result->useranswer = $data->answer;
}
$result->studentanswer = $result->userresponse = $result->useranswer;
$answers = $this->get_answers();
foreach ($answers as $answer) {
$answer = parent::rewrite_answers_urls($answer);
if (strpos($answer->answer, ':')) {
// there's a pairs of values
list($min, $max) = explode(':', $answer->answer);
$minimum = (float) $min;
$maximum = (float) $max;
} else {
// there's only one value
$minimum = (float) $answer->answer;
$maximum = $minimum;
}
if (($result->useranswer >= $minimum) && ($result->useranswer <= $maximum)) {
$result->newpageid = $answer->jumpto;
$result->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
if ($this->lesson->jumpto_is_correct($this->properties->id, $result->newpageid)) {
$result->correctanswer = true;
}
if ($this->lesson->custom) {
if ($answer->score > 0) {
$result->correctanswer = true;
} else {
$result->correctanswer = false;
}
}
$result->answerid = $answer->id;
return $result;
}
}
// We could check here to see if we have a wrong answer jump to use.
if ($result->answerid == 0) {
// Use the all other answers jump details if it is set up.
$lastanswer = end($answers);
// Double check that this is the @#wronganswer#@ answer.
if (strpos($lastanswer->answer, LESSON_OTHER_ANSWERS) !== false) {
$otheranswers = end($answers);
$result->newpageid = $otheranswers->jumpto;
$result->response = format_text($otheranswers->response, $otheranswers->responseformat, $formattextdefoptions);
// Does this also need to do the jumpto_is_correct?
if ($this->lesson->custom) {
$result->correctanswer = ($otheranswers->score > 0);
}
$result->answerid = $otheranswers->id;
}
}
return $result;
}
public function display_answers(html_table $table) {
$answers = $this->get_answers();
$options = new stdClass;
$options->noclean = true;
$options->para = false;
$i = 1;
foreach ($answers as $answer) {
$answer = parent::rewrite_answers_urls($answer, false);
$cells = array();
if ($this->lesson->custom && $answer->score > 0) {
// if the score is > 0, then it is correct
$cells[] = '<label class="correct">' . get_string('answer', 'lesson') . ' ' . $i . '</label>:';
} else if ($this->lesson->custom) {
$cells[] = '<label>' . get_string('answer', 'lesson') . ' ' . $i . '</label>:';
} else if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
// underline correct answers
$cells[] = '<span class="correct">' . get_string('answer', 'lesson') . ' ' . $i . '</span>:' . "\n";
} else {
$cells[] = '<label class="correct">' . get_string('answer', 'lesson') . ' ' . $i . '</label>:';
}
$formattedanswer = helper::lesson_format_numeric_value($answer->answer);
$cells[] = format_text($formattedanswer, $answer->answerformat, $options);
$table->data[] = new html_table_row($cells);
$cells = array();
$cells[] = '<label>' . get_string('response', 'lesson') . ' ' . $i . '</label>:';
$cells[] = format_text($answer->response, $answer->responseformat, $options);
$table->data[] = new html_table_row($cells);
$cells = array();
$cells[] = '<label>' . get_string('score', 'lesson') . '</label>:';
$cells[] = $answer->score;
$table->data[] = new html_table_row($cells);
$cells = array();
$cells[] = '<label>' . get_string('jump', 'lesson') . '</label>:';
$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;
}
public function stats(array &$pagestats, $tries) {
$temp = $this->lesson->get_last_attempt($tries);
if (isset($pagestats[$temp->pageid][$temp->useranswer])) {
$pagestats[$temp->pageid][$temp->useranswer]++;
} else {
$pagestats[$temp->pageid][$temp->useranswer] = 1;
}
if (isset($pagestats[$temp->pageid]["total"])) {
$pagestats[$temp->pageid]["total"]++;
} else {
$pagestats[$temp->pageid]["total"] = 1;
}
return true;
}
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) {
if ($useranswer == null && $i == 0) {
// I have the $i == 0 because it is easier to blast through it all at once.
if (isset($pagestats[$this->properties->id])) {
$stats = $pagestats[$this->properties->id];
$total = $stats["total"];
unset($stats["total"]);
foreach ($stats as $valentered => $ntimes) {
$valformatted = '';
if (!is_null($valentered) && trim($valentered) !== '') { // Empty response, 0 could be ok.
$valformatted = s(format_float($valentered, strlen($valentered), true, true));
}
$data = '<input class="form-control" type="text" size="50" ' .
'disabled="disabled" readonly="readonly" value="'. $valformatted .'" />';
$percent = $ntimes / $total * 100;
$percent = round($percent, 2);
$percent .= "% ".get_string("enteredthis", "lesson");
$answerdata->answers[] = array($data, $percent);
}
} else {
$answerdata->answers[] = array(get_string("nooneansweredthisquestion", "lesson"), " ");
}
$i++;
} else if ($useranswer != null && ($answer->id == $useranswer->answerid || ($answer == end($answers) &&
empty($answerdata->answers)))) {
// Get in here when the user answered or for the last answer.
$valformatted = '';
if (!is_null($useranswer->useranswer) && trim($useranswer->useranswer) !== '') { // Empty response, 0 could be ok.
$valformatted = s(format_float($useranswer->useranswer, strlen($useranswer->useranswer), true, true));
}
$data = '<input class="form-control" type="text" size="50" ' .
'disabled="disabled" readonly="readonly" value="' . $valformatted .'">';
if (isset($pagestats[$this->properties->id][$useranswer->useranswer])) {
$percent = $pagestats[$this->properties->id][$useranswer->useranswer] / $pagestats[$this->properties->id]["total"] * 100;
$percent = round($percent, 2);
$percent .= "% ".get_string("enteredthis", "lesson");
} else {
$percent = get_string("nooneenteredthis", "lesson");
}
$answerdata->answers[] = array($data, $percent);
if ($answer->id == $useranswer->answerid) {
if ($answer->response == null) {
if ($useranswer->correct) {
$answerdata->response = get_string("thatsthecorrectanswer", "lesson");
} else {
$answerdata->response = get_string("thatsthewronganswer", "lesson");
}
} else {
$answerdata->response = $answer->response;
}
if ($this->lesson->custom) {
$answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
} elseif ($useranswer->correct) {
$answerdata->score = get_string("receivedcredit", "lesson");
} else {
$answerdata->score = get_string("didnotreceivecredit", "lesson");
}
} else {
$answerdata->response = get_string("thatsthewronganswer", "lesson");
if ($this->lesson->custom) {
$answerdata->score = get_string("pointsearned", "lesson").": 0";
} else {
$answerdata->score = get_string("didnotreceivecredit", "lesson");
}
}
}
$answerpage->answerdata = $answerdata;
}
return $answerpage;
}
/**
* Make updates to the form data if required. In this case to put the all other answer data into the write section of the form.
*
* @param stdClass $data The form data to update.
* @return stdClass The updated fom data.
*/
public function update_form_data(stdClass $data): stdClass {
$answercount = count($this->get_answers());
// If no answers provided, then we don't need to check anything.
if (!$answercount) {
return $data;
}
// Check for other answer entry.
$lastanswer = $data->{'answer_editor[' . ($answercount - 1) . ']'};
if (strpos($lastanswer, LESSON_OTHER_ANSWERS) !== false) {
$data->{'answer_editor[' . ($this->lesson->maxanswers + 1) . ']'} =
$data->{'answer_editor[' . ($answercount - 1) . ']'};
$data->{'response_editor[' . ($this->lesson->maxanswers + 1) . ']'} =
$data->{'response_editor[' . ($answercount - 1) . ']'};
$data->{'jumpto[' . ($this->lesson->maxanswers + 1) . ']'} = $data->{'jumpto[' . ($answercount - 1) . ']'};
$data->{'score[' . ($this->lesson->maxanswers + 1) . ']'} = $data->{'score[' . ($answercount - 1) . ']'};
$data->enableotheranswers = true;
// Unset the old values.
unset($data->{'answer_editor[' . ($answercount - 1) . ']'});
unset($data->{'response_editor[' . ($answercount - 1) . ']'});
unset($data->{'jumpto['. ($answercount - 1) . ']'});
unset($data->{'score[' . ($answercount - 1) . ']'});
}
return $data;
}
/**
* Custom formats the answer to display
*
* @param string $answer
* @param context $context
* @param int $answerformat
* @param array $options Optional param for additional options.
* @return string Returns formatted string
*/
public function format_answer($answer, $context, $answerformat, $options = []) {
$answer = helper::lesson_format_numeric_value($answer);
return parent::format_answer($answer, $context, $answerformat, $options);
}
}
class lesson_add_page_form_numerical extends lesson_add_page_form_base {
public $qtype = 'numerical';
public $qtypestring = 'numerical';
protected $answerformat = '';
protected $responseformat = LESSON_ANSWER_HTML;
public function custom_definition() {
$answercount = $this->_customdata['lesson']->maxanswers;
for ($i = 0; $i < $answercount; $i++) {
$this->_form->addElement('header', 'answertitle'.$i, get_string('answer').' '.($i+1));
$this->add_answer($i, null, ($i < 1), '', [
'identifier' => 'numericanswer',
'component' => 'mod_lesson'
]);
$this->add_response($i);
$this->add_jumpto($i, null, ($i == 0 ? LESSON_NEXTPAGE : LESSON_THISPAGE));
$this->add_score($i, null, ($i===0)?1:0);
}
// Wrong answer jump.
$this->_form->addElement('header', 'wronganswer', get_string('allotheranswers', 'lesson'));
$newcount = $answercount + 1;
$this->_form->addElement('advcheckbox', 'enableotheranswers', get_string('enabled', 'lesson'));
$this->add_response($newcount);
$this->add_jumpto($newcount, get_string('allotheranswersjump', 'lesson'), LESSON_NEXTPAGE);
$this->add_score($newcount, get_string('allotheranswersscore', 'lesson'), 0);
}
/**
* We call get data when storing the data into the db. Override to format the floats properly
*
* @return object|void
*/
public function get_data(): ?stdClass {
$data = parent::get_data();
if (!empty($data->answer_editor)) {
foreach ($data->answer_editor as $key => $answer) {
$data->answer_editor[$key] = helper::lesson_unformat_numeric_value($answer);
}
}
return $data;
}
/**
* Return submitted data if properly submitted or returns NULL if validation fails or
* if there is no submitted data with formatted numbers
*
* @return object submitted data; NULL if not valid or not submitted or cancelled
*/
public function get_submitted_data(): ?stdClass {
$data = parent::get_submitted_data();
if (!empty($data->answer_editor)) {
foreach ($data->answer_editor as $key => $answer) {
$data->answer_editor[$key] = helper::lesson_unformat_numeric_value($answer);
}
}
return $data;
}
/**
* Load in existing data as form defaults. Usually new entry defaults are stored directly in
* form definition (new entry form); this function is used to load in data where values
* already exist and data is being edited (edit entry form) after formatting numbers
*
*
* @param stdClass|array $defaults object or array of default values
*/
public function set_data($defaults) {
if (is_object($defaults)) {
$defaults = (array) $defaults;
}
$editor = 'answer_editor';
foreach ($defaults as $key => $answer) {
if (substr($key, 0, strlen($editor)) == $editor) {
$defaults[$key] = helper::lesson_format_numeric_value($answer);
}
}
parent::set_data($defaults);
}
}
class lesson_display_answer_form_numerical extends moodleform {
public function definition() {
global $USER, $OUTPUT;
$mform = $this->_form;
$contents = $this->_customdata['contents'];
// Disable shortforms.
$mform->setDisableShortforms();
$mform->addElement('header', 'pageheader');
$mform->addElement('html', $OUTPUT->container($contents, 'contents'));
$hasattempt = false;
$attrs = array('size'=>'50', 'maxlength'=>'200');
if (isset($this->_customdata['lessonid'])) {
$lessonid = $this->_customdata['lessonid'];
if (isset($USER->modattempts[$lessonid]->useranswer)) {
$attrs['readonly'] = 'readonly';
$hasattempt = true;
}
}
$options = new stdClass;
$options->para = false;
$options->noclean = true;
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'pageid');
$mform->setType('pageid', PARAM_INT);
$mform->addElement('float', 'answer', get_string('youranswer', 'lesson'), $attrs);
if ($hasattempt) {
$this->add_action_buttons(null, get_string("nextpage", "lesson"));
} else {
$this->add_action_buttons(null, get_string("submit", "lesson"));
}
}
}
+497
View File
@@ -0,0 +1,497 @@
<?php
// 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/>.
/**
* Short answer
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
defined('MOODLE_INTERNAL') || die();
/** Short answer question type */
define("LESSON_PAGE_SHORTANSWER", "1");
class lesson_page_type_shortanswer extends lesson_page {
protected $type = lesson_page::TYPE_QUESTION;
protected $typeidstring = 'shortanswer';
protected $typeid = LESSON_PAGE_SHORTANSWER;
protected $string = null;
public function get_typeid() {
return $this->typeid;
}
public function get_typestring() {
if ($this->string===null) {
$this->string = get_string($this->typeidstring, 'lesson');
}
return $this->string;
}
public function get_idstring() {
return $this->typeidstring;
}
public function display($renderer, $attempt) {
global $USER, $CFG, $PAGE;
$mform = new lesson_display_answer_form_shortanswer($CFG->wwwroot.'/mod/lesson/continue.php', array('contents'=>$this->get_contents(), 'lessonid'=>$this->lesson->id));
$data = new stdClass;
$data->id = $PAGE->cm->id;
$data->pageid = $this->properties->id;
if (isset($USER->modattempts[$this->lesson->id])) {
$data->answer = s($attempt->useranswer);
}
$mform->set_data($data);
// Trigger an event question viewed.
$eventparams = array(
'context' => context_module::instance($PAGE->cm->id),
'objectid' => $this->properties->id,
'other' => array(
'pagetype' => $this->get_typestring()
)
);
$event = \mod_lesson\event\question_viewed::create($eventparams);
$event->trigger();
return $mform->display();
}
/**
* Creates answers for this page type.
*
* @param object $properties The answer properties.
*/
public function create_answers($properties) {
if (isset($properties->enableotheranswers) && $properties->enableotheranswers) {
$properties->response_editor = array_values($properties->response_editor);
$properties->jumpto = array_values($properties->jumpto);
$properties->score = array_values($properties->score);
$wrongresponse = end($properties->response_editor);
$wrongkey = key($properties->response_editor);
$properties->answer_editor[$wrongkey] = LESSON_OTHER_ANSWERS;
}
parent::create_answers($properties);
}
/**
* Update the answers for this page type.
*
* @param object $properties The answer properties.
* @param context $context The context for this module.
* @param int $maxbytes The maximum bytes for any uploades.
*/
public function update($properties, $context = null, $maxbytes = null) {
if ($properties->enableotheranswers) {
$properties->response_editor = array_values($properties->response_editor);
$properties->jumpto = array_values($properties->jumpto);
$properties->score = array_values($properties->score);
$wrongresponse = end($properties->response_editor);
$wrongkey = key($properties->response_editor);
$properties->answer_editor[$wrongkey] = LESSON_OTHER_ANSWERS;
}
parent::update($properties, $context, $maxbytes);
}
public function check_answer() {
global $CFG;
$result = parent::check_answer();
$mform = new lesson_display_answer_form_shortanswer($CFG->wwwroot.'/mod/lesson/continue.php', array('contents'=>$this->get_contents()));
$data = $mform->get_data();
require_sesskey();
$studentanswer = trim($data->answer);
if ($studentanswer === '') {
$result->noanswer = true;
return $result;
}
$i=0;
$answers = $this->get_answers();
foreach ($answers as $answer) {
$answer = parent::rewrite_answers_urls($answer, false);
$i++;
// Applying PARAM_TEXT as it is applied to the answer submitted by the user.
$expectedanswer = clean_param($answer->answer, PARAM_TEXT);
$ismatch = false;
$markit = false;
$useregexp = ($this->qoption);
if ($useregexp) { //we are using 'normal analysis', which ignores case
$ignorecase = '';
if (substr($expectedanswer, -2) == '/i') {
$expectedanswer = substr($expectedanswer, 0, -2);
$ignorecase = 'i';
}
} else {
$expectedanswer = str_replace('*', '%@@%@@%', $expectedanswer);
$expectedanswer = preg_quote($expectedanswer, '/');
$expectedanswer = str_replace('%@@%@@%', '.*', $expectedanswer);
}
// see if user typed in any of the correct answers
if ((!$this->lesson->custom && $this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) or ($this->lesson->custom && $answer->score > 0) ) {
if (!$useregexp) { // we are using 'normal analysis', which ignores case
if (preg_match('/^'.$expectedanswer.'$/i',$studentanswer)) {
$ismatch = true;
}
} else {
if (preg_match('/^'.$expectedanswer.'$/'.$ignorecase,$studentanswer)) {
$ismatch = true;
}
}
if ($ismatch == true) {
$result->correctanswer = true;
}
} else {
if (!$useregexp) { //we are using 'normal analysis'
// see if user typed in any of the wrong answers; don't worry about case
if (preg_match('/^'.$expectedanswer.'$/i',$studentanswer)) {
$ismatch = true;
}
} else { // we are using regular expressions analysis
$startcode = substr($expectedanswer,0,2);
switch ($startcode){
//1- check for absence of required string in $studentanswer (coded by initial '--')
case "--":
$expectedanswer = substr($expectedanswer,2);
if (!preg_match('/^'.$expectedanswer.'$/'.$ignorecase,$studentanswer)) {
$ismatch = true;
}
break;
//2- check for code for marking wrong strings (coded by initial '++')
case "++":
$expectedanswer=substr($expectedanswer,2);
$markit = true;
//check for one or several matches
if (preg_match_all('/'.$expectedanswer.'/'.$ignorecase,$studentanswer, $matches)) {
$ismatch = true;
$nb = count($matches[0]);
$original = array();
$marked = array();
$fontStart = '<span class="incorrect matches">';
$fontEnd = '</span>';
for ($i = 0; $i < $nb; $i++) {
array_push($original,$matches[0][$i]);
array_push($marked,$fontStart.$matches[0][$i].$fontEnd);
}
$studentanswer = str_replace($original, $marked, $studentanswer);
}
break;
//3- check for wrong answers belonging neither to -- nor to ++ categories
default:
if (preg_match('/^'.$expectedanswer.'$/'.$ignorecase,$studentanswer, $matches)) {
$ismatch = true;
}
break;
}
$result->correctanswer = false;
}
}
if ($ismatch) {
$result->newpageid = $answer->jumpto;
$options = new stdClass();
$options->para = false;
$options->noclean = true;
$result->response = format_text($answer->response, $answer->responseformat, $options);
$result->answerid = $answer->id;
break; // quit answer analysis immediately after a match has been found
}
}
// We could check here to see if we have a wrong answer jump to use.
if ($result->answerid == 0) {
// Use the all other answers jump details if it is set up.
$lastanswer = end($answers);
// Double check that this is the @#wronganswer#@ answer.
if (strpos($lastanswer->answer, LESSON_OTHER_ANSWERS) !== false) {
$otheranswers = end($answers);
$result->newpageid = $otheranswers->jumpto;
$options = new stdClass();
$options->para = false;
$result->response = format_text($otheranswers->response, $otheranswers->responseformat, $options);
// Does this also need to do the jumpto_is_correct?
if ($this->lesson->custom) {
$result->correctanswer = ($otheranswers->score > 0);
}
$result->answerid = $otheranswers->id;
}
}
$result->userresponse = $studentanswer;
//clean student answer as it goes to output.
$result->studentanswer = s($studentanswer);
return $result;
}
public function option_description_string() {
if ($this->properties->qoption) {
return " - ".get_string("casesensitive", "lesson");
}
return parent::option_description_string();
}
public function display_answers(html_table $table) {
$answers = $this->get_answers();
$options = new stdClass;
$options->noclean = true;
$options->para = false;
$i = 1;
foreach ($answers as $answer) {
$answer = parent::rewrite_answers_urls($answer, false);
$cells = array();
if ($this->lesson->custom && $answer->score > 0) {
// if the score is > 0, then it is correct
$cells[] = '<label class="correct">' . get_string('answer', 'lesson') . ' ' . $i . '</label>:';
} else if ($this->lesson->custom) {
$cells[] = '<label>' . get_string('answer', 'lesson') . ' ' . $i . '</label>:';
} else if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
// underline correct answers
$cells[] = '<span class="correct">' . get_string('answer', 'lesson') . ' ' . $i . '</span>:' . "\n";
} else {
$cells[] = '<label class="correct">' . get_string('answer', 'lesson') . ' ' . $i . '</label>:';
}
$cells[] = format_text($answer->answer, $answer->answerformat, $options);
$table->data[] = new html_table_row($cells);
$cells = array();
$cells[] = '<label>' . get_string('response', 'lesson') . ' ' . $i . '</label>:';
$cells[] = format_text($answer->response, $answer->responseformat, $options);
$table->data[] = new html_table_row($cells);
$cells = array();
$cells[] = '<label>' . get_string('score', 'lesson') . '</label>:';
$cells[] = $answer->score;
$table->data[] = new html_table_row($cells);
$cells = array();
$cells[] = '<label>' . get_string('jump', 'lesson') . '</label>:';
$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;
}
public function stats(array &$pagestats, $tries) {
$temp = $this->lesson->get_last_attempt($tries);
if (isset($pagestats[$temp->pageid][$temp->useranswer])) {
$pagestats[$temp->pageid][$temp->useranswer]++;
} else {
$pagestats[$temp->pageid][$temp->useranswer] = 1;
}
if (isset($pagestats[$temp->pageid]["total"])) {
$pagestats[$temp->pageid]["total"]++;
} else {
$pagestats[$temp->pageid]["total"] = 1;
}
return true;
}
public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) {
global $PAGE;
$answers = $this->get_answers();
$formattextdefoptions = new stdClass;
$formattextdefoptions->para = false; //I'll use it widely in this page
foreach ($answers as $answer) {
$answer = parent::rewrite_answers_urls($answer, false);
if ($useranswer == null && $i == 0) {
// I have the $i == 0 because it is easier to blast through it all at once.
if (isset($pagestats[$this->properties->id])) {
$stats = $pagestats[$this->properties->id];
$total = $stats["total"];
unset($stats["total"]);
foreach ($stats as $valentered => $ntimes) {
$data = '<input type="text" size="50" disabled="disabled" class="form-control" ' .
'readonly="readonly" value="'.s($valentered).'" />';
$percent = $ntimes / $total * 100;
$percent = round($percent, 2);
$percent .= "% ".get_string("enteredthis", "lesson");
$answerdata->answers[] = array($data, $percent);
}
} else {
$answerdata->answers[] = array(get_string("nooneansweredthisquestion", "lesson"), " ");
}
$i++;
} else if ($useranswer != null && ($answer->id == $useranswer->answerid || $answer == end($answers))) {
// get in here when what the user entered is not one of the answers
$data = '<input type="text" size="50" disabled="disabled" class="form-control" ' .
'readonly="readonly" value="'.s($useranswer->useranswer).'">';
if (isset($pagestats[$this->properties->id][$useranswer->useranswer])) {
$percent = $pagestats[$this->properties->id][$useranswer->useranswer] / $pagestats[$this->properties->id]["total"] * 100;
$percent = round($percent, 2);
$percent .= "% ".get_string("enteredthis", "lesson");
} else {
$percent = get_string("nooneenteredthis", "lesson");
}
$answerdata->answers[] = array($data, $percent);
if ($answer->id == $useranswer->answerid) {
if ($answer->response == null) {
if ($useranswer->correct) {
$answerdata->response = get_string("thatsthecorrectanswer", "lesson");
} else {
$answerdata->response = get_string("thatsthewronganswer", "lesson");
}
} else {
$answerdata->response = $answer->response;
}
if ($this->lesson->custom) {
$answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
} elseif ($useranswer->correct) {
$answerdata->score = get_string("receivedcredit", "lesson");
} else {
$answerdata->score = get_string("didnotreceivecredit", "lesson");
}
// We have found the correct answer, do not process any more answers.
$answerpage->answerdata = $answerdata;
break;
} else {
$answerdata->response = get_string("thatsthewronganswer", "lesson");
if ($this->lesson->custom) {
$answerdata->score = get_string("pointsearned", "lesson").": 0";
} else {
$answerdata->score = get_string("didnotreceivecredit", "lesson");
}
}
}
$answerpage->answerdata = $answerdata;
}
return $answerpage;
}
/**
* Make updates to the form data if required. In this case to put the all other answer data into the write section of the form.
*
* @param stdClass $data The form data to update.
* @return stdClass The updated fom data.
*/
public function update_form_data(stdClass $data): stdClass {
$answercount = count($this->get_answers());
// Check for other answer entry.
$lastanswer = $data->{'answer_editor[' . ($answercount - 1) . ']'};
if (strpos($lastanswer, LESSON_OTHER_ANSWERS) !== false) {
$data->{'answer_editor[' . ($this->lesson->maxanswers + 1) . ']'} =
$data->{'answer_editor[' . ($answercount - 1) . ']'};
$data->{'response_editor[' . ($this->lesson->maxanswers + 1) . ']'} =
$data->{'response_editor[' . ($answercount - 1) . ']'};
$data->{'jumpto[' . ($this->lesson->maxanswers + 1) . ']'} = $data->{'jumpto[' . ($answercount - 1) . ']'};
$data->{'score[' . ($this->lesson->maxanswers + 1) . ']'} = $data->{'score[' . ($answercount - 1) . ']'};
$data->enableotheranswers = true;
// Unset the old values.
unset($data->{'answer_editor[' . ($answercount - 1) . ']'});
unset($data->{'response_editor[' . ($answercount - 1) . ']'});
unset($data->{'jumpto[' . ($answercount - 1) . ']'});
unset($data->{'score[' . ($answercount - 1) . ']'});
}
return $data;
}
}
class lesson_add_page_form_shortanswer extends lesson_add_page_form_base {
public $qtype = 'shortanswer';
public $qtypestring = 'shortanswer';
protected $answerformat = '';
protected $responseformat = LESSON_ANSWER_HTML;
public function custom_definition() {
$this->_form->addElement('checkbox', 'qoption', get_string('options', 'lesson'), get_string('casesensitive', 'lesson')); //oh my, this is a regex option!
$this->_form->setDefault('qoption', 0);
$this->_form->addHelpButton('qoption', 'casesensitive', 'lesson');
$answercount = $this->_customdata['lesson']->maxanswers;
for ($i = 0; $i < $answercount; $i++) {
$this->_form->addElement('header', 'answertitle'.$i, get_string('answer').' '.($i+1));
// Only first answer is required.
$this->add_answer($i, null, ($i < 1));
$this->add_response($i);
$this->add_jumpto($i, null, ($i == 0 ? LESSON_NEXTPAGE : LESSON_THISPAGE));
$this->add_score($i, null, ($i===0)?1:0);
}
// Other answer jump.
$this->_form->addElement('header', 'wronganswer', get_string('allotheranswers', 'lesson'));
$newcount = $answercount + 1;
$this->_form->addElement('advcheckbox', 'enableotheranswers', get_string('enabled', 'lesson'));
$this->add_response($newcount);
$this->add_jumpto($newcount, get_string('allotheranswersjump', 'lesson'), LESSON_NEXTPAGE);
$this->add_score($newcount, get_string('allotheranswersscore', 'lesson'), 0);
}
}
class lesson_display_answer_form_shortanswer extends moodleform {
public function definition() {
global $OUTPUT, $USER;
$mform = $this->_form;
$contents = $this->_customdata['contents'];
$hasattempt = false;
$attrs = array('size'=>'50', 'maxlength'=>'200');
if (isset($this->_customdata['lessonid'])) {
$lessonid = $this->_customdata['lessonid'];
if (isset($USER->modattempts[$lessonid]->useranswer)) {
$attrs['readonly'] = 'readonly';
$hasattempt = true;
}
}
$placeholder = false;
if (preg_match('/_____+/', $contents, $matches)) {
$placeholder = $matches[0];
$contentsparts = explode( $placeholder, $contents, 2);
$attrs['size'] = round(strlen($placeholder) * 1.1);
}
// Disable shortforms.
$mform->setDisableShortforms();
$mform->addElement('header', 'pageheader');
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'pageid');
$mform->setType('pageid', PARAM_INT);
if ($placeholder) {
$contentsgroup = array();
$contentsgroup[] = $mform->createElement('static', '', '', $contentsparts[0]);
$contentsgroup[] = $mform->createElement('text', 'answer', '', $attrs);
$contentsgroup[] = $mform->createElement('static', '', '', $contentsparts[1]);
$mform->addGroup($contentsgroup, '', '', '', false);
} else {
$mform->addElement('html', $OUTPUT->container($contents, 'contents'));
$mform->addElement('text', 'answer', get_string('youranswer', 'lesson'), $attrs);
}
$mform->setType('answer', PARAM_TEXT);
if ($hasattempt) {
$this->add_action_buttons(null, get_string("nextpage", "lesson"));
} else {
$this->add_action_buttons(null, get_string("submit", "lesson"));
}
}
}
+442
View File
@@ -0,0 +1,442 @@
<?php
// 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/>.
/**
* True/false
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
defined('MOODLE_INTERNAL') || die();
/** True/False question type */
define("LESSON_PAGE_TRUEFALSE", "2");
class lesson_page_type_truefalse extends lesson_page {
protected $type = lesson_page::TYPE_QUESTION;
protected $typeidstring = 'truefalse';
protected $typeid = LESSON_PAGE_TRUEFALSE;
protected $string = null;
public function get_typeid() {
return $this->typeid;
}
public function get_typestring() {
if ($this->string===null) {
$this->string = get_string($this->typeidstring, 'lesson');
}
return $this->string;
}
public function get_idstring() {
return $this->typeidstring;
}
public function display($renderer, $attempt) {
global $USER, $CFG, $PAGE;
$answers = $this->get_answers();
foreach ($answers as $key => $answer) {
$answers[$key] = parent::rewrite_answers_urls($answer);
}
shuffle($answers);
$params = array('answers'=>$answers, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents(), 'attempt'=>$attempt);
$mform = new lesson_display_answer_form_truefalse($CFG->wwwroot.'/mod/lesson/continue.php', $params);
$data = new stdClass;
$data->id = $PAGE->cm->id;
$data->pageid = $this->properties->id;
$mform->set_data($data);
// Trigger an event question viewed.
$eventparams = array(
'context' => context_module::instance($PAGE->cm->id),
'objectid' => $this->properties->id,
'other' => array(
'pagetype' => $this->get_typestring()
)
);
$event = \mod_lesson\event\question_viewed::create($eventparams);
$event->trigger();
return $mform->display();
}
public function check_answer() {
global $DB, $CFG;
$formattextdefoptions = new stdClass();
$formattextdefoptions->noclean = true;
$formattextdefoptions->para = false;
$answers = $this->get_answers();
shuffle($answers);
$params = array('answers'=>$answers, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents());
$mform = new lesson_display_answer_form_truefalse($CFG->wwwroot.'/mod/lesson/continue.php', $params);
$data = $mform->get_data();
require_sesskey();
$result = parent::check_answer();
if (empty($data->answerid)) {
$result->noanswer = true;
return $result;
}
$result->answerid = $data->answerid;
$answer = $DB->get_record("lesson_answers", array("id" => $result->answerid), '*', MUST_EXIST);
$answer = parent::rewrite_answers_urls($answer);
if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
$result->correctanswer = true;
}
if ($this->lesson->custom) {
if ($answer->score > 0) {
$result->correctanswer = true;
} else {
$result->correctanswer = false;
}
}
$result->newpageid = $answer->jumpto;
$result->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
$result->studentanswer = $result->userresponse = $answer->answer;
return $result;
}
public function display_answers(html_table $table) {
$answers = $this->get_answers();
$options = new stdClass();
$options->noclean = true;
$options->para = false;
$i = 1;
foreach ($answers as $answer) {
$answer = parent::rewrite_answers_urls($answer);
$cells = array();
if ($this->lesson->custom && $answer->score > 0) {
// if the score is > 0, then it is correct
$cells[] = '<label class="correct">' . get_string('answer', 'lesson') . " {$i}</label>: \n";
} else if ($this->lesson->custom) {
$cells[] = '<label>' . get_string('answer', 'lesson') . " {$i}</label>: \n";
} else if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
// underline correct answers
$cells[] = '<span class="correct">' . get_string('answer', 'lesson') . " {$i}</span>: \n";
} else {
$cells[] = '<label class="correct">' . get_string('answer', 'lesson') . " {$i}</label>: \n";
}
$cells[] = format_text($answer->answer, $answer->answerformat, $options);
$table->data[] = new html_table_row($cells);
$cells = array();
$cells[] = '<label>' . get_string('response', 'lesson') . ' ' . $i . '</label>:';
$cells[] = format_text($answer->response, $answer->responseformat, $options);
$table->data[] = new html_table_row($cells);
$cells = array();
$cells[] = '<label>' . get_string('score', 'lesson') . '</label>:';
$cells[] = $answer->score;
$table->data[] = new html_table_row($cells);
$cells = array();
$cells[] = '<label>' . get_string('jump', 'lesson') . '</label>:';
$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;
}
/**
* Updates the page and its answers
*
* @global moodle_database $DB
* @global moodle_page $PAGE
* @param stdClass $properties
* @return bool
*/
public function update($properties, $context = null, $maxbytes = null) {
global $DB, $PAGE;
$answers = $this->get_answers();
$properties->id = $this->properties->id;
$properties->lessonid = $this->lesson->id;
$properties->timemodified = time();
$properties = file_postupdate_standard_editor($properties, 'contents', array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes), context_module::instance($PAGE->cm->id), 'mod_lesson', 'page_contents', $properties->id);
$DB->update_record("lesson_pages", $properties);
// Trigger an event: page updated.
\mod_lesson\event\page_updated::create_from_lesson_page($this, $context)->trigger();
// need to reset offset for correct and wrong responses
$this->lesson->maxanswers = 2;
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;
}
if (!empty($properties->answer_editor[$i]) && is_array($properties->answer_editor[$i])) {
$this->answers[$i]->answer = $properties->answer_editor[$i]['text'];
$this->answers[$i]->answerformat = $properties->answer_editor[$i]['format'];
}
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'];
}
// we don't need to check for isset here because properties called it's own isset method.
if ($this->answers[$i]->answer != '') {
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());
}
// Save files in answers and responses.
$this->save_answers_files($context, $maxbytes, $this->answers[$i],
$properties->answer_editor[$i], $properties->response_editor[$i]);
} else if (isset($this->answers[$i]->id)) {
$DB->delete_records('lesson_answers', array('id'=>$this->answers[$i]->id));
unset($this->answers[$i]);
}
}
return true;
}
public function stats(array &$pagestats, $tries) {
$temp = $this->lesson->get_last_attempt($tries);
if ($this->properties->qoption) {
$userresponse = explode(",", $temp->useranswer);
foreach ($userresponse as $response) {
if (isset($pagestats[$temp->pageid][$response])) {
$pagestats[$temp->pageid][$response]++;
} else {
$pagestats[$temp->pageid][$response] = 1;
}
}
} else {
if (isset($pagestats[$temp->pageid][$temp->answerid])) {
$pagestats[$temp->pageid][$temp->answerid]++;
} else {
$pagestats[$temp->pageid][$temp->answerid] = 1;
}
}
if (isset($pagestats[$temp->pageid]["total"])) {
$pagestats[$temp->pageid]["total"]++;
} else {
$pagestats[$temp->pageid]["total"] = 1;
}
return true;
}
public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) {
$answers = $this->get_answers();
$formattextdefoptions = new stdClass(); //I'll use it widely in this page
$formattextdefoptions->para = false;
$formattextdefoptions->noclean = true;
$formattextdefoptions->context = $answerpage->context;
foreach ($answers as $answer) {
$answer = parent::rewrite_answers_urls($answer);
$answertext = format_text($answer->answer, $answer->answerformat, $formattextdefoptions);
$correctresponsetext = html_writer::div(get_string('correctresponse', 'lesson'), 'badge bg-success text-white');
if ($this->properties->qoption) {
if ($useranswer == null) {
$userresponse = array();
} else {
$userresponse = explode(",", $useranswer->useranswer);
}
if (in_array($answer->id, $userresponse)) {
// make checked
$checkboxelement = "<input readonly=\"readonly\" disabled=\"disabled\" name=\"answer[$i]\" checked=\"checked\" type=\"checkbox\" value=\"1\" />";
if (!isset($answerdata->response)) {
if ($answer->response == null) {
if ($useranswer->correct) {
$answerdata->response = get_string("thatsthecorrectanswer", "lesson");
} else {
$answerdata->response = get_string("thatsthewronganswer", "lesson");
}
} else {
$answerdata->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
}
}
if (!isset($answerdata->score)) {
if ($this->lesson->custom) {
$answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
} elseif ($useranswer->correct) {
$answerdata->score = get_string("receivedcredit", "lesson");
} else {
$answerdata->score = get_string("didnotreceivecredit", "lesson");
}
}
} else {
// unchecked
$checkboxelement = "<input type=\"checkbox\" readonly=\"readonly\" name=\"answer[$i]\" value=\"0\" disabled=\"disabled\" />";
}
$answercontent = html_writer::label($checkboxelement . ' ' . $answertext, null);
if (($answer->score > 0 && $this->lesson->custom) || ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto) && !$this->lesson->custom)) {
$data = html_writer::div($answercontent, 'text-success') . $correctresponsetext;
} else {
$data = $answercontent;
}
} else {
if ($useranswer != null and $answer->id == $useranswer->answerid) {
// make checked
$checkboxelement = "<input readonly=\"readonly\" disabled=\"disabled\" name=\"answer[$i]\" checked=\"checked\" type=\"checkbox\" value=\"1\" />";
if ($answer->response == null) {
if ($useranswer->correct) {
$answerdata->response = get_string("thatsthecorrectanswer", "lesson");
} else {
$answerdata->response = get_string("thatsthewronganswer", "lesson");
}
} else {
$answerdata->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
}
if ($this->lesson->custom) {
$answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
} elseif ($useranswer->correct) {
$answerdata->score = get_string("receivedcredit", "lesson");
} else {
$answerdata->score = get_string("didnotreceivecredit", "lesson");
}
} else {
// unchecked
$checkboxelement = "<input type=\"checkbox\" readonly=\"readonly\" name=\"answer[$i]\" value=\"0\" disabled=\"disabled\" />";
}
$answercontent = html_writer::label($checkboxelement . ' ' . $answertext, null);
if (($answer->score > 0 && $this->lesson->custom) || ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto) && !$this->lesson->custom)) {
$data = html_writer::div($answercontent, 'text-success') . $correctresponsetext;
} else {
$data = $answercontent;
}
}
if (isset($pagestats[$this->properties->id][$answer->id])) {
$percent = $pagestats[$this->properties->id][$answer->id] / $pagestats[$this->properties->id]["total"] * 100;
$percent = round($percent, 2);
$percent .= "% ".get_string("checkedthisone", "lesson");
} else {
$percent = get_string("noonecheckedthis", "lesson");
}
$answerdata->answers[] = array($data, $percent);
$answerpage->answerdata = $answerdata;
}
return $answerpage;
}
}
class lesson_add_page_form_truefalse extends lesson_add_page_form_base {
public $qtype = 'truefalse';
public $qtypestring = 'truefalse';
protected $answerformat = LESSON_ANSWER_HTML;
protected $responseformat = LESSON_ANSWER_HTML;
public function custom_definition() {
$this->_form->addElement('header', 'answertitle0', get_string('correctresponse', 'lesson'));
$this->add_answer(0, null, true, $this->get_answer_format());
$this->add_response(0);
$this->add_jumpto(0, get_string('correctanswerjump', 'lesson'), LESSON_NEXTPAGE);
$this->add_score(0, get_string('correctanswerscore', 'lesson'), 1);
$this->_form->addElement('header', 'answertitle1', get_string('wrongresponse', 'lesson'));
$this->add_answer(1, null, true, $this->get_answer_format());
$this->add_response(1);
$this->add_jumpto(1, get_string('wronganswerjump', 'lesson'), LESSON_THISPAGE);
$this->add_score(1, get_string('wronganswerscore', 'lesson'), 0);
}
}
class lesson_display_answer_form_truefalse extends moodleform {
public function definition() {
global $USER, $OUTPUT;
$mform = $this->_form;
$answers = $this->_customdata['answers'];
$lessonid = $this->_customdata['lessonid'];
$contents = $this->_customdata['contents'];
if (array_key_exists('attempt', $this->_customdata)) {
$attempt = $this->_customdata['attempt'];
} else {
$attempt = new stdClass();
$attempt->answerid = null;
}
// Disable shortforms.
$mform->setDisableShortforms();
$mform->addElement('header', 'pageheader');
$mform->addElement('html', $OUTPUT->container($contents, 'contents'));
$hasattempt = false;
$disabled = '';
if (isset($USER->modattempts[$lessonid]) && !empty($USER->modattempts[$lessonid])) {
$hasattempt = true;
$disabled = array('disabled' => 'disabled');
}
$options = new stdClass();
$options->para = false;
$options->noclean = true;
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'pageid');
$mform->setType('pageid', PARAM_INT);
$i = 0;
$radiobuttons = array();
foreach ($answers as $answer) {
$ansid = 'answerid';
if ($hasattempt) {
$ansid = 'answer_id';
}
$answer = lesson_page_type_truefalse::rewrite_answers_urls($answer);
$radiobuttons[] = $mform->createElement('radio', $ansid, null,
format_text($answer->answer, $answer->answerformat, $options), $answer->id, $disabled);
$mform->setType($ansid, PARAM_INT);
if ($hasattempt && $answer->id == $USER->modattempts[$lessonid]->answerid) {
$mform->setDefault($ansid, $attempt->answerid);
$mform->addElement('hidden', 'answerid', $answer->id);
$mform->setType('answerid', PARAM_INT);
}
$i++;
}
$radiogroup = $mform->addGroup($radiobuttons, $ansid, '', array(''), false);
$radiogroup->setAttributes(array('class' => 'answeroptiongroup'));
if ($hasattempt) {
$this->add_action_buttons(null, get_string("nextpage", "lesson"));
} else {
$this->add_action_buttons(null, get_string("submit", "lesson"));
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

+3
View File
@@ -0,0 +1,3 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" preserveAspectRatio="xMinYMid meet" overflow="visible"><path d="M16 7v9H6v-4H0V0h7l3 3v1h3l3 3zM1 11h8V4H6V1H1v10zm6-8h1.6L7 1.4V3zm8 5h-3V5h-2v7H7v3h8V8zm-2-1h1.6L13 5.4V7z" fill="#888"/></svg>

After

Width:  |  Height:  |  Size: 432 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMid meet">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.99927 5.49438C3.99927 4.66596 4.67084 3.99438 5.49927 3.99438H8.50415C9.33258 3.99438 10.0042 4.66596 10.0042 5.49438V6.50087L14.048 6.49259C14.2857 5.07478 15.5187 3.99438 17.004 3.99438C18.6595 3.99438 20.0015 5.33638 20.0015 6.99182C20.0015 8.64726 18.6595 9.98926 17.004 9.98926C15.5192 9.98926 14.2866 8.90965 14.0482 7.49259L10.0042 7.50088V8.49927C10.0042 8.73786 9.94845 8.96344 9.84933 9.16371L14.8381 14.1525C15.0368 14.0553 15.2602 14.0007 15.4963 14.0007H18.5012C19.3296 14.0007 20.0012 14.6723 20.0012 15.5007V18.5056C20.0012 19.334 19.3296 20.0056 18.5012 20.0056H15.4963C14.6679 20.0056 13.9963 19.334 13.9963 18.5056V17.499L9.95083 17.5073C9.70905 18.9203 8.47833 19.9956 6.99646 19.9956C5.34102 19.9956 3.99902 18.6536 3.99902 16.9982C3.99902 15.3427 5.34102 14.0007 6.99646 14.0007C8.48474 14.0007 9.71968 15.0854 9.95391 16.5073L13.9963 16.499V15.5007C13.9963 15.2737 14.0468 15.0585 14.137 14.8656L9.13293 9.86152C8.94169 9.94993 8.72868 9.99927 8.50415 9.99927H5.49927C4.67084 9.99927 3.99927 9.32769 3.99927 8.49927V5.49438ZM9.00415 5.49438V6.94828C9.00222 6.96623 9.00124 6.98446 9.00126 7.00293C9.00127 7.02104 9.00226 7.03892 9.00415 7.05652V8.49927C9.00415 8.77541 8.78029 8.99927 8.50415 8.99927H5.49927C5.22313 8.99927 4.99927 8.77541 4.99927 8.49927V5.49438C4.99927 5.21824 5.22313 4.99438 5.49927 4.99438H8.50415C8.78029 4.99438 9.00415 5.21824 9.00415 5.49438ZM14.9963 17.0539V18.5056C14.9963 18.7818 15.2202 19.0056 15.4963 19.0056H18.5012C18.7774 19.0056 19.0012 18.7818 19.0012 18.5056V15.5007C19.0012 15.2246 18.7774 15.0007 18.5012 15.0007H15.4963C15.2202 15.0007 14.9963 15.2246 14.9963 15.5007V16.9411C14.9984 16.9594 14.9995 16.9781 14.9995 16.997C14.9995 17.0162 14.9984 17.0352 14.9963 17.0539ZM15.0066 6.99182C15.0066 5.88867 15.9009 4.99438 17.004 4.99438C18.1072 4.99438 19.0015 5.88867 19.0015 6.99182C19.0015 8.09497 18.1072 8.98926 17.004 8.98926C15.9009 8.98926 15.0066 8.09497 15.0066 6.99182ZM6.99646 15.0007C8.09961 15.0007 8.9939 15.895 8.9939 16.9982C8.9939 18.1013 8.09961 18.9956 6.99646 18.9956C5.89331 18.9956 4.99902 18.1013 4.99902 16.9982C4.99902 15.895 5.89331 15.0007 6.99646 15.0007Z" fill="#212529"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+688
View File
@@ -0,0 +1,688 @@
<?php
// 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/>.
/**
* Moodle renderer used to display special elements of the lesson module
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
defined('MOODLE_INTERNAL') || die();
class mod_lesson_renderer extends plugin_renderer_base {
/**
* Returns the header for the lesson module
*
* @param lesson $lesson a lesson object.
* @param string $currenttab current tab that is shown.
* @param bool $extraeditbuttons if extra edit buttons should be displayed.
* @param int $lessonpageid id of the lesson page that needs to be displayed.
* @param string $extrapagetitle String to appent to the page title.
* @return string
*/
public function header($lesson, $cm, $currenttab = '', $extraeditbuttons = false, $lessonpageid = null, $extrapagetitle = null) {
global $CFG, $USER;
$activityname = format_string($lesson->name, true, $lesson->course);
if (empty($extrapagetitle)) {
$title = $this->page->course->shortname.": ".$activityname;
} else {
$title = $this->page->course->shortname.": ".$activityname.": ".$extrapagetitle;
}
// Build the buttons
$context = context_module::instance($cm->id);
// Header setup.
$this->page->set_title($title);
$this->page->set_heading($this->page->course->fullname);
$canmanage = has_capability('mod/lesson:manage', $context);
$activityheader = $this->page->activityheader;
$activitypage = new moodle_url('/mod/' . $this->page->activityname . '/view.php');
$setactive = $activitypage->compare($this->page->url, URL_MATCH_BASE);
if ($activityheader->is_title_allowed()) {
$title = $canmanage && $setactive ?
$this->output->heading_with_help($activityname, 'overview', 'lesson') :
$activityname;
$activityheader->set_title($title);
}
// If we have the capability to manage the lesson but not within the view page,
// there's no reason to show activity/completion information.
if ($canmanage && !$setactive) {
$activityheader->set_hidecompletion(true);
}
$output = $this->output->header();
foreach ($lesson->messages as $message) {
$output .= $this->output->notification($message[0], $message[1], $message[2]);
}
return $output;
}
/**
* Returns the footer
* @return string
*/
public function footer() {
return $this->output->footer();
}
/**
* Returns HTML for a lesson inaccessible message
*
* @param string $message
* @return <type>
*/
public function lesson_inaccessible($message) {
global $CFG;
$output = $this->output->box_start('generalbox boxaligncenter');
$output .= $this->output->box_start('center');
$output .= $message;
$output .= $this->output->box('<a href="'.$CFG->wwwroot.'/course/view.php?id='. $this->page->course->id .'">'. get_string('returnto', 'lesson', format_string($this->page->course->fullname, true)) .'</a>', 'lessonbutton standardbutton');
$output .= $this->output->box_end();
$output .= $this->output->box_end();
return $output;
}
/**
* Returns HTML to prompt the user to log in
* @param lesson $lesson
* @param bool $failedattempt
* @return string
*/
public function login_prompt(lesson $lesson, $failedattempt = false) {
global $CFG;
$output = $this->output->box_start('password-form');
$output .= $this->output->box_start('generalbox boxaligncenter');
$output .= '<form id="password" method="post" action="'.$CFG->wwwroot.'/mod/lesson/view.php" autocomplete="off">';
$output .= '<fieldset class="invisiblefieldset center">';
$output .= '<input type="hidden" name="id" value="'. $this->page->cm->id .'" />';
$output .= '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
if ($failedattempt) {
$output .= $this->output->notification(get_string('loginfail', 'lesson'));
}
$output .= get_string('passwordprotectedlesson', 'lesson', format_string($lesson->name)).'<br /><br />';
$output .= get_string('enterpassword', 'lesson')." <input type=\"password\" name=\"userpassword\" /><br /><br />";
$output .= "<div class='lessonbutton standardbutton submitbutton'><input type='submit' value='".get_string('continue', 'lesson')."' /></div>";
$output .= " <div class='lessonbutton standardbutton submitbutton'><input type='submit' name='backtocourse' value='".get_string('cancel', 'lesson')."' /></div>";
$output .= '</fieldset></form>';
$output .= $this->output->box_end();
$output .= $this->output->box_end();
return $output;
}
/**
* Returns HTML to display dependancy errors
*
* @param object $dependentlesson
* @param array $errors
* @return string
*/
public function dependancy_errors($dependentlesson, $errors) {
$output = $this->output->box_start('generalbox boxaligncenter');
$output .= get_string('completethefollowingconditions', 'lesson', $dependentlesson->name);
$output .= $this->output->box(implode('<br />'.get_string('and', 'lesson').'<br />', $errors),'center');
$output .= $this->output->box_end();
return $output;
}
/**
* Returns HTML to display a message
* @param string $message
* @param single_button $button
* @return string
*/
public function message($message, single_button $button = null) {
$output = $this->output->box_start('generalbox boxaligncenter');
$output .= $message;
if ($button !== null) {
$output .= $this->output->box($this->output->render($button), 'lessonbutton standardbutton');
}
$output .= $this->output->box_end();
return $output;
}
/**
* Returns HTML to display a continue button
* @param lesson $lesson
* @param int $lastpageseen
* @return string
*/
public function continue_links(lesson $lesson, $lastpageseenid) {
global $CFG;
$output = $this->output->box(get_string('youhaveseen','lesson'), 'generalbox boxaligncenter');
$output .= $this->output->box_start('center');
$yeslink = html_writer::link(new moodle_url('/mod/lesson/view.php', array('id' => $this->page->cm->id,
'pageid' => $lastpageseenid, 'startlastseen' => 'yes')), get_string('yes'), array('class' => 'btn btn-primary'));
$output .= html_writer::tag('span', $yeslink, array('class'=>'lessonbutton standardbutton'));
$output .= '&nbsp;';
$nolink = html_writer::link(new moodle_url('/mod/lesson/view.php', array('id' => $this->page->cm->id,
'pageid' => $lesson->firstpageid, 'startlastseen' => 'no')), get_string('no'), array('class' => 'btn btn-secondary'));
$output .= html_writer::tag('span', $nolink, array('class'=>'lessonbutton standardbutton'));
$output .= $this->output->box_end();
return $output;
}
/**
* Returns HTML to display a page to the user
* @param lesson $lesson
* @param lesson_page $page
* @param object $attempt
* @return string
*/
public function display_page(lesson $lesson, lesson_page $page, $attempt) {
// We need to buffer here as there is an mforms display call
ob_start();
echo $page->display($this, $attempt);
$output = ob_get_contents();
ob_end_clean();
return $output;
}
/**
* Returns HTML to display a collapsed edit form
*
* @param lesson $lesson
* @param int $pageid
* @return string
*/
public function display_edit_collapsed(lesson $lesson, $pageid) {
global $DB, $CFG;
$manager = lesson_page_type_manager::get($lesson);
$qtypes = $manager->get_page_type_strings();
$npages = count($lesson->load_all_pages());
$table = new html_table();
$table->head = array(get_string('pagetitle', 'lesson'), get_string('qtype', 'lesson'), get_string('jumps', 'lesson'), get_string('actions', 'lesson'));
$table->align = array('left', 'left', 'left', 'center');
$table->data = array();
$canedit = has_capability('mod/lesson:edit', context_module::instance($this->page->cm->id));
while ($pageid != 0) {
$page = $lesson->load_page($pageid);
$data = array();
$url = new moodle_url('/mod/lesson/edit.php', array(
'id' => $this->page->cm->id,
'mode' => 'single',
'pageid' => $page->id
));
$data[] = html_writer::link($url, format_string($page->title, true), array('id' => 'lesson-' . $page->id));
$data[] = $qtypes[$page->qtype];
$data[] = implode("<br />\n", $page->jumps);
if ($canedit) {
$data[] = $this->page_action_links($page, $npages, true);
} else {
$data[] = '';
}
$table->data[] = $data;
$pageid = $page->nextpageid;
}
return html_writer::table($table);
}
/**
* Returns HTML to display the full edit page
*
* @param lesson $lesson
* @param int $pageid
* @param int $prevpageid
* @param bool $single
* @return string
*/
public function display_edit_full(lesson $lesson, $pageid, $prevpageid, $single=false) {
global $DB, $CFG;
$manager = lesson_page_type_manager::get($lesson);
$qtypes = $manager->get_page_type_strings();
$npages = count($lesson->load_all_pages());
$canedit = has_capability('mod/lesson:edit', context_module::instance($this->page->cm->id));
$content = '';
if ($canedit) {
$content = $this->add_page_links($lesson, $prevpageid);
}
$options = new stdClass;
$options->noclean = true;
while ($pageid != 0 && $single!=='stop') {
$page = $lesson->load_page($pageid);
$pagetable = new html_table();
$pagetable->align = array('right','left');
$pagetable->width = '100%';
$pagetable->tablealign = 'center';
$pagetable->cellspacing = 0;
$pagetable->cellpadding = '5px';
$pagetable->data = array();
$pageheading = new html_table_cell();
$pageheading->text = html_writer::tag('a', '', array('id' => 'lesson-' . $pageid)) . format_string($page->title);
if ($canedit) {
$pageheading->text .= ' '.$this->page_action_links($page, $npages);
}
$pageheading->style = 'text-align:center';
$pageheading->colspan = 2;
$pageheading->scope = 'col';
$pagetable->head = array($pageheading);
$cell = new html_table_cell();
$cell->colspan = 2;
$cell->style = 'text-align:left';
$cell->text = $page->contents;
$pagetable->data[] = new html_table_row(array($cell));
$cell = new html_table_cell();
$cell->colspan = 2;
$cell->style = 'text-align:center';
$cell->text = '<strong>'.$qtypes[$page->qtype] . $page->option_description_string().'</strong>';
$pagetable->data[] = new html_table_row(array($cell));
$pagetable = $page->display_answers($pagetable);
$content .= html_writer::start_tag('div');
$content .= html_writer::table($pagetable);
$content .= html_writer::end_tag('div');
if ($canedit) {
$content .= $this->add_page_links($lesson, $pageid);
}
// check the prev links - fix (silently) if necessary - there was a bug in
// versions 1 and 2 when add new pages. Not serious then as the backwards
// links were not used in those versions
if ($page->prevpageid != $prevpageid) {
// fix it
$DB->set_field("lesson_pages", "prevpageid", $prevpageid, array("id" => $page->id));
debugging("<p>***prevpageid of page $page->id set to $prevpageid***");
}
$prevpageid = $page->id;
$pageid = $page->nextpageid;
if ($single === true) {
$single = 'stop';
}
}
return $this->output->box($content, 'edit_pages_box');
}
/**
* Returns HTML to display the add page links
*
* @param lesson $lesson
* @param int $prevpageid
* @return string
*/
public function add_page_links(lesson $lesson, $prevpageid=false) {
global $CFG;
$links = array();
$importquestionsurl = new moodle_url('/mod/lesson/import.php',array('id'=>$this->page->cm->id, 'pageid'=>$prevpageid));
$links[] = html_writer::link($importquestionsurl, get_string('importquestions', 'lesson'));
$manager = lesson_page_type_manager::get($lesson);
foreach($manager->get_add_page_type_links($prevpageid) as $link) {
$links[] = html_writer::link($link['addurl'], $link['name']);
}
$addquestionurl = new moodle_url('/mod/lesson/editpage.php', array('id'=>$this->page->cm->id, 'pageid'=>$prevpageid));
$links[] = html_writer::link($addquestionurl, get_string('addaquestionpagehere', 'lesson'));
return $this->output->box(implode(" | \n", $links), 'addlinks');
}
/**
* Return HTML to display add first page links
* @param lesson $lesson
* @return string
*/
public function add_first_page_links(lesson $lesson) {
$prevpageid = 0;
$headinglevel = $this->page->activityheader->get_heading_level(3);
$output = $this->output->heading(get_string("whatdofirst", "lesson"), $headinglevel);
$links = array();
$importquestionsurl = new moodle_url('/mod/lesson/import.php',array('id'=>$this->page->cm->id, 'pageid'=>$prevpageid));
$links[] = html_writer::link($importquestionsurl, get_string('importquestions', 'lesson'));
$manager = lesson_page_type_manager::get($lesson);
foreach ($manager->get_add_page_type_links($prevpageid) as $link) {
$link['addurl']->param('firstpage', 1);
$links[] = html_writer::link($link['addurl'], $link['name']);
}
$addquestionurl = new moodle_url('/mod/lesson/editpage.php', array('id'=>$this->page->cm->id, 'pageid'=>$prevpageid, 'firstpage'=>1));
$links[] = html_writer::link($addquestionurl, get_string('addaquestionpage', 'lesson'));
return $this->output->box($output.'<p>'.implode('</p><p>', $links).'</p>', 'generalbox firstpageoptions');
}
/**
* Returns HTML to display action links for a page
*
* @param lesson_page $page
* @param bool $printmove
* @param bool $printaddpage
* @return string
*/
public function page_action_links(lesson_page $page, $printmove, $printaddpage=false) {
global $CFG;
$actions = array();
if ($printmove) {
$url = new moodle_url('/mod/lesson/lesson.php',
array('id' => $this->page->cm->id, 'action' => 'move', 'pageid' => $page->id, 'sesskey' => sesskey()));
$label = get_string('movepagenamed', 'lesson', format_string($page->title));
$img = $this->output->pix_icon('t/move', $label);
$actions[] = html_writer::link($url, $img, array('title' => $label));
}
$url = new moodle_url('/mod/lesson/editpage.php', array('id' => $this->page->cm->id, 'pageid' => $page->id, 'edit' => 1));
$label = get_string('updatepagenamed', 'lesson', format_string($page->title));
$img = $this->output->pix_icon('t/edit', $label);
$actions[] = html_writer::link($url, $img, array('title' => $label));
// Duplicate action.
$url = new moodle_url('/mod/lesson/lesson.php', array('id' => $this->page->cm->id, 'pageid' => $page->id,
'action' => 'duplicate', 'sesskey' => sesskey()));
$label = get_string('duplicatepagenamed', 'lesson', format_string($page->title));
$img = $this->output->pix_icon('e/copy', $label, 'mod_lesson');
$actions[] = html_writer::link($url, $img, array('title' => $label));
$url = new moodle_url('/mod/lesson/view.php', array('id' => $this->page->cm->id, 'pageid' => $page->id));
$label = get_string('previewpagenamed', 'lesson', format_string($page->title));
$img = $this->output->pix_icon('t/preview', $label);
$actions[] = html_writer::link($url, $img, array('title' => $label));
$url = new moodle_url('/mod/lesson/lesson.php',
array('id' => $this->page->cm->id, 'action' => 'confirmdelete', 'pageid' => $page->id, 'sesskey' => sesskey()));
$label = get_string('deletepagenamed', 'lesson', format_string($page->title));
$img = $this->output->pix_icon('t/delete', $label);
$actions[] = html_writer::link($url, $img, array('title' => $label));
if ($printaddpage) {
$options = array();
$manager = lesson_page_type_manager::get($page->lesson);
$links = $manager->get_add_page_type_links($page->id);
foreach ($links as $link) {
$options[$link['type']] = $link['name'];
}
$options[0] = get_string('addaquestionpage', 'lesson');
$addpageurl = new moodle_url('/mod/lesson/editpage.php', array('id'=>$this->page->cm->id, 'pageid'=>$page->id, 'sesskey'=>sesskey()));
$addpageselect = new single_select($addpageurl, 'qtype', $options, null, array(''=>get_string('addanewpage', 'lesson').'...'), 'addpageafter'.$page->id);
$addpageselect->attributes = ['aria-label' => get_string('actions', 'lesson')];
$addpageselector = $this->output->render($addpageselect);
}
if (isset($addpageselector)) {
$actions[] = $addpageselector;
}
return implode(' ', $actions);
}
/**
* Prints the on going message to the user.
*
* With custom grading On, displays points
* earned out of total points possible thus far.
* With custom grading Off, displays number of correct
* answers out of total attempted.
*
* @param object $lesson The lesson that the user is taking.
* @return void
**/
/**
* Prints the on going message to the user.
*
* With custom grading On, displays points
* earned out of total points possible thus far.
* With custom grading Off, displays number of correct
* answers out of total attempted.
*
* @param lesson $lesson
* @return string
*/
public function ongoing_score(lesson $lesson) {
return $this->output->box($lesson->get_ongoing_score_message(), "ongoing center");
}
/**
* Returns HTML to display a progress bar of progression through a lesson
*
* @param lesson $lesson
* @param int $progress optional, if empty it will be calculated
* @return string
*/
public function progress_bar(lesson $lesson, $progress = null) {
$context = context_module::instance($this->page->cm->id);
// lesson setting to turn progress bar on or off
if (!$lesson->progressbar) {
return '';
}
// catch teachers
if (has_capability('mod/lesson:manage', $context)) {
return $this->output->notification(get_string('progressbarteacherwarning2', 'lesson'));
}
if ($progress === null) {
$progress = $lesson->calculate_progress();
}
$content = html_writer::start_tag('div');
$content .= html_writer::start_tag('div', array('class' => 'progress'));
$content .= html_writer::start_tag('div', array('class' => 'progress-bar bar', 'role' => 'progressbar',
'style' => 'width: ' . $progress .'%', 'aria-valuenow' => $progress, 'aria-valuemin' => 0, 'aria-valuemax' => 100));
$content .= $progress . "%";
$content .= html_writer::end_tag('div');
$content .= html_writer::end_tag('div');
$printprogress = html_writer::tag('div', get_string('progresscompleted', 'lesson', $progress) . $content);
return $this->output->box($printprogress, 'progress_bar');
}
/**
* Returns HTML to show the start of a slideshow
* @param lesson $lesson
*/
public function slideshow_start(lesson $lesson) {
$attributes = array();
$attributes['class'] = 'slideshow';
$attributes['style'] = 'background-color:'.$lesson->properties()->bgcolor.';height:'.
$lesson->properties()->height.'px;width:'.$lesson->properties()->width.'px;';
$output = html_writer::start_tag('div', $attributes);
return $output;
}
/**
* Returns HTML to show the end of a slideshow
*/
public function slideshow_end() {
$output = html_writer::end_tag('div');
return $output;
}
/**
* Returns a P tag containing contents
* @param string $contents
* @param string $class
*/
public function paragraph($contents, $class='') {
$attributes = array();
if ($class !== '') {
$attributes['class'] = $class;
}
$output = html_writer::tag('p', $contents, $attributes);
return $output;
}
/**
* Returns the HTML for displaying the end of lesson page.
*
* @param lesson $lesson lesson instance
* @param stdclass $data lesson data to be rendered
* @return string HTML contents
*/
public function display_eol_page(lesson $lesson, $data) {
$output = '';
$canmanage = $lesson->can_manage();
$course = $lesson->courserecord;
if ($lesson->custom && !$canmanage && (($data->gradeinfo->nquestions < $lesson->minquestions))) {
$output .= $this->box_start('generalbox boxaligncenter');
}
if ($data->gradelesson) {
$headinglevel = $this->page->activityheader->get_heading_level();
$output .= $this->heading(get_string("congratulations", "lesson"), $headinglevel);
$output .= $this->box_start('generalbox boxaligncenter');
}
if ($data->notenoughtimespent !== false) {
$output .= $this->paragraph(get_string("notenoughtimespent", "lesson", $data->notenoughtimespent), 'center');
}
if ($data->numberofpagesviewed !== false) {
$output .= $this->paragraph(get_string("numberofpagesviewed", "lesson", $data->numberofpagesviewed), 'center');
}
if ($data->youshouldview !== false) {
$output .= $this->paragraph(get_string("youshouldview", "lesson", $data->youshouldview), 'center');
}
if ($data->numberofcorrectanswers !== false) {
$output .= $this->paragraph(get_string("numberofcorrectanswers", "lesson", $data->numberofcorrectanswers), 'center');
}
if ($data->displayscorewithessays !== false) {
$output .= $this->box(get_string("displayscorewithessays", "lesson", $data->displayscorewithessays), 'center');
} else if ($data->displayscorewithoutessays !== false) {
$output .= $this->box(get_string("displayscorewithoutessays", "lesson", $data->displayscorewithoutessays), 'center');
}
if ($data->yourcurrentgradeisoutof !== false) {
$output .= $this->paragraph(get_string("yourcurrentgradeisoutof", "lesson", $data->yourcurrentgradeisoutof), 'center');
}
if ($data->yourcurrentgradeis !== false) {
$output .= $this->paragraph(get_string("yourcurrentgradeis", "lesson", $data->yourcurrentgradeis), 'center');
}
if ($data->eolstudentoutoftimenoanswers !== false) {
$output .= $this->paragraph(get_string("eolstudentoutoftimenoanswers", "lesson"));
}
if ($data->welldone !== false) {
$output .= $this->paragraph(get_string("welldone", "lesson"));
}
if ($data->progresscompleted !== false) {
$output .= $this->progress_bar($lesson, $data->progresscompleted);
}
if ($data->displayofgrade !== false) {
$output .= $this->paragraph(get_string("displayofgrade", "lesson"), 'center');
}
$output .= $this->box_end(); // End of Lesson button to Continue.
if ($data->reviewlesson !== false) {
$output .= html_writer::link($data->reviewlesson, get_string('reviewlesson', 'lesson'),
array('class' => 'centerpadded lessonbutton standardbutton pr-3'));
}
if ($data->modattemptsnoteacher !== false) {
$output .= $this->paragraph(get_string("modattemptsnoteacher", "lesson"), 'centerpadded');
}
if ($data->activitylink !== false) {
$output .= $data->activitylink;
}
$url = new moodle_url('/course/view.php', array('id' => $course->id));
$output .= html_writer::link($url, get_string('returnto', 'lesson', format_string($course->fullname, true)),
array('class' => 'centerpadded lessonbutton standardbutton pr-3'));
if (has_capability('gradereport/user:view', context_course::instance($course->id))
&& $course->showgrades && $lesson->grade != 0 && !$lesson->practice) {
$url = new moodle_url('/grade/index.php', array('id' => $course->id));
$output .= html_writer::link($url, get_string('viewgrades', 'lesson'),
array('class' => 'centerpadded lessonbutton standardbutton pr-3'));
}
return $output;
}
/**
* Render the override action menu.
*
* @param \mod_lesson\output\override_action_menu $overrideactionmenu The overrideactionmenu
*
* @return string The rendered override action menu.
*/
public function render_override_action_menu(\mod_lesson\output\override_action_menu $overrideactionmenu): string {
$context = $overrideactionmenu->export_for_template($this);
return $this->render_from_template('mod_lesson/override_action_menu', $context);
}
/**
* Render the edit action buttons.
*
* @param \mod_lesson\output\edit_action_buttons $editbuttons The editbuttons
*
* @return string The rendered edit action buttons.
*/
public function render_edit_action_buttons(\mod_lesson\output\edit_action_buttons $editbuttons): string {
$context = $editbuttons->export_for_template($this);
return $this->render_from_template('mod_lesson/edit_action_buttons', $context);
}
/**
* Render the edit action area.
*
* @param \mod_lesson\output\edit_action_area $editarea The edit area.
* @return string The rendered edit action area.
*/
public function render_edit_action_area(\mod_lesson\output\edit_action_area $editarea): string {
$context = $editarea->export_for_template($this);
return $this->render_from_template('mod_lesson/edit_action_area', $context);
}
/**
* Render the report action menu
*
* @param \mod\lesson\output\report_action_menu $reportmenu The reportmenu.
* @return string The rendered report action menu.
*/
public function render_report_action_menu(\mod_lesson\output\report_action_menu $reportmenu): string {
$context = $reportmenu->export_for_template($this);
return $this->render_from_template('mod_lesson/report_action_menu', $context);
}
}
+370
View File
@@ -0,0 +1,370 @@
<?php
// 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/>.
/**
* Displays the lesson statistics.
*
* @package mod_lesson
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or late
**/
require_once('../../config.php');
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
$id = required_param('id', PARAM_INT); // Course Module ID
$pageid = optional_param('pageid', null, PARAM_INT); // Lesson Page ID
$action = optional_param('action', 'reportoverview', PARAM_ALPHA); // action to take
$nothingtodisplay = false;
$cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST));
require_login($course, false, $cm);
$currentgroup = groups_get_activity_group($cm, true);
$context = context_module::instance($cm->id);
require_capability('mod/lesson:viewreports', $context);
$url = new moodle_url('/mod/lesson/report.php', array('id'=>$id));
$url->param('action', $action);
if ($pageid !== null) {
$url->param('pageid', $pageid);
}
$PAGE->set_url($url);
if ($action == 'reportdetail') {
$PAGE->navbar->add(get_string('report', 'lesson'), $url);
}
$lessonoutput = $PAGE->get_renderer('mod_lesson');
$PAGE->activityheader->set_description('');
$reportactionmenu = new \mod_lesson\output\report_action_menu($id, $url);
$reportactionarea = $lessonoutput->render($reportactionmenu);
if ($action === 'delete') {
/// Process any form data before fetching attempts, grades and times
if (has_capability('mod/lesson:edit', $context) and $form = data_submitted() and confirm_sesskey()) {
/// Cycle through array of userids with nested arrays of tries
if (!empty($form->attempts)) {
foreach ($form->attempts as $userid => $tries) {
// Modifier IS VERY IMPORTANT! What does it do?
// Well, it is for when you delete multiple attempts for the same user.
// If you delete try 1 and 3 for a user, then after deleting try 1, try 3 then
// becomes try 2 (because try 1 is gone and all tries after try 1 get decremented).
// So, the modifier makes sure that the submitted try refers to the current try in the
// database - hope this all makes sense :)
$modifier = 0;
foreach ($tries as $try => $junk) {
$try -= $modifier;
/// Clean up the timer table by removing using the order - this is silly, it should be linked to specific attempt (skodak)
$timers = $lesson->get_user_timers($userid, 'starttime', 'id', $try, 1);
if ($timers) {
$timer = reset($timers);
$DB->delete_records('lesson_timer', array('id' => $timer->id));
}
$params = array ("userid" => $userid, "lessonid" => $lesson->id);
// Remove the grade from the grades tables - this is silly, it should be linked to specific attempt (skodak).
$grades = $DB->get_records_sql("SELECT id FROM {lesson_grades}
WHERE userid = :userid AND lessonid = :lessonid
ORDER BY completed", $params, $try, 1);
if ($grades) {
$grade = reset($grades);
$DB->delete_records('lesson_grades', array('id' => $grade->id));
}
/// Remove attempts and update the retry number
$DB->delete_records('lesson_attempts', array('userid' => $userid, 'lessonid' => $lesson->id, 'retry' => $try));
$DB->execute("UPDATE {lesson_attempts} SET retry = retry - 1 WHERE userid = ? AND lessonid = ? AND retry > ?", array($userid, $lesson->id, $try));
/// Remove seen branches and update the retry number
$DB->delete_records('lesson_branch', array('userid' => $userid, 'lessonid' => $lesson->id, 'retry' => $try));
$DB->execute("UPDATE {lesson_branch} SET retry = retry - 1 WHERE userid = ? AND lessonid = ? AND retry > ?", array($userid, $lesson->id, $try));
/// update central gradebook
lesson_update_grades($lesson, $userid);
$modifier++;
}
}
}
}
redirect(new moodle_url($PAGE->url, array('action'=>'reportoverview')));
} else if ($action === 'reportoverview') {
/**************************************************************************
this action is for default view and overview view
**************************************************************************/
// Get the table and data for build statistics.
list($table, $data) = lesson_get_overview_report_table_and_data($lesson, $currentgroup);
if ($table === false) {
echo $lessonoutput->header($lesson, $cm, $action, false, null, get_string('nolessonattempts', 'lesson'));
echo $reportactionarea;
if (!empty($currentgroup)) {
$groupname = groups_get_group_name($currentgroup);
echo $OUTPUT->notification(get_string('nolessonattemptsgroup', 'lesson', $groupname));
} else {
echo $OUTPUT->notification(get_string('nolessonattempts', 'lesson'));
}
groups_print_activity_menu($cm, $url);
echo $OUTPUT->footer();
exit();
}
echo $lessonoutput->header($lesson, $cm, $action, false, null, get_string('overview', 'lesson'));
echo $reportactionarea;
groups_print_activity_menu($cm, $url);
$course_context = context_course::instance($course->id);
if (has_capability('gradereport/grader:view', $course_context) && has_capability('moodle/grade:viewall', $course_context)) {
$seeallgradeslink = new moodle_url('/grade/report/grader/index.php', array('id'=>$course->id));
$seeallgradeslink = html_writer::link($seeallgradeslink, get_string('seeallcoursegrades', 'grades'));
echo $OUTPUT->box($seeallgradeslink, 'allcoursegrades');
}
// The attempts table.
$attemptstable = html_writer::table($table);
// The HTML that we will be displaying which includes the attempts table and bulk actions menu, if necessary.
$attemptshtml = $attemptstable;
// Show bulk actions when user has capability to edit the lesson.
if (has_capability('mod/lesson:edit', $context)) {
$reporturl = new moodle_url('/mod/lesson/report.php');
$formid = 'mod-lesson-report-form';
// Sesskey hidden input.
$formcontents = html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()]);
// CMID hidden input.
$formcontents .= html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'id', 'value' => $cm->id]);
// Attempts table.
$formcontents .= $attemptstable;
// Bulk actions menu.
$attemptsactions = [
'delete' => get_string('deleteselected')
];
$bulkactions = new single_select($reporturl, 'action', $attemptsactions, '', ['' => 'choosedots'], $formid);
$bulkactions->set_label(get_string('withselectedattempts', 'lesson'));
$bulkactions->disabled = true;
$bulkactions->attributes = [
'data-action' => 'toggle',
'data-togglegroup' => 'lesson-attempts',
'data-toggle' => 'action',
];
$bulkactionshtml = $OUTPUT->render($bulkactions);
$formcontents .= $OUTPUT->box($bulkactionshtml, 'center');
// Build the attempts form.
$formattributes = [
'id' => $formid,
'method' => 'post',
];
$attemptshtml = html_writer::tag('form', $formcontents, $formattributes);
}
// Show the attempts HTML.
echo $attemptshtml;
// Calculate the Statistics.
if ($data->avetime == null) {
$data->avetime = get_string("notcompleted", "lesson");
} else {
$data->avetime = format_float($data->avetime / $data->numofattempts, 0);
$data->avetime = format_time($data->avetime);
}
if ($data->hightime == null) {
$data->hightime = get_string("notcompleted", "lesson");
} else {
$data->hightime = format_time($data->hightime);
}
if ($data->lowtime == null) {
$data->lowtime = get_string("notcompleted", "lesson");
} else {
$data->lowtime = format_time($data->lowtime);
}
if ($data->lessonscored) {
if ($data->numofattempts == 0) {
$data->avescore = get_string("notcompleted", "lesson");
} else {
$data->avescore = format_float($data->avescore, 2) . '%';
}
if ($data->highscore === null) {
$data->highscore = get_string("notcompleted", "lesson");
} else {
$data->highscore .= '%';
}
if ($data->lowscore === null) {
$data->lowscore = get_string("notcompleted", "lesson");
} else {
$data->lowscore .= '%';
}
// Display the full stats for the lesson.
echo $OUTPUT->heading(get_string('lessonstats', 'lesson'), 3);
$stattable = new html_table();
$stattable->head = array(get_string('averagescore', 'lesson'), get_string('averagetime', 'lesson'),
get_string('highscore', 'lesson'), get_string('lowscore', 'lesson'),
get_string('hightime', 'lesson'), get_string('lowtime', 'lesson'));
$stattable->align = array('center', 'center', 'center', 'center', 'center', 'center');
$stattable->attributes['class'] = 'standardtable generaltable';
$stattable->data[] = array($data->avescore, $data->avetime, $data->highscore, $data->lowscore, $data->hightime, $data->lowtime);
} else {
// Display simple stats for the lesson.
echo $OUTPUT->heading(get_string('lessonstats', 'lesson'), 3);
$stattable = new html_table();
$stattable->head = array(get_string('averagetime', 'lesson'), get_string('hightime', 'lesson'),
get_string('lowtime', 'lesson'));
$stattable->align = array('center', 'center', 'center');
$stattable->attributes['class'] = 'standardtable generaltable';
$stattable->data[] = array($data->avetime, $data->hightime, $data->lowtime);
}
echo html_writer::table($stattable);
} else if ($action === 'reportdetail') {
/**************************************************************************
this action is for a student detailed view and for the general detailed view
General flow of this section of the code
1. Generate a object which holds values for the statistics for each question/answer
2. Cycle through all the pages to create a object. Foreach page, see if the student actually answered
the page. Then process the page appropriatly. Display all info about the question,
Highlight correct answers, show how the user answered the question, and display statistics
about each page
3. Print out info about the try (if needed)
4. Print out the object which contains all the try info
**************************************************************************/
echo $lessonoutput->header($lesson, $cm, $action, false, null, get_string('detailedstats', 'lesson'));
echo $reportactionarea;
groups_print_activity_menu($cm, $url);
$course_context = context_course::instance($course->id);
if (has_capability('gradereport/grader:view', $course_context) && has_capability('moodle/grade:viewall', $course_context)) {
$seeallgradeslink = new moodle_url('/grade/report/grader/index.php', array('id'=>$course->id));
$seeallgradeslink = html_writer::link($seeallgradeslink, get_string('seeallcoursegrades', 'grades'));
echo $OUTPUT->box($seeallgradeslink, 'allcoursegrades');
}
$formattextdefoptions = new stdClass;
$formattextdefoptions->para = false; //I'll use it widely in this page
$formattextdefoptions->overflowdiv = true;
$userid = optional_param('userid', null, PARAM_INT); // if empty, then will display the general detailed view
$try = optional_param('try', null, PARAM_INT);
list($answerpages, $userstats) = lesson_get_user_detailed_report_data($lesson, $userid, $try);
/// actually start printing something
$table = new html_table();
$table->wrap = array();
$table->width = "60%";
if (!empty($userid)) {
// if looking at a students try, print out some basic stats at the top
// print out users name
//$headingobject->lastname = $students[$userid]->lastname;
//$headingobject->firstname = $students[$userid]->firstname;
//$headingobject->attempt = $try + 1;
//print_heading(get_string("studentattemptlesson", "lesson", $headingobject));
echo $OUTPUT->heading(get_string('attempt', 'lesson', $try+1), 3);
$table->head = array();
$table->align = array('right', 'left');
$table->attributes['class'] = 'table table-striped';
if (empty($userstats->gradeinfo)) {
$table->align = array("center");
$table->data[] = array(get_string("notcompleted", "lesson"));
} else {
$user = $DB->get_record('user', array('id' => $userid));
$gradeinfo = lesson_grade($lesson, $try, $user->id);
$table->data[] = array(get_string('name').':', $OUTPUT->user_picture($user, array('courseid'=>$course->id)).fullname($user, true));
$table->data[] = array(get_string("timetaken", "lesson").":", format_time($userstats->timetotake));
$table->data[] = array(get_string("completed", "lesson").":", userdate($userstats->completed));
$table->data[] = array(get_string('rawgrade', 'lesson').':', $userstats->gradeinfo->earned.'/'.$userstats->gradeinfo->total);
$table->data[] = array(get_string("gradenoun").":", $userstats->grade."%");
}
echo html_writer::table($table);
// Don't want this class for later tables
$table->attributes['class'] = '';
}
foreach ($answerpages as $page) {
$table->align = array('left', 'left');
$table->size = array('70%', null);
$table->attributes['class'] = 'table table-striped';
unset($table->data);
if ($page->grayout) { // set the color of text
$fontstart = html_writer::start_tag('span', array('class' => 'dimmed_text'));
$fontend = html_writer::end_tag('span');
$fontstart2 = $fontstart;
$fontend2 = $fontend;
} else {
$fontstart = '';
$fontend = '';
$fontstart2 = '';
$fontend2 = '';
}
$table->head = array($fontstart2.$page->qtype.": ".format_string($page->title).$fontend2, $fontstart2.get_string("classstats", "lesson").$fontend2);
$table->data[] = array($fontstart.get_string("question", "lesson").": <br />".$fontend.$fontstart2.$page->contents.$fontend2, " ");
$table->data[] = array($fontstart.get_string("answer", "lesson").":".$fontend, ' ');
// apply the font to each answer
if (!empty($page->answerdata) && !empty($page->answerdata->answers)) {
foreach ($page->answerdata->answers as $answer){
$modified = array();
foreach ($answer as $single) {
// need to apply a font to each one
$modified[] = $fontstart2.$single.$fontend2;
}
$table->data[] = $modified;
}
if (isset($page->answerdata->response)) {
$table->data[] = array($fontstart.get_string("response", "lesson").": <br />".$fontend
.$fontstart2.$page->answerdata->response.$fontend2, " ");
}
$table->data[] = array($page->answerdata->score, " ");
} else {
$table->data[] = array(get_string('didnotanswerquestion', 'lesson'), " ");
}
echo html_writer::table($table);
}
} else {
throw new \moodle_exception('unknowaction');
}
/// Finish the page
echo $OUTPUT->footer();
+181
View File
@@ -0,0 +1,181 @@
<?php
// 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/>.
/**
* Settings used by the lesson module, were moved from mod_edit
*
* @package mod_lesson
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or late
**/
defined('MOODLE_INTERNAL') || die;
if ($ADMIN->fulltree) {
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
$yesno = array(0 => get_string('no'), 1 => get_string('yes'));
// Introductory explanation that all the settings are defaults for the add lesson form.
$settings->add(new admin_setting_heading('mod_lesson/lessonintro', '', get_string('configintro', 'lesson')));
// Appearance settings.
$settings->add(new admin_setting_heading('mod_lesson/appearance', get_string('appearance'), ''));
// Media file popup settings.
$setting = new admin_setting_configempty('mod_lesson/mediafile', get_string('mediafile', 'lesson'),
get_string('mediafile_help', 'lesson'));
$setting->set_advanced_flag_options(admin_setting_flag::ENABLED, true);
$settings->add($setting);
$settings->add(new admin_setting_configtext('mod_lesson/mediawidth', get_string('mediawidth', 'lesson'),
get_string('configmediawidth', 'lesson'), 640, PARAM_INT));
$settings->add(new admin_setting_configtext('mod_lesson/mediaheight', get_string('mediaheight', 'lesson'),
get_string('configmediaheight', 'lesson'), 480, PARAM_INT));
$settings->add(new admin_setting_configcheckbox('mod_lesson/mediaclose', get_string('mediaclose', 'lesson'),
get_string('configmediaclose', 'lesson'), false, PARAM_TEXT));
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/progressbar',
get_string('progressbar', 'lesson'), get_string('progressbar_help', 'lesson'),
array('value' => 0, 'adv' => false), $yesno));
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/ongoing',
get_string('ongoing', 'lesson'), get_string('ongoing_help', 'lesson'),
array('value' => 0, 'adv' => true), $yesno));
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/displayleftmenu',
get_string('displayleftmenu', 'lesson'), get_string('displayleftmenu_help', 'lesson'),
array('value' => 0, 'adv' => false), $yesno));
$percentage = array();
for ($i = 100; $i >= 0; $i--) {
$percentage[$i] = $i.'%';
}
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/displayleftif',
get_string('displayleftif', 'lesson'), get_string('displayleftif_help', 'lesson'),
array('value' => 0, 'adv' => true), $percentage));
// Slideshow settings.
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/slideshow',
get_string('slideshow', 'lesson'), get_string('slideshow_help', 'lesson'),
array('value' => 0, 'adv' => true), $yesno));
$settings->add(new admin_setting_configtext('mod_lesson/slideshowwidth', get_string('slideshowwidth', 'lesson'),
get_string('configslideshowwidth', 'lesson'), 640, PARAM_INT));
$settings->add(new admin_setting_configtext('mod_lesson/slideshowheight', get_string('slideshowheight', 'lesson'),
get_string('configslideshowheight', 'lesson'), 480, PARAM_INT));
$settings->add(new admin_setting_configtext('mod_lesson/slideshowbgcolor', get_string('slideshowbgcolor', 'lesson'),
get_string('configslideshowbgcolor', 'lesson'), '#FFFFFF', PARAM_TEXT));
$numbers = array();
for ($i = 20; $i > 1; $i--) {
$numbers[$i] = $i;
}
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/maxanswers',
get_string('maximumnumberofanswersbranches', 'lesson'), get_string('maximumnumberofanswersbranches_help', 'lesson'),
array('value' => '5', 'adv' => true), $numbers));
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/defaultfeedback',
get_string('displaydefaultfeedback', 'lesson'), get_string('displaydefaultfeedback_help', 'lesson'),
array('value' => 0, 'adv' => true), $yesno));
$setting = new admin_setting_configempty('mod_lesson/activitylink', get_string('activitylink', 'lesson'),
'');
$setting->set_advanced_flag_options(admin_setting_flag::ENABLED, true);
$settings->add($setting);
// Availability settings.
$settings->add(new admin_setting_heading('mod_lesson/availibility', get_string('availability'), ''));
$settings->add(new admin_setting_configduration_with_advanced('mod_lesson/timelimit',
get_string('timelimit', 'lesson'), get_string('configtimelimit_desc', 'lesson'),
array('value' => '0', 'adv' => false), 60));
$settings->add(new admin_setting_configcheckbox_with_advanced('mod_lesson/password',
get_string('password', 'lesson'), get_string('configpassword_desc', 'lesson'),
array('value' => 0, 'adv' => true)));
// Flow Control.
$settings->add(new admin_setting_heading('lesson/flowcontrol', get_string('flowcontrol', 'lesson'), ''));
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/modattempts',
get_string('modattempts', 'lesson'), get_string('modattempts_help', 'lesson'),
array('value' => 0, 'adv' => false), $yesno));
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/displayreview',
get_string('displayreview', 'lesson'), get_string('displayreview_help', 'lesson'),
array('value' => 0, 'adv' => false), $yesno));
$attempts = ['0' => get_string('unlimited')];
for ($i = 10; $i > 0; $i--) {
$attempts[$i] = $i;
}
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/maximumnumberofattempts',
get_string('maximumnumberofattempts', 'lesson'), get_string('maximumnumberofattempts_help', 'lesson'),
array('value' => '1', 'adv' => false), $attempts));
$defaultnextpages = array();
$defaultnextpages[0] = get_string("normal", "lesson");
$defaultnextpages[LESSON_UNSEENPAGE] = get_string("showanunseenpage", "lesson");
$defaultnextpages[LESSON_UNANSWEREDPAGE] = get_string("showanunansweredpage", "lesson");
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/defaultnextpage',
get_string('actionaftercorrectanswer', 'lesson'), '',
array('value' => 0, 'adv' => true), $defaultnextpages));
$pages = array();
for ($i = 100; $i >= 0; $i--) {
$pages[$i] = $i;
}
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/numberofpagestoshow',
get_string('numberofpagestoshow', 'lesson'), get_string('numberofpagestoshow_help', 'lesson'),
array('value' => '1', 'adv' => true), $pages));
// Grade.
$settings->add(new admin_setting_heading('lesson/grade', get_string('gradenoun'), ''));
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/practice',
get_string('practice', 'lesson'), get_string('practice_help', 'lesson'),
array('value' => 0, 'adv' => false), $yesno));
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/customscoring',
get_string('customscoring', 'lesson'), get_string('customscoring_help', 'lesson'),
array('value' => 1, 'adv' => true), $yesno));
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/retakesallowed',
get_string('retakesallowed', 'lesson'), get_string('retakesallowed_help', 'lesson'),
array('value' => 0, 'adv' => false), $yesno));
$options = array();
$options[0] = get_string('usemean', 'lesson');
$options[1] = get_string('usemaximum', 'lesson');
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/handlingofretakes',
get_string('handlingofretakes', 'lesson'), get_string('handlingofretakes_help', 'lesson'),
array('value' => 0, 'adv' => true), $options));
$settings->add(new admin_setting_configselect_with_advanced('mod_lesson/minimumnumberofquestions',
get_string('minimumnumberofquestions', 'lesson'), get_string('minimumnumberofquestions_help', 'lesson'),
array('value' => 0, 'adv' => true), $pages));
}
+90
View File
@@ -0,0 +1,90 @@
<?php
// 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/>.
/**
* Sets up the tabs used by the lesson pages for teachers.
*
* This file was adapted from the mod/quiz/tabs.php
*
* @package mod_lesson
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or late
*/
defined('MOODLE_INTERNAL') || die();
/// This file to be included so we can assume config.php has already been included.
global $DB;
if (empty($lesson)) {
throw new \moodle_exception('cannotcallscript');
}
if (!isset($currenttab)) {
$currenttab = '';
}
if (!isset($cm)) {
$cm = get_coursemodule_from_instance('lesson', $lesson->id);
$context = context_module::instance($cm->id);
}
if (!isset($course)) {
$course = $DB->get_record('course', array('id' => $lesson->course));
}
$tabs = $row = $inactive = $activated = array();
/// user attempt count for reports link hover (completed attempts - much faster)
$attemptscount = $DB->count_records('lesson_grades', array('lessonid'=>$lesson->id));
$row[] = new tabobject('view', "$CFG->wwwroot/mod/lesson/view.php?id=$cm->id", get_string('preview', 'lesson'), get_string('previewlesson', 'lesson', format_string($lesson->name)));
$row[] = new tabobject('edit', "$CFG->wwwroot/mod/lesson/edit.php?id=$cm->id", get_string('edit', 'lesson'), get_string('edita', 'moodle', format_string($lesson->name)));
if (has_capability('mod/lesson:viewreports', $context)) {
$row[] = new tabobject('reports', "$CFG->wwwroot/mod/lesson/report.php?id=$cm->id", get_string('reports', 'lesson'),
get_string('viewreports2', 'lesson', $attemptscount));
}
if (has_capability('mod/lesson:grade', $context)) {
$row[] = new tabobject('essay', "$CFG->wwwroot/mod/lesson/essay.php?id=$cm->id", get_string('manualgrading', 'lesson'));
}
$tabs[] = $row;
switch ($currenttab) {
case 'reportoverview':
case 'reportdetail':
/// sub tabs for reports (overview and detail)
$inactive[] = 'reports';
$activated[] = 'reports';
$row = array();
$row[] = new tabobject('reportoverview', "$CFG->wwwroot/mod/lesson/report.php?id=$cm->id&amp;action=reportoverview", get_string('overview', 'lesson'));
$row[] = new tabobject('reportdetail', "$CFG->wwwroot/mod/lesson/report.php?id=$cm->id&amp;action=reportdetail", get_string('detailedstats', 'lesson'));
$tabs[] = $row;
break;
case 'collapsed':
case 'full':
case 'single':
/// sub tabs for edit view (collapsed and expanded aka full)
$inactive[] = 'edit';
$activated[] = 'edit';
$row = array();
$row[] = new tabobject('collapsed', "$CFG->wwwroot/mod/lesson/edit.php?id=$cm->id&amp;mode=collapsed", get_string('collapsed', 'lesson'));
$row[] = new tabobject('full', "$CFG->wwwroot/mod/lesson/edit.php?id=$cm->id&amp;mode=full", get_string('full', 'lesson'));
$tabs[] = $row;
break;
}
print_tabs($tabs, $currenttab, $inactive, $activated);
@@ -0,0 +1,85 @@
{{!
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/>.
}}
{{!
@template mod_lesson/edit_action_area
Edit navigation for the lesson.
Classes required for JS:
* none
Context variables required for this template:
* see mod/lesson/classes/output/edit_action_area.php
Example context (json):
{
"back": {
"link": "https://moodle.org",
"text": "Back"
},
"viewselect": {
"id": "url_select_test",
"action": "https://example.com/post",
"formid": "url_select_form",
"sesskey": "sesskey",
"label": "core/url_select",
"helpicon": {
"title": "Help with something",
"text": "Help with something",
"url": "http://example.org/help",
"linktext": "",
"icon":{
"extraclasses": "iconhelp",
"attributes": [
{"name": "src", "value": "../../../pix/help.svg"},
{"name": "alt", "value": "Help icon"}
]
}
},
"showbutton": "Go",
"options": [{
"name": "Group 1", "isgroup": true, "options":
[
{"name": "Item 1", "isgroup": false, "value": "1"},
{"name": "Item 2", "isgroup": false, "value": "2"}
]},
{"name": "Group 2", "isgroup": true, "options":
[
{"name": "Item 3", "isgroup": false, "value": "3"},
{"name": "Item 4", "isgroup": false, "value": "4"}
]}],
"disabled": false,
"title": "Some cool title"
},
"headinglevel": 3,
"heading": "Some heading"
}
}}
<div class="container-fluid tertiary-navigation">
<div class="row">
<div class="navitem">
<a class="btn btn-secondary" href="{{back.link}}">{{back.text}}</a>
</div>
{{#viewselect}}
<div class="navitem">
{{>core/url_select}}
</div>
{{/viewselect}}
</div>
</div>
<h{{headinglevel}}>{{heading}}</h{{headinglevel}}>
@@ -0,0 +1,94 @@
{{!
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/>.
}}
{{!
@template mod_lesson/edit_action_buttons
Actions panel for essay grading.
Classes required for JS:
* none
Context variables required for this template:
* see mod/lesson/classes/output/edit_action_buttons.php
Example context (json):
{
"edit": {
"button": {
"id" : "id1",
"method" : "get",
"url" : "#",
"formid": "1",
"primary" : true,
"tooltip" : "This is a tooltip",
"label" : "Edit",
"attributes": [
{
"name": "data-attribute",
"value": "yeah"
}
]
}
},
"gradeessays": {
"button": {
"id" : "id2",
"method" : "get",
"url" : "#",
"formid": "2",
"primary" : false,
"tooltip" : "This is a tooltip",
"label" : "Grade essays",
"attributes": [
{
"name": "data-attribute",
"value": "yeah"
}
]
}
}
}
}}
<div class="container-fluid tertiary-navigation">
<div class="row">
{{#edit}}
{{#button}}
<div class="navitem">
{{>core/single_button}}
</div>
{{/button}}
{{/edit}}
{{#editcontents}}
{{#button}}
<div class="navitem">
{{>core/single_button}}
</div>
{{/button}}
{{/editcontents}}
{{#gradeessays}}
{{#button}}
<div class="navitem">
{{>core/single_button}}
</div>
{{/button}}
{{/gradeessays}}
</div>
</div>
{{#notification}}
{{> core/notification }}
{{/notification}}
@@ -0,0 +1,87 @@
{{!
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/>.
}}
{{!
@template mod_lesson/override_action_menu
A select menu and button for user and group overrides.
Classes required for JS:
* none
Context variables required for this template:
* see mod/lesson/classes/output/override_action_menu.php
Example context (json):
{
"urlselect": {
"id": "url_select_test",
"action": "https://example.com/post",
"formid": "url_select_form",
"sesskey": "sesskey",
"label": "core/url_select",
"helpicon": {
"title": "Help with something",
"text": "Help with something",
"url": "http://example.org/help",
"linktext": "",
"icon":{
"extraclasses": "iconhelp",
"attributes": [
{"name": "src", "value": "../../../pix/help.svg"},
{"name": "alt", "value": "Help icon"}
]
}
},
"showbutton": "Go",
"options": [{
"name": "Group 1", "isgroup": true, "options":
[
{"name": "Item 1", "isgroup": false, "value": "1"},
{"name": "Item 2", "isgroup": false, "value": "2"}
]},
{"name": "Group 2", "isgroup": true, "options":
[
{"name": "Item 3", "isgroup": false, "value": "3"},
{"name": "Item 4", "isgroup": false, "value": "4"}
]}],
"disabled": false,
"title": "Some cool title"
},
"addoverride": {
"link": "https://moodle.org",
"text": "Add override"
},
"headinglevel": 3,
"heading": "Some heading"
}
}}
<div class="container-fluid tertiary-navigation">
<div class="row">
{{#urlselect}}
<div class="navitem">
{{>core/url_select}}
</div>
{{/urlselect}}
{{#addoverride}}
<div class="navitem">
<a class="btn btn-primary" href="{{link}}">{{text}}</a>
</div>
{{/addoverride}}
</div>
</div>
<h{{headinglevel}}>{{heading}}</h{{headinglevel}}>
@@ -0,0 +1,76 @@
{{!
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/>.
}}
{{!
@template mod_lesson/report_action_menu
Navigation element for different reports.
Classes required for JS:
* none
Context variables required for this template:
* see mod/lesson/classes/output/report_action_menu.php
Example context (json):
{
"id": "url_select_test",
"action": "https://example.com/post",
"formid": "url_select_form",
"sesskey": "sesskey",
"label": "core/url_select",
"helpicon": {
"title": "Help with something",
"text": "Help with something",
"url": "http://example.org/help",
"linktext": "",
"icon":{
"extraclasses": "iconhelp",
"attributes": [
{"name": "src", "value": "../../../pix/help.svg"},
{"name": "alt", "value": "Help icon"}
]
}
},
"showbutton": "Go",
"options": [{
"name": "Group 1", "isgroup": true, "options":
[
{"name": "Item 1", "isgroup": false, "value": "1"},
{"name": "Item 2", "isgroup": false, "value": "2"}
]},
{"name": "Group 2", "isgroup": true, "options":
[
{"name": "Item 3", "isgroup": false, "value": "3"},
{"name": "Item 4", "isgroup": false, "value": "4"}
]}],
"disabled": false,
"title": "Some cool title",
"headinglevel": 3,
"heading": "Some heading"
}
}}
<div class="container-fluid tertiary-navigation">
{{#reportselect}}
<div class="row">
<div class="navitem">
{{>core/url_select}}
</div>
</div>
{{/reportselect}}
</div>
<h{{headinglevel}}>{{heading}}</h{{headinglevel}}>
@@ -0,0 +1,186 @@
<?php
// 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/>.
namespace mod_lesson\backup;
use core_external\external_api;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . "/phpunit/classes/restore_date_testcase.php");
/**
* Restore date tests.
*
* @package mod_lesson
* @copyright 2017 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_date_test extends \restore_date_testcase {
/**
* Creates an attempt for the given userwith a correct or incorrect answer and optionally finishes it.
*
* TODO This api can be better extracted to a generator.
*
* @param \stdClass $lesson Lesson object.
* @param \stdClass $page page object.
* @param boolean $correct If the answer should be correct.
* @param boolean $finished If we should finish the attempt.
*
* @return array the result of the attempt creation or finalisation.
*/
protected function create_attempt($lesson, $page, $correct = true, $finished = false) {
global $DB, $USER;
// First we need to launch the lesson so the timer is on.
\mod_lesson_external::launch_attempt($lesson->id);
$DB->set_field('lesson', 'feedback', 1, array('id' => $lesson->id));
$DB->set_field('lesson', 'progressbar', 1, array('id' => $lesson->id));
$DB->set_field('lesson', 'custom', 0, array('id' => $lesson->id));
$DB->set_field('lesson', 'maxattempts', 3, array('id' => $lesson->id));
$answercorrect = 0;
$answerincorrect = 0;
$p2answers = $DB->get_records('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $page->id), 'id');
foreach ($p2answers as $answer) {
if ($answer->jumpto == 0) {
$answerincorrect = $answer->id;
} else {
$answercorrect = $answer->id;
}
}
$data = array(
array(
'name' => 'answerid',
'value' => $correct ? $answercorrect : $answerincorrect,
),
array(
'name' => '_qf__lesson_display_answer_form_truefalse',
'value' => 1,
)
);
$result = \mod_lesson_external::process_page($lesson->id, $page->id, $data);
$result = external_api::clean_returnvalue(\mod_lesson_external::process_page_returns(), $result);
// Create attempt.
$newpageattempt = [
'lessonid' => $lesson->id,
'pageid' => $page->id,
'userid' => $USER->id,
'answerid' => $answercorrect,
'retry' => 1, // First attempt is always 0.
'correct' => 1,
'useranswer' => '1',
'timeseen' => time(),
];
$DB->insert_record('lesson_attempts', (object) $newpageattempt);
if ($finished) {
$result = \mod_lesson_external::finish_attempt($lesson->id);
$result = external_api::clean_returnvalue(\mod_lesson_external::finish_attempt_returns(), $result);
}
return $result;
}
/**
* Test restore dates.
*/
public function test_restore_dates(): void {
global $DB, $USER;
// Create lesson data.
$record = ['available' => 100, 'deadline' => 100, 'timemodified' => 100];
list($course, $lesson) = $this->create_course_and_module('lesson', $record);
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$page = $lessongenerator->create_content($lesson);
$page2 = $lessongenerator->create_question_truefalse($lesson);
$this->create_attempt($lesson, $page2, true, true);
$timer = $DB->get_record('lesson_timer', ['lessonid' => $lesson->id]);
// Lesson grade.
$timestamp = 100;
$grade = new \stdClass();
$grade->lessonid = $lesson->id;
$grade->userid = $USER->id;
$grade->grade = 8.9;
$grade->completed = $timestamp;
$grade->id = $DB->insert_record('lesson_grades', $grade);
// User override.
$override = (object)[
'lessonid' => $lesson->id,
'groupid' => 0,
'userid' => $USER->id,
'sortorder' => 1,
'available' => 100,
'deadline' => 200
];
$DB->insert_record('lesson_overrides', $override);
// Set time fields to a constant for easy validation.
$DB->set_field('lesson_pages', 'timecreated', $timestamp);
$DB->set_field('lesson_pages', 'timemodified', $timestamp);
$DB->set_field('lesson_answers', 'timecreated', $timestamp);
$DB->set_field('lesson_answers', 'timemodified', $timestamp);
$DB->set_field('lesson_attempts', 'timeseen', $timestamp);
// Do backup and restore.
$newcourseid = $this->backup_and_restore($course);
$newlesson = $DB->get_record('lesson', ['course' => $newcourseid]);
$this->assertFieldsNotRolledForward($lesson, $newlesson, ['timemodified']);
$props = ['available', 'deadline'];
$this->assertFieldsRolledForward($lesson, $newlesson, $props);
$newpages = $DB->get_records('lesson_pages', ['lessonid' => $newlesson->id]);
$newanswers = $DB->get_records('lesson_answers', ['lessonid' => $newlesson->id]);
$newgrade = $DB->get_record('lesson_grades', ['lessonid' => $newlesson->id]);
$newoverride = $DB->get_record('lesson_overrides', ['lessonid' => $newlesson->id]);
$newtimer = $DB->get_record('lesson_timer', ['lessonid' => $newlesson->id]);
$newattempt = $DB->get_record('lesson_attempts', ['lessonid' => $newlesson->id]);
// Page time checks.
foreach ($newpages as $newpage) {
$this->assertEquals($timestamp, $newpage->timemodified);
$this->assertEquals($timestamp, $newpage->timecreated);
}
// Page answers time checks.
foreach ($newanswers as $newanswer) {
$this->assertEquals($timestamp, $newanswer->timemodified);
$this->assertEquals($timestamp, $newanswer->timecreated);
}
// Lesson override time checks.
$diff = $this->get_diff();
$this->assertEquals($override->available + $diff, $newoverride->available);
$this->assertEquals($override->deadline + $diff, $newoverride->deadline);
// Lesson grade time checks.
$this->assertEquals($timestamp, $newgrade->completed);
// Lesson timer time checks.
$this->assertEquals($timer->starttime, $newtimer->starttime);
$this->assertEquals($timer->lessontime, $newtimer->lessontime);
// Lesson attempt time check.
$this->assertEquals($timestamp, $newattempt->timeseen);
}
}
@@ -0,0 +1,98 @@
<?php
// 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/>.
namespace mod_lesson\backup;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . "/phpunit/classes/restore_date_testcase.php");
/**
* Restore override tests.
*
* @package mod_lesson
* @author 2019 Nathan Nguyen <nathannguyen@catalyst-au.net>
* @copyright Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_override_test extends \restore_date_testcase {
/**
* Test restore overrides.
*/
public function test_restore_overrides(): void {
global $DB, $USER;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$lessongen = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$lesson = $lessongen->create_instance(['course' => $course->id]);
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$now = 100;
$groupoverride1 = (object)[
'lessonid' => $lesson->id,
'groupid' => $group1->id,
'available' => $now,
'deadline' => $now + 20
];
$DB->insert_record('lesson_overrides', $groupoverride1);
$groupoverride2 = (object)[
'lessonid' => $lesson->id,
'groupid' => $group2->id,
'available' => $now,
'deadline' => $now + 40
];
$DB->insert_record('lesson_overrides', $groupoverride2);
// Current quiz overrides.
$overrides = $DB->get_records('lesson_overrides', ['lessonid' => $lesson->id]);
$this->assertEquals(2, count($overrides));
// User override.
$useroverride = (object)[
'lessonid' => $lesson->id,
'userid' => $USER->id,
'sortorder' => 1,
'available' => 100,
'deadline' => 200
];
$DB->insert_record('lesson_overrides', $useroverride);
// Current quiz overrides.
$overrides = $DB->get_records('lesson_overrides', ['lessonid' => $lesson->id]);
$this->assertEquals(3, count($overrides));
// Back up and restore including group info and user info.
set_config('backup_general_groups', 1, 'backup');
$newcourseid = $this->backup_and_restore($course);
$newquiz = $DB->get_record('lesson', ['course' => $newcourseid]);
$overrides = $DB->get_records('lesson_overrides', ['lessonid' => $newquiz->id]);
// 2 groups overrides and 1 user override.
$this->assertEquals(3, count($overrides));
// Back up and restore with user info and without group info.
set_config('backup_general_groups', 0, 'backup');
$newcourseid = $this->backup_and_restore($course);
$newquiz = $DB->get_record('lesson', ['course' => $newcourseid]);
$overrides = $DB->get_records('lesson_overrides', ['lessonid' => $newquiz->id]);
// 1 user override.
$this->assertEquals(1, count($overrides));
}
}
@@ -0,0 +1,91 @@
@mod @mod_lesson
Feature: Numeric and short answer questions have a section to catch all other student answers.
In order for lesson pages to catch any student answer
As a teacher
I need to fill in the sections to catch all other student answers
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
Given the following "activity" exists:
| activity | lesson |
| course | C1 |
| idnumber | 0001 |
| name | Test lesson name |
| maxattempts | 3 |
And I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I expand all fieldsets
And I set the following fields to these values:
| Provide option to try a question again | Yes |
And I press "Save and display"
Scenario: I can create a numerical question with an option to catch all student responses.
Given I follow "Add a question page"
And I set the field "Select a question type" to "Numerical"
And I press "Add a question page"
And I set the following fields to these values:
| Page title | Numerical question |
| Page contents | What is 1 + 2? |
| id_answer_editor_0 | 3 |
| id_jumpto_0 | End of lesson |
| id_enableotheranswers | 1 |
| id_jumpto_6 | Next page |
And I press "Save page"
And I select "Add a content page" from the "qtype" singleselect
And I set the following fields to these values:
| Page title | Just move on page |
| Page contents | You are here to move on |
| id_answer_editor_0 | End this lesson |
| id_jumpto_0 | End of lesson |
And I press "Save page"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I set the field "Your answer" to "5"
And I press "Submit"
And I should see "That's the wrong answer"
And I press "Yes, I'd like to try again"
And I should see "What is 1 + 2?"
And I set the field "Your answer" to "7"
And I press "Submit"
And I should see "That's the wrong answer"
When I press "No, I just want to go on to the next question"
Then I should see "You are here to move on"
Scenario: I can create a shortanswer question with an option to catch all student responses.
Given I follow "Add a question page"
And I set the field "Select a question type" to "Short answer"
And I press "Add a question page"
And I set the following fields to these values:
| Page title | Short answer question |
| Page contents | Please type in cat |
| id_answer_editor_0 | 3 |
| id_jumpto_0 | End of lesson |
| id_enableotheranswers | 1 |
| id_jumpto_6 | Next page |
And I press "Save page"
And I select "Add a content page" from the "qtype" singleselect
And I set the following fields to these values:
| Page title | Just move on page |
| Page contents | You are here to move on |
| id_answer_editor_0 | End this lesson |
| id_jumpto_0 | End of lesson |
And I press "Save page"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I set the field "Your answer" to "dog"
And I press "Submit"
And I should see "That's the wrong answer"
And I press "Yes, I'd like to try again"
And I should see "Please type in cat"
And I set the field "Your answer" to "bird"
And I press "Submit"
And I should see "That's the wrong answer"
When I press "No, I just want to go on to the next question"
Then I should see "You are here to move on"

Some files were not shown because too many files have changed in this diff Show More