From fee78147dffddecd110d1202f0a6853374a0ab7f Mon Sep 17 00:00:00 2001 From: Thomas Marstrander <marstranderthomas@gmail.com> Date: Fri, 31 Jan 2020 17:18:34 +0100 Subject: [PATCH] HFP-2876 Add notifications when attempt is submitted --- classes/event/attempt_submitted.php | 46 +++++++ classes/user_grades.php | 6 + db/access.php | 14 ++ db/events.php | 38 ++++++ db/messages.php | 40 ++++++ lang/en/hvp.php | 24 +++- locallib.php | 192 ++++++++++++++++++++++++++++ version.php | 2 +- 8 files changed, 360 insertions(+), 2 deletions(-) create mode 100644 classes/event/attempt_submitted.php create mode 100644 db/events.php create mode 100644 db/messages.php diff --git a/classes/event/attempt_submitted.php b/classes/event/attempt_submitted.php new file mode 100644 index 0000000..8392b24 --- /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 103ea6d..eca660a 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 8475711..5658c6e 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 0000000..c6f7983 --- /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 0000000..a08d8f0 --- /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 7eaa575..ce26356 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 72f7eee..f75e4d3 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 b4b6975..90d9ba2 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'; -- GitLab