diff --git a/classes/event/attempt_submitted.php b/classes/event/attempt_submitted.php new file mode 100644 index 0000000000000000000000000000000000000000..8392b2469d4f9550457ccae5ec81d468c29e0689 --- /dev/null +++ b/classes/event/attempt_submitted.php @@ -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/>. + +/** + * Attempt submitted event + * + * @package mod_hvp + * @copyright 2020 Joubel AS <contact@joubel.com> + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_hvp\event; + +defined('MOODLE_INTERNAL') || die(); + +/** + * The mod_hvp instance list viewed event class. + * + * @package mod_hvp + * @copyright @copyright 2016 Joubel AS <contact@joubel.com> + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class attempt_submitted extends \core\event\base +{ + + /** + * @inheritDoc + */ + protected function init() { + $this->data['crud'] = 'u'; + $this->data['edulevel'] = self::LEVEL_PARTICIPATING; + } +} diff --git a/classes/user_grades.php b/classes/user_grades.php index 103ea6db39a3b06e343a83f5010c18344620aa81..eca660a53fffd6286bc1b2068ccc31713fee2701 100644 --- a/classes/user_grades.php +++ b/classes/user_grades.php @@ -96,6 +96,12 @@ class user_grades { $content->name, $content->major_version . '.' . $content->minor_version ); + // Trigger Moodle event for async notification messages + $event = \mod_hvp\event\attempt_submitted::create([ + 'context' => $context, + ]); + $event->trigger(); + \H5PCore::ajaxSuccess(); } diff --git a/db/access.php b/db/access.php index 84757113d3aed7fa107476ec2d8a331b0d4b34e4..5658c6e3f18a34858c7439111a9a92fcabe6d0fb 100644 --- a/db/access.php +++ b/db/access.php @@ -173,4 +173,18 @@ $capabilities = array( ) ), + // Receive a confirmation message of own h5p submission. + 'mod/hvp:emailconfirmsubmission' => array( + 'captype' => 'read', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => array() + ), + + // Receive a notification message of other peoples' h5p submissions. + 'mod/hvp:emailnotifysubmission' => array( + 'captype' => 'read', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => array() + ), + ); diff --git a/db/events.php b/db/events.php new file mode 100644 index 0000000000000000000000000000000000000000..c6f7983bf08934a3d025dc600068932fd531ce3f --- /dev/null +++ b/db/events.php @@ -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/>. + +/** + * Add event handlers for H5P + * + * @package mod_hvp + * @category event + * @copyright 2020 Joubel AS <contact@joubel.com> + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +$observers = array( + + // Handle attempt submitted event, as a way to send confirmation messages asynchronously. + array( + 'eventname' => '\mod_hvp\event\attempt_submitted', + 'includefile' => '/mod/hvp/locallib.php', + 'callback' => 'hvp_attempt_submitted_handler', + 'internal' => false + ), +); diff --git a/db/messages.php b/db/messages.php new file mode 100644 index 0000000000000000000000000000000000000000..a08d8f0d56f009f42ece8d954c76393312cd1ed4 --- /dev/null +++ b/db/messages.php @@ -0,0 +1,40 @@ +<?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 message sent) for the hvp module. + * + * @package mod_hvp + * @copyright 2020 Joubel AS <contact@joubel.com> + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$messageproviders = array( + // Notify teacher that a student has submitted an attempt. + 'submission' => array( + 'capability' => 'mod/hvp:emailnotifysubmission' + ), + + // Confirm a student's quiz attempt. + 'confirmation' => array( + 'capability' => 'mod/hvp:emailconfirmsubmission', + 'defaults' => array( + 'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_LOGGEDIN + MESSAGE_DEFAULT_LOGGEDOFF, + ), + ), +); diff --git a/lang/en/hvp.php b/lang/en/hvp.php index 7eaa5755b0d4c0916f4a56556157f2524bfe70d3..ce26356cae3313d45e9937fedbba0b7159358a4f 100644 --- a/lang/en/hvp.php +++ b/lang/en/hvp.php @@ -225,6 +225,8 @@ $string['hvp:userestrictedlibraries'] = 'Use restricted H5P content types'; $string['hvp:updatelibraries'] = 'Install new H5P content types or update existing ones'; $string['hvp:getcachedassets'] = 'Required for viewing H5P activities'; $string['hvp:installrecommendedh5plibraries'] = 'Install new safe H5P content types recommended by H5P.org'; +$string['hvp:emailconfirmsubmission'] = 'Get a confirmation message when submitting'; +$string['hvp:emailnotifysubmission'] = 'Get a notification message when an attempt is submitted'; // Capabilities error messages. $string['nopermissiontogettranslations'] = 'You do not have permissions to retrieve translations'; @@ -448,4 +450,24 @@ $string['unpackedfilesexceedsmaxsize'] = 'The total size of the unpacked files e $string['couldnotreadfilefromzip'] = 'Unable to read file from the package: %fileName'; $string['couldnotparsejsonfromzip'] = 'Unable to parse JSON from the package: %fileName'; $string['couldnotparsepostdata'] = 'Could not parse post data.'; -$string['nombstringexteension'] = 'The mbstring PHP extension is not loaded. H5P needs this to function properly'; \ No newline at end of file +$string['nombstringexteension'] = 'The mbstring PHP extension is not loaded. H5P needs this to function properly'; + +// Messaging api +$string['messageprovider:confirmation'] = 'Confirmation of your own H5P submissions'; +$string['messageprovider:submission'] = 'Notification of H5P submissions'; +$string['emailnotifysubject'] = '{$a->studentname} has completed {$a->hvpname}'; +$string['emailnotifybody'] = 'Hi {$a->username}, + +{$a->studentname} has completed \'{$a->hvpname}\' ({$a->hvpurl}) in course \'{$a->coursename}\'. + +You can review this attempt at {$a->hvpreporturl}.'; +$string['emailnotifysmall'] = '{$a->studentname} has completed {$a->hvpname}. See {$a->hvpreporturl}'; +$string['emailconfirmbody'] = 'Hi {$a->username}, + +Thank you for submitting your answers to \'{$a->hvpname}\' in course \'{$a->coursename}\'. + +This message confirms that your answers have been saved. + +You can access this H5P at {$a->hvpurl}.'; +$string['emailconfirmsmall'] = 'Thank you for submitting your answers to \'{$a->hvpname}\''; +$string['emailconfirmsubject'] = 'Submission confirmation: {$a->hvpname}'; diff --git a/locallib.php b/locallib.php index 72f7eee24911a3c90859d9282dbcb0744bc7643b..f75e4d344ad687d1e46070e57bbc9fe495980413 100644 --- a/locallib.php +++ b/locallib.php @@ -23,6 +23,9 @@ * @copyright 2016 Joubel AS <contact@joubel.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + +use core\message\message; + defined('MOODLE_INTERNAL') || die(); require_once('autoloader.php'); @@ -459,3 +462,192 @@ function hvp_require_view_results_permission($userid, $context, $redirectcontent } } } + +/** + * Sends notification messages to the interested parties that assign the role capability + * + * @param object $recipient user object of the intended recipient + * @param $submitter + * @param object $a associative array of replaceable fields for the templates + * + * @return int|false as for {@link message_send()}. + * @throws coding_exception + */ +function hvp_send_notification($recipient, $submitter, $a) { + // Recipient info for template. + $a->useridnumber = $recipient->id; + $a->username = fullname($recipient); + $a->userusername = $recipient->username; + + // Prepare the message. + $message = new message(); + $message->component = 'mod_hvp'; + $message->name = 'submission'; + $message->userfrom = $submitter; + $message->userto = $recipient; + $message->subject = get_string('emailnotifysubject', 'hvp', $a); + $message->fullmessage = get_string('emailnotifybody', 'hvp', $a); + $message->fullmessageformat = FORMAT_PLAIN; + $message->fullmessagehtml = ''; + $message->smallmessage = get_string('emailnotifysmall', 'hvp', $a); + $message->courseid = $a->courseid; + + $message->contexturl = $a->hvpreporturl; + $message->contexturlname = $a->hvpname; + + return message_send($message); +} + +/** + * Sends a confirmation message to the student confirming that the attempt was processed. + * + * @param object $a useful information that can be used in the message + * subject and body. + * + * @return int|false as for {@link message_send()}. + * @throws coding_exception + */ +function hvp_send_confirmation($recipient, $a) { + // Add information about the recipient to $a. + $a->username = fullname($recipient); + $a->userusername = $recipient->username; + + // Prepare the message. + $eventdata = new \core\message\message(); + $eventdata->courseid = $a->courseid; + $eventdata->component = 'mod_hvp'; + $eventdata->name = 'confirmation'; + $eventdata->notification = 1; + + $eventdata->userfrom = core_user::get_noreply_user(); + $eventdata->userto = $recipient; + $eventdata->subject = get_string('emailconfirmsubject', 'hvp', $a); + $eventdata->fullmessage = get_string('emailconfirmbody', 'hvp', $a); + $eventdata->fullmessageformat = FORMAT_PLAIN; + $eventdata->fullmessagehtml = ''; + + $eventdata->smallmessage = get_string('emailconfirmsmall', 'hvp', $a); + $eventdata->contexturl = $a->hvpurl; + $eventdata->contexturlname = $a->hvpname; + + return message_send($eventdata); +} + +/** + * Send all the required messages when a h5p attempt is submitted. + * + * @param object $course the course + * @param object $hvp the h5p + * @param object $attempt this attempt just finished + * @param context $context the h5p context + * @param object $cm the coursemodule for this h5p + * + * @return bool true if all necessary messages were sent successfully, else false. + * @throws coding_exception + * @throws dml_exception + */ +function hvp_send_notification_messages($course, $hvp, $attempt, $context, $cm) { + global $CFG, $DB; + + // Do nothing if required objects not present. + if (empty($course) or empty($hvp) or empty($attempt) or empty($context)) { + throw new coding_exception('$course, $hvp, $attempt, $context and $cm must all be set.'); + } + + $submitter = $DB->get_record('user', array('id' => $attempt->userid), '*', MUST_EXIST); + + // Check for confirmation required. + $sendconfirm = false; + $notifyexcludeusers = ''; + if (has_capability('mod/hvp:emailconfirmsubmission', $context, $submitter, false)) { + $notifyexcludeusers = $submitter->id; + $sendconfirm = true; + } + + // Check for notifications required. + $notifyfields = 'u.id, u.username, u.idnumber, u.email, u.emailstop, u.lang, + u.timezone, u.mailformat, u.maildisplay, u.auth, u.suspended, u.deleted, '; + $notifyfields .= get_all_user_name_fields(true, 'u'); + $groups = groups_get_all_groups($course->id, $submitter->id, $cm->groupingid); + if (is_array($groups) && count($groups) > 0) { + $groups = array_keys($groups); + } else if (groups_get_activity_groupmode($cm, $course) != NOGROUPS) { + // If the user is not in a group, and the hvp is set to group mode, + // then set $groups to a non-existant id so that only users with + // 'moodle/site:accessallgroups' get notified. + $groups = - 1; + } else { + $groups = ''; + } + $userstonotify = get_users_by_capability($context, 'mod/hvp:emailnotifysubmission', + $notifyfields, '', '', '', $groups, $notifyexcludeusers, false, false, true); + + if (empty($userstonotify) && !$sendconfirm) { + return true; // Nothing to do. + } + + $a = new stdClass(); + // Course info. + $a->courseid = $course->id; + $a->coursename = $course->fullname; + $a->courseshortname = $course->shortname; + + // H5P info. + $a->hvpname = $hvp->name; + $report = "{$CFG->wwwroot}/mod/hvp/review.php?id={$hvp->id}&course={$course->id}&user={$submitter->id}"; + $a->hvpreporturl = $report; + $a->hvpreportlink = '<a href="' . $a->hvpreporturl . '">' . + format_string($hvp->name, true, ['context' => $context]) . ' report</a>'; + $a->hvpurl = $CFG->wwwroot . '/mod/hvp/view.php?id=' . $cm->id; + $a->hvplink = '<a href="' . $a->hvpurl . '">' . + format_string($hvp->name, true, ['context' => $context]) . '</a>'; + $a->hvpid = $hvp->id; + $a->hvpcmid = $cm->id; + + // Student who sat the hvp info. + $a->studentidnumber = $submitter->id; + $a->studentname = fullname($submitter); + $a->studentusername = $submitter->username; + + $allok = true; + + // Send notifications if required. + if (!empty($userstonotify)) { + foreach ($userstonotify as $recipient) { + $allok = $allok && hvp_send_notification($recipient, $submitter, $a); + } + } + + // Send confirmation if required. We send the student confirmation last, so + // that if message sending is being intermittently buggy, which means we send + // some but not all messages, and then try again later, then teachers may get + // duplicate messages, but the student will always get exactly one. + if ($sendconfirm) { + $allok = $allok && hvp_send_confirmation($submitter, $a); + } + + return $allok; +} + +/** + * Callback for the attempt_submitted event. + * Sends out notification messages. + * + * @param $event + * + * @throws coding_exception + * @throws dml_exception + */ +function hvp_attempt_submitted_handler($event) { + global $DB, $PAGE; + $course = $DB->get_record('course', array('id' => $event->courseid)); + $cm = get_coursemodule_from_id('hvp', $event->get_context()->instanceid, $event->courseid); + $hvp = $DB->get_record('hvp', array('id' => $cm->instance)); + $attempt = (object) [ + 'userid' => $event->userid + ]; + $context = context_module::instance($cm->id); + $PAGE->set_context($context); + + hvp_send_notification_messages($course, $hvp, $attempt, $context, $cm); +} \ No newline at end of file diff --git a/version.php b/version.php index b4b69751dcedee61431601b5523b1b9bbbdad215..90d9ba2d7804730d68a2a20daa66d50e137dc2ab 100644 --- a/version.php +++ b/version.php @@ -23,7 +23,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2019111300; +$plugin->version = 2020013103; $plugin->requires = 2013051403; $plugin->cron = 0; $plugin->component = 'mod_hvp';