From c6f9f061469d4342309dcfb01a5caaaf8e6659fa Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Tue, 24 Jan 2017 17:06:28 +0100 Subject: [PATCH 1/2] MDL-57759 mod_lesson: New settings for allowing offline attempts --- .../backup/moodle2/backup_lesson_stepslib.php | 4 ++-- mod/lesson/db/install.xml | 2 ++ mod/lesson/db/upgrade.php | 23 +++++++++++++++++++ mod/lesson/lang/en/lesson.php | 4 ++++ mod/lesson/mod_form.php | 18 ++++++++++++++- mod/lesson/version.php | 2 +- 6 files changed, 49 insertions(+), 4 deletions(-) diff --git a/mod/lesson/backup/moodle2/backup_lesson_stepslib.php b/mod/lesson/backup/moodle2/backup_lesson_stepslib.php index c2351c85839..388d55d9a46 100644 --- a/mod/lesson/backup/moodle2/backup_lesson_stepslib.php +++ b/mod/lesson/backup/moodle2/backup_lesson_stepslib.php @@ -77,7 +77,7 @@ class backup_lesson_activity_structure_step extends backup_activity_structure_st 'mediafile', 'mediaheight', 'mediawidth', 'mediaclose', 'slideshow', 'width', 'height', 'bgcolor', 'displayleft', 'displayleftif', 'progressbar', 'available', 'deadline', 'timemodified', - 'completionendreached', 'completiontimespent' + 'completionendreached', 'completiontimespent', 'allowofflineattempts' )); // The lesson_pages table @@ -131,7 +131,7 @@ class backup_lesson_activity_structure_step extends backup_activity_structure_st // 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' + 'userid', 'starttime', 'lessontime', 'completed', 'timemodifiedoffline' )); $overrides = new backup_nested_element('overrides'); diff --git a/mod/lesson/db/install.xml b/mod/lesson/db/install.xml index 1a4694af8f2..583b408d8d4 100644 --- a/mod/lesson/db/install.xml +++ b/mod/lesson/db/install.xml @@ -47,6 +47,7 @@ + @@ -145,6 +146,7 @@ + diff --git a/mod/lesson/db/upgrade.php b/mod/lesson/db/upgrade.php index d12bb8ed32c..578f6c6c092 100644 --- a/mod/lesson/db/upgrade.php +++ b/mod/lesson/db/upgrade.php @@ -390,5 +390,28 @@ function xmldb_lesson_upgrade($oldversion) { // Automatically generated Moodle v3.2.0 release upgrade line. // Put any upgrade step following this. + if ($oldversion < 2016120515) { + // Define new fields to be added to lesson. + $table = new xmldb_table('lesson'); + $field = new xmldb_field('allowofflineattempts', XMLDB_TYPE_INTEGER, '1', null, null, null, 0, 'completiontimespent'); + // Conditionally launch add field allowofflineattempts. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + // Lesson savepoint reached. + upgrade_mod_savepoint(true, 2016120515, 'lesson'); + } + if ($oldversion < 2016120516) { + // New field for lesson_timer. + $table = new xmldb_table('lesson_timer'); + $field = new xmldb_field('timemodifiedoffline', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0, 'completed'); + // Conditionally launch add field timemodifiedoffline. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + // Lesson savepoint reached. + upgrade_mod_savepoint(true, 2016120516, 'lesson'); + } + return true; } diff --git a/mod/lesson/lang/en/lesson.php b/mod/lesson/lang/en/lesson.php index 4b3cea46fc4..d0f361803b3 100644 --- a/mod/lesson/lang/en/lesson.php +++ b/mod/lesson/lang/en/lesson.php @@ -52,6 +52,10 @@ $string['addnewgroupoverride'] = 'Add group override'; $string['addnewuseroverride'] = 'Add user override'; $string['additionalattemptsremaining'] = 'Completed, You can re-attempt this lesson'; $string['addpage'] = 'Add a page'; +$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'; diff --git a/mod/lesson/mod_form.php b/mod/lesson/mod_form.php index 9c9df170a5f..252c2b5c506 100644 --- a/mod/lesson/mod_form.php +++ b/mod/lesson/mod_form.php @@ -49,7 +49,7 @@ class mod_lesson_mod_form extends moodleform_mod { } function definition() { - global $CFG, $COURSE, $DB; + global $CFG, $COURSE, $DB, $OUTPUT; $mform = $this->_form; @@ -249,6 +249,22 @@ class mod_lesson_mod_form extends moodleform_mod { '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')); diff --git a/mod/lesson/version.php b/mod/lesson/version.php index d55d771cc4a..e65884b5cb6 100644 --- a/mod/lesson/version.php +++ b/mod/lesson/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2016120514; // The current module version (Date: YYYYMMDDXX) +$plugin->version = 2016120516; // The current module version (Date: YYYYMMDDXX) $plugin->requires = 2016112900; // Requires this Moodle version $plugin->component = 'mod_lesson'; // Full name of the plugin (used for diagnostics) $plugin->cron = 0; From 2db90397f1ec0a49c63de14610bcb8afeff1aee4 Mon Sep 17 00:00:00 2001 From: Juan Leyva Date: Tue, 24 Jan 2017 17:08:03 +0100 Subject: [PATCH 2/2] MDL-57759 mod_lesson: Handle offline attempts in view and WS - Notify the user if there are previous offline attempts - Set the offline attempts and return additional information via Web Services --- mod/lesson/classes/external.php | 10 +++++++--- mod/lesson/lang/en/lesson.php | 1 + mod/lesson/locallib.php | 16 +++++++++++++--- mod/lesson/tests/external_test.php | 4 +++- mod/lesson/view.php | 10 ++++++++++ 5 files changed, 34 insertions(+), 7 deletions(-) diff --git a/mod/lesson/classes/external.php b/mod/lesson/classes/external.php index 55f428c7f7b..6945716afeb 100644 --- a/mod/lesson/classes/external.php +++ b/mod/lesson/classes/external.php @@ -118,7 +118,7 @@ class mod_lesson_external extends external_api { 'maxanswers', 'maxattempts', 'review', 'nextpagedefault', 'feedback', 'minquestions', 'maxpages', 'timelimit', 'retake', 'mediafile', 'mediaheight', 'mediawidth', 'mediaclose', 'slideshow', 'width', 'height', 'bgcolor', 'displayleft', 'displayleftif', - 'progressbar'); + 'progressbar', 'allowofflineattempts'); // Fields only for managers. if ($lesson->can_manage()) { @@ -199,6 +199,8 @@ class mod_lesson_external extends external_api { VALUE_OPTIONAL), 'completiontimespent' => new external_value(PARAM_INT, 'Student must do this activity at least for', VALUE_OPTIONAL), + 'allowofflineattempts' => new external_value(PARAM_INT, 'Whether to allow the lesson to be attempted + offline in the mobile app', VALUE_OPTIONAL), 'visible' => new external_value(PARAM_INT, 'Visible?', VALUE_OPTIONAL), 'groupmode' => new external_value(PARAM_INT, 'Group mode', VALUE_OPTIONAL), 'groupingid' => new external_value(PARAM_INT, 'Grouping id', VALUE_OPTIONAL), @@ -959,6 +961,7 @@ class mod_lesson_external extends external_api { 'starttime' => new external_value(PARAM_INT, 'First access time for a new timer session'), 'lessontime' => new external_value(PARAM_INT, 'Last access time to the lesson during the timer session'), 'completed' => new external_value(PARAM_INT, 'If the lesson for this timer was completed'), + 'timemodifiedoffline' => new external_value(PARAM_INT, 'Last modified time via webservices.'), ), 'The timers' ) @@ -1332,8 +1335,9 @@ class mod_lesson_external extends external_api { 'answerfiles' => external_util::get_area_files($context->id, 'mod_lesson', 'page_answers', $a->id), 'responsefiles' => external_util::get_area_files($context->id, 'mod_lesson', 'page_responses', $a->id), ); - // For managers, return all the information (including scoring, jumps). - if ($lesson->can_manage()) { + // For managers, return all the information (including correct answers, jumps). + // If the teacher enabled offline attempts, this information will be downloaded too. + if ($lesson->can_manage() || $lesson->allowofflineattempts) { $extraproperties = array('jumpto', 'grade', 'score', 'flags', 'timecreated', 'timemodified'); foreach ($extraproperties as $prop) { $answer[$prop] = $a->{$prop}; diff --git a/mod/lesson/lang/en/lesson.php b/mod/lesson/lang/en/lesson.php index d0f361803b3..b61512ee5a0 100644 --- a/mod/lesson/lang/en/lesson.php +++ b/mod/lesson/lang/en/lesson.php @@ -359,6 +359,7 @@ $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['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.'; diff --git a/mod/lesson/locallib.php b/mod/lesson/locallib.php index 24d85cdc144..721e56842a2 100644 --- a/mod/lesson/locallib.php +++ b/mod/lesson/locallib.php @@ -1448,6 +1448,7 @@ abstract class lesson_add_page_form_base extends moodleform { * @property int $available Timestamp of when this lesson becomes available * @property int $deadline Timestamp of when this lesson is no longer available * @property int $timemodified Timestamp when lesson was last modified + * @property int $allowofflineattempts Whether to allow the lesson to be attempted offline in the mobile app * * These properties are calculated * @property int $firstpageid Id of the first page of this lesson (prevpageid=0) @@ -2035,11 +2036,16 @@ class lesson extends lesson_base { $event->trigger(); $USER->startlesson[$this->properties->id] = true; + + $timenow = time(); $startlesson = new stdClass; $startlesson->lessonid = $this->properties->id; $startlesson->userid = $USER->id; - $startlesson->starttime = time(); - $startlesson->lessontime = time(); + $startlesson->starttime = $timenow; + $startlesson->lessontime = $timenow; + if (WS_SERVER) { + $startlesson->timemodifiedoffline = $timenow; + } $DB->insert_record('lesson_timer', $startlesson); if ($this->properties->timelimit) { $this->add_message(get_string('timelimitwarning', 'lesson', format_time($this->properties->timelimit)), 'center'); @@ -2095,7 +2101,11 @@ class lesson extends lesson_base { } } - $timer->lessontime = time(); + $timenow = time(); + $timer->lessontime = $timenow; + if (WS_SERVER) { + $timer->timemodifiedoffline = $timenow; + } $timer->completed = $endreached; $DB->update_record('lesson_timer', $timer); diff --git a/mod/lesson/tests/external_test.php b/mod/lesson/tests/external_test.php index 66e7ad91c1b..c86ff3bd605 100644 --- a/mod/lesson/tests/external_test.php +++ b/mod/lesson/tests/external_test.php @@ -131,7 +131,7 @@ class mod_lesson_external_testcase extends externallib_advanced_testcase { 'maxanswers', 'maxattempts', 'review', 'nextpagedefault', 'feedback', 'minquestions', 'maxpages', 'timelimit', 'retake', 'mediafile', 'mediafiles', 'mediaheight', 'mediawidth', 'mediaclose', 'slideshow', 'width', 'height', 'bgcolor', 'displayleft', 'displayleftif', - 'progressbar'); + 'progressbar', 'allowofflineattempts'); // Add expected coursemodule and data. $lesson1 = $this->lesson; @@ -661,6 +661,7 @@ class mod_lesson_external_testcase extends externallib_advanced_testcase { $timer1->completed = 1; $timer1->starttime = time() - WEEKSECS; $timer1->lessontime = time(); + $timer1->timemodifiedoffline = time(); $timer1->id = $DB->insert_record("lesson_timer", $timer1); $timer2 = new stdClass; @@ -669,6 +670,7 @@ class mod_lesson_external_testcase extends externallib_advanced_testcase { $timer2->completed = 0; $timer2->starttime = time() - DAYSECS; $timer2->lessontime = time() + 1; + $timer2->timemodifiedoffline = time() + 1; $timer2->id = $DB->insert_record("lesson_timer", $timer2); // Test retrieve timers. diff --git a/mod/lesson/view.php b/mod/lesson/view.php index b308cec5c69..0ca0495cc92 100644 --- a/mod/lesson/view.php +++ b/mod/lesson/view.php @@ -121,6 +121,16 @@ if (empty($pageid)) { $lastpageseen = $lesson->get_last_page_seen($retries); + // Check if the lesson was attempted in an external device like the mobile app. + // This check makes sense only when the lesson allows offline attempts. + if ($lesson->allowofflineattempts && $timers = $lesson->get_user_timers($USER->id, 'starttime DESC', '*', 0, 1)) { + $timer = current($timers); + if (!empty($timer->timemodifiedoffline)) { + $lasttime = format_time(time() - $timer->timemodifiedoffline); + $lesson->add_message(get_string('offlinedatamessage', 'lesson', $lasttime), 'warning'); + } + } + // Check to see if end of lesson was reached. if (($lastpageseen !== false && ($lastpageseen != LESSON_EOL))) { // End not reached. Check if the user left.