$string['duedatevalidation'] = 'Due date must be after the allow submissions from date.';
$string['editsubmission'] = 'Edit my submission';
$string['editaction'] = 'Actions...';
-$string['emailgradermail'] = '{$a->username} has updated their assignment submission
+$string['gradersubmissionupdatedtext'] = '{$a->username} has updated their assignment submission
for \'{$a->assignment}\' at {$a->timeupdated}
It is available here:
{$a->url}';
-$string['emailgradermailhtml'] = '{$a->username} has updated their assignment submission
+$string['gradersubmissionupdatedhtml'] = '{$a->username} has updated their assignment submission
for <i>\'{$a->assignment}\' at {$a->timeupdated}</i><br /><br />
It is <a href="{$a->url}">available on the web site</a>.';
+$string['gradersubmissionupdatedsmall'] = '{$a->username} has updated their submission for assignment {$a->assignment}.';
$string['enabled'] = 'Enabled';
$string['errornosubmissions'] = 'There are no submissions to download';
$string['errorquickgradingvsadvancedgrading'] = 'The grades were not saved because this assignment is currently using advanced grading';
$string['errorrecordmodified'] = 'The grades were not saved because someone has modified one or more records more recently than when you loaded the page.';
-$string['feedbackcomments'] = 'Feedback comments';
$string['feedback'] = 'Feedback';
+$string['feedbackavailabletext'] = '{$a->username} has posted some feedback on your
+assignment submission for \'{$a->assignment}\'
+
+You can see it appended to your assignment submission:
+
+ {$a->url}';
+$string['feedbackavailablehtml'] = '{$a->username} has posted some feedback on your
+assignment submission for \'<i>{$a->assignment}</i>\'<br /><br />
+You can see it appended to your <a href="{$a->url}">assignment submission</a>.';
+$string['feedbackavailablesmall'] = '{$a->username} has given feedback for assignment {$a->assignment}';
$string['feedbackplugins'] = 'Feedback plugins';
$string['feedbackpluginforgradebook'] = 'Feedback plugin that will push comments to the gradebook';
+$string['feedbackpluginforgradebook_help'] = 'Only one assignment feedback plugin can push feedback into the gradebook.';
$string['feedbackplugin'] = 'Feedback plugin';
$string['feedbacksettings'] = 'Feedback settings';
$string['filesubmissions'] = 'File submissions';
$string['locksubmissions'] = 'Lock submissions';
$string['manageassignfeedbackplugins'] = 'Manage assignment feedback plugins';
$string['manageassignsubmissionplugins'] = 'Manage assignment submission plugins';
-$string['messageprovider:assign_updates'] = 'Assignment notifications';
+$string['messageprovider:assign_student_notification'] = 'Assignment student notifications';
+$string['messageprovider:assign_grader_notification'] = 'Assignment grader notifications';
$string['modulename'] = 'Assignment';
$string['modulename_help'] = 'The assignment activity module enables a teacher to assess students’ learning by setting work and then reviewing it and providing feedback and grades.
$string['notsubmittedyet'] = 'Not submitted yet';
$string['notifications'] = 'Notifications';
$string['nousersselected'] = 'No users selected';
+$string['notification'] = 'Notification';
$string['numberofdraftsubmissions'] = 'Drafts';
$string['numberofparticipants'] = 'Participants';
$string['numberofsubmittedassignments'] = 'Submitted';
$string['savechanges'] = 'Save changes';
$string['saveallchanges'] = 'Save all changes';
$string['savenext'] = 'Save and show next';
-$string['sendnotifications'] = 'Send notifications to graders';
+$string['sendnotifications'] = 'Notify graders about submissions';
$string['sendnotifications_help'] = 'If enabled, graders (usually teachers) receive a message whenever a student submits an assignment, early, on time and late. Message methods are configurable.';
$string['selectlink'] = 'Select...';
+$string['sendlatenotifications'] = 'Notify graders about late submissions';
+$string['sendlatenotifications_help'] = 'If enabled, graders (usually teachers) receive a message whenever a student submits an assignment late. Message methods are configurable.';
+$string['sendsubmissionreceipts'] = 'Send submission receipt to students';
+$string['sendsubmissionreceipts_help'] = 'This switch will enable submission receipts for students. Students will receive a notification every time they successfully submit an assignment';
$string['settings'] = 'Assignment settings';
$string['showrecentsubmissions'] = 'Show recent submissions';
$string['submissiondrafts'] = 'Require students click submit button';
$string['submissionnotready'] = 'This assignment is not ready to submit:';
$string['submissionplugins'] = 'Submission plugins';
$string['submissionreceipts'] = 'Send submission receipts';
+$string['submissionreceipttext'] = 'You have submitted an
+assignment submission for \'{$a->assignment}\'
+
+You can see the status of your assignment submission:
+
+ {$a->url}';
+$string['submissionreceipthtml'] = 'You have submitted an
+assignment submission for \'<i>{$a->assignment}</i>\'<br /><br />
+You can the status of your <a href="{$a->url}">assignment submission</a>.';
+$string['submissionreceiptsmall'] = 'You have submitted your assignment submission for {$a->assignment}';
$string['submissionslocked'] = 'This assignment is not accepting submissions';
$string['submissionslockedshort'] = 'Submission changes not allowed';
$string['submissions'] = 'Submissions';
$update->preventlatesubmissions = $formdata->preventlatesubmissions;
$update->submissiondrafts = $formdata->submissiondrafts;
$update->sendnotifications = $formdata->sendnotifications;
+ $update->sendlatenotifications = $formdata->sendlatenotifications;
$update->duedate = $formdata->duedate;
$update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
$update->grade = $formdata->grade;
$update->preventlatesubmissions = $formdata->preventlatesubmissions;
$update->submissiondrafts = $formdata->submissiondrafts;
$update->sendnotifications = $formdata->sendnotifications;
+ $update->sendlatenotifications = $formdata->sendlatenotifications;
$update->duedate = $formdata->duedate;
$update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
$update->grade = $formdata->grade;
return false;
}
-
/**
* Cron function to be run periodically according to the moodle cron
* Finds all assignment notifications that have yet to be mailed out, and mails them
* @return bool
*/
static function cron() {
- global $CFG, $USER, $DB;
+ global $CFG, $DB;
+
+ // used to cache DB lookups
+ $croncache = array();
+
+ // only ever send a max of one days worth of updates
+ $yesterday = time() - (24 * 3600);
+ $timenow = time();
+
+ // email students about new feedback
+ $submissions = $DB->get_records_sql("SELECT s.*, a.course, a.name, g.*, g.id as gradeid, g.timemodified as lastmodified
+ FROM {assign} a
+ JOIN {assign_grades} g ON g.assignment = a.id
+ LEFT JOIN {assign_submission} s ON s.assignment = a.id AND s.userid = g.userid
+ WHERE g.timemodified >= :yesterday
+ AND g.timemodified <= :today
+ AND g.mailed = 0", array('yesterday'=>$yesterday, 'today'=>$timenow));
+
+ mtrace('Processing ' . count($submissions) . ' assignment submissions ...');
+
+ foreach ($submissions as $submission) {
+
+ mtrace("Processing assignment submission $submission->id ...");
+
+ // do not cache user lookups - could be too many
+ if (! $user = $DB->get_record("user", array("id"=>$submission->userid))) {
+ mtrace("Could not find user $submission->userid");
+ continue;
+ }
+
+ // use a cache to prevent the same DB queries happening over and over
+ if (! $course = array_search('course_' . $submission->course, $croncache)) {
+ if (! $course = $DB->get_record("course", array("id"=>$submission->course))) {
+ mtrace("Could not find course $submission->course");
+ continue;
+ }
+ $croncache['course_' . $submission->course] = $course;
+ }
+
+ /// Override the language and timezone of the "current" user, so that
+ /// mail is customised for the receiver.
+ cron_setup_user($user, $course);
+
+ // context lookups are already cached
+ $coursecontext = context_course::instance($submission->course);
+ $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext));
+ if (!is_enrolled($coursecontext, $user->id)) {
+ mtrace(fullname($user)." not an active participant in " . $courseshortname);
+ continue;
+ }
+
+ if (! $grader = $DB->get_record("user", array("id"=>$submission->grader))) {
+ mtrace("Could not find grader $submission->grader");
+ continue;
+ }
+ if (! $mod = array_search('mod_' . $submission->assignment, $croncache)) {
+ if (! $mod = get_coursemodule_from_instance("assign", $submission->assignment, $course->id)) {
+ mtrace("Could not find course module for assignment id $submission->assignment");
+ continue;
+ }
+ $croncache['mod_' . $submission->assignment] = $mod;
+ }
+
+ // context lookups are already cached
+ $contextmodule = context_module::instance($mod->id);
+
+ if (! $mod->visible) { /// Hold mail notification for hidden assignments until later
+ continue;
+ }
+
+ // need to send this to the student
+ self::send_assignment_notification($grader, $user, 'feedbackavailable', 'assign_student_notification', $submission->lastmodified, $mod, $contextmodule, $course, get_string('modulename', 'assign'), $submission->name);
+
+ $grade = new stdClass();
+ $grade->id = $submission->gradeid;
+ $grade->mailed = 1;
+ $DB->update_record('assign_grades', $grade);
+
+ mtrace('Done');
+ }
+ mtrace('Done processing ' . count($submissions) . ' assignment submissions');
+
+ cron_setup_user();
+ // free up memory
+ unset($croncache);
+
return true;
}
/**
* Returns a list of teachers that should be grading given submission
*
- * @param stdClass $user
+ * @param int $userid
* @return array
*/
- private function get_graders(stdClass $user) {
+ private function get_graders($userid) {
//potential graders
- $potentialgraders = get_users_by_capability($this->context, 'mod/assign:grade', '', '', '', '', '', '', false, false);
+ $potentialgraders = get_enrolled_users($this->context, "mod/assign:grade");
$graders = array();
if (groups_get_activity_groupmode($this->get_course_module()) == SEPARATEGROUPS) { // Separate groups are being used
- if ($groups = groups_get_all_groups($this->get_course()->id, $user->id)) { // Try to find all groups
+ if ($groups = groups_get_all_groups($this->get_course()->id, $userid)) { // Try to find all groups
foreach ($groups as $group) {
foreach ($potentialgraders as $grader) {
- if ($grader->id == $user->id) {
+ if ($grader->id == $userid) {
continue; // do not send self
}
if (groups_is_member($group->id, $grader->id)) {
} else {
// user not in group, try to find graders without group
foreach ($potentialgraders as $grader) {
- if ($grader->id == $user->id) {
+ if ($grader->id == $userid) {
continue; // do not send self
}
if (!groups_has_membership($this->get_course_module(), $grader->id)) {
}
} else {
foreach ($potentialgraders as $grader) {
- if ($grader->id == $user->id) {
+ if ($grader->id == $userid) {
continue; // do not send self
}
// must be enrolled
}
/**
- * Creates the text content for emails to grader
+ * Format a notification based on it's type
*
- * @param array $info The info used by the 'emailgradermail' language string
- * @return string
+ * @param string $messagetype
+ * @param stdClass $info
+ * @param stdClass $course
+ * @param stdClass $context
+ * @param string $modulename
+ * @param string $assignmentname
*/
- private function format_email_grader_text($info) {
- $posttext = format_string($this->get_course()->shortname, true, array('context' => $this->get_course_context())).' -> '.
- $this->get_module_name().' -> '.
- format_string($this->get_instance()->name, true, array('context' => $this->context))."\n";
+ private static function format_notification_message_text($messagetype, $info, $course, $context, $modulename, $assignmentname) {
+ $posttext = format_string($course->shortname, true, array('context' => $context->get_course_context())).' -> '.
+ $modulename.' -> '.
+ format_string($assignmentname, true, array('context' => $context))."\n";
$posttext .= '---------------------------------------------------------------------'."\n";
- $posttext .= get_string("emailgradermail", "assign", $info)."\n";
+ $posttext .= get_string($messagetype . 'text', "assign", $info)."\n";
$posttext .= "\n---------------------------------------------------------------------\n";
return $posttext;
}
- /**
- * Creates the html content for emails to graders
+ /**
+ * Format a notification based on it's type
*
- * @param array $info The info used by the 'emailgradermailhtml' language string
- * @return string
+ * @param string $messagetype
+ * @param stdClass $info
*/
- private function format_email_grader_html($info) {
+ private static function format_notification_message_html($messagetype, $info, $course, $context, $modulename, $coursemodule, $assignmentname) {
global $CFG;
$posthtml = '<p><font face="sans-serif">'.
- '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$this->get_course()->id.'">'.format_string($this->get_course()->shortname, true, array('context' => $this->get_course_context())).'</a> ->'.
- '<a href="'.$CFG->wwwroot.'/mod/assignment/index.php?id='.$this->get_course()->id.'">'.$this->get_module_name().'</a> ->'.
- '<a href="'.$CFG->wwwroot.'/mod/assignment/view.php?id='.$this->get_course_module()->id.'">'.format_string($this->get_instance()->name, true, array('context' => $this->context)).'</a></font></p>';
+ '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.format_string($course->shortname, true, array('context' => $context->get_course_context())).'</a> ->'.
+ '<a href="'.$CFG->wwwroot.'/mod/assignment/index.php?id='.$course->id.'">'.$modulename.'</a> ->'.
+ '<a href="'.$CFG->wwwroot.'/mod/assignment/view.php?id='.$coursemodule->id.'">'.format_string($assignmentname, true, array('context' => $context)).'</a></font></p>';
$posthtml .= '<hr /><font face="sans-serif">';
- $posthtml .= '<p>'.get_string('emailgradermailhtml', 'assign', $info).'</p>';
+ $posthtml .= '<p>'.get_string($messagetype . 'html', 'assign', $info).'</p>';
$posthtml .= '</font><hr />';
return $posthtml;
}
+ /**
+ * email someone about something (static so it can be called from cron)
+ *
+ * @global stdClass $CFG
+ * @global moodle_database $DB
+ * @param stdClass $userfrom
+ * @param stdClass $userto
+ * @param string $messagetype
+ * @param string $eventtype
+ * @param int $updatetime
+ * @param stdClass $coursemodule
+ * @param stdClass $context
+ * @param stdClass $course
+ * @param string $modulename
+ * @param string $moduleplural
+ * @param string $assignmentname
+ * @return void
+ */
+ public static function send_assignment_notification($userfrom, $userto, $messagetype, $eventtype,
+ $updatetime, $coursemodule, $context, $course,
+ $modulename, $assignmentname) {
+ global $CFG, $DB;
+
+ $strnotification = get_string('notification', 'assign');
+
+ $info = new stdClass();
+ $info->username = fullname($userfrom, true);
+ $info->assignment = format_string($assignmentname,true, array('context'=>$context));
+ $info->url = $CFG->wwwroot.'/mod/assign/view.php?id='.$coursemodule->id;
+ $info->timeupdated = strftime('%c',$updatetime);
+
+ $postsubject = get_string($messagetype . 'small', 'assign', $info);
+ $posttext = self::format_notification_message_text($messagetype, $info, $course, $context, $modulename, $assignmentname);
+ $posthtml = ($userto->mailformat == 1) ? self::format_notification_message_html($messagetype, $info, $course, $context, $modulename, $coursemodule, $assignmentname) : '';
+
+ $eventdata = new stdClass();
+ $eventdata->modulename = 'assign';
+ $eventdata->userfrom = $userfrom;
+ $eventdata->userto = $userto;
+ $eventdata->subject = $postsubject;
+ $eventdata->fullmessage = $posttext;
+ $eventdata->fullmessageformat = FORMAT_PLAIN;
+ $eventdata->fullmessagehtml = $posthtml;
+ $eventdata->smallmessage = $postsubject;
+
+ $eventdata->name = $eventtype;
+ $eventdata->component = 'mod_assign';
+ $eventdata->notification = 1;
+ $eventdata->contexturl = $info->url;
+ $eventdata->contexturlname = $info->assignment;
+
+ message_send($eventdata);
+
+ }
+
+ /**
+ * email someone about something
+ *
+ * @global stdClass $CFG
+ * @global moodle_database $DB
+ * @param stdClass $submission
+ * @return void
+ */
+ public function send_notification($userfrom, $userto, $messagetype, $eventtype, $updatetime) {
+ self::send_assignment_notification($userfrom, $userto, $messagetype, $eventtype, $updatetime, $this->get_course_module(), $this->get_context(), $this->get_course(), $this->get_module_name(), $this->get_instance()->name);
+ }
+
+ /**
+ * email student upon successful submission
+ *
+ * @global stdClass $CFG
+ * @global moodle_database $DB
+ * @param stdClass $submission
+ * @return void
+ */
+ private function email_student_submission_receipt(stdClass $submission) {
+ global $DB;
+
+ $adminconfig = $this->get_admin_config();
+ if (!$adminconfig->submissionreceipts) { // No need to do anything
+ return;
+ }
+
+ $user = $DB->get_record('user', array('id'=>$submission->userid), '*', MUST_EXIST);
+
+ $this->send_notification($user, $user, 'submissionreceipt', 'assign_student_notification', $submission->timemodified);
+ }
+
/**
* email graders upon student submissions
*
+ * @global stdClass $CFG
+ * @global moodle_database $DB
* @param stdClass $submission
* @return void
*/
private function email_graders(stdClass $submission) {
- global $CFG, $DB;
+ global $DB;
+
+ $late = $this->get_instance()->duedate && ($this->get_instance()->duedate < time());
- if (empty($this->get_instance()->sendnotifications)) { // No need to do anything
+ if (!$this->get_instance()->sendnotifications && !($late && $this->get_instance()->sendlatenotifications)) { // No need to do anything
return;
}
$user = $DB->get_record('user', array('id'=>$submission->userid), '*', MUST_EXIST);
- if ($teachers = $this->get_graders($user)) {
+ if ($teachers = $this->get_graders($user->id)) {
$strassignments = $this->get_module_name_plural();
$strassignment = $this->get_module_name();
$strsubmitted = get_string('submitted', 'assign');
foreach ($teachers as $teacher) {
- $info = new stdClass();
- $info->username = fullname($user, true);
- $info->assignment = format_string($this->get_instance()->name,true, array('context'=>$this->get_context()));
- $info->url = $CFG->wwwroot.'/mod/assign/view.php?id='.$this->get_course_module()->id;
- $info->timeupdated = strftime('%c',$submission->timemodified);
-
- $postsubject = $strsubmitted.': '.$info->username.' -> '.$this->get_instance()->name;
- $posttext = $this->format_email_grader_text($info);
- $posthtml = ($teacher->mailformat == 1) ? $this->format_email_grader_html($info) : '';
-
- $eventdata = new stdClass();
- $eventdata->modulename = 'assign';
- $eventdata->userfrom = $user;
- $eventdata->userto = $teacher;
- $eventdata->subject = $postsubject;
- $eventdata->fullmessage = $posttext;
- $eventdata->fullmessageformat = FORMAT_PLAIN;
- $eventdata->fullmessagehtml = $posthtml;
- $eventdata->smallmessage = $postsubject;
-
- $eventdata->name = 'assign_updates';
- $eventdata->component = 'mod_assign';
- $eventdata->notification = 1;
- $eventdata->contexturl = $info->url;
- $eventdata->contexturlname = $info->assignment;
-
- message_send($eventdata);
+ $this->send_notification($user, $teacher, 'gradersubmissionupdated', 'assign_grader_notification', $submission->timemodified);
}
}
}
$this->update_submission($submission);
$this->add_to_log('submit for grading', $this->format_submission_for_log($submission));
$this->email_graders($submission);
+ $this->email_student_submission_receipt($submission);
}
}
$this->add_to_log('submit', $this->format_submission_for_log($submission));
if (!$this->get_instance()->submissiondrafts) {
+ $this->email_student_submission_receipt($submission);
$this->email_graders($submission);
}
return true;
}
}
$this->process_outcomes($userid, $formdata);
+
+ $grade->mailed = 0;
+
$this->update_grade($grade);
$user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);