Skip to content

Commit

Permalink
MDL-35843 refactor expiry notifications to core enrol feature
Browse files Browse the repository at this point in the history
  • Loading branch information
skodak committed Oct 19, 2012
1 parent 8e94120 commit 8c04252
Show file tree
Hide file tree
Showing 3 changed files with 271 additions and 233 deletions.
243 changes: 12 additions & 231 deletions enrol/manual/lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
class enrol_manual_plugin extends enrol_plugin {

protected $lasternoller = null;
protected $lasternollercourseid = 0;
protected $lasternollerinstanceid = 0;

public function roles_protected() {
// Users may tweak the roles later.
Expand Down Expand Up @@ -376,257 +376,38 @@ public function sync($courseid = null, $verbose = false) {
}

/**
* Send notifications.
*
* @param bool $verbose verbose CLI output
*/
public function send_expiry_notifications($verbose = false) {
global $DB, $CFG;

// Unfortunately this may take a long time, it should not be interrupted,
// otherwise users get duplicate notification.

@set_time_limit(0);
raise_memory_limit(MEMORY_HUGE);

$expirynotifylast = $this->get_config('expirynotifylast', 0);
$expirynotifyhour = $this->get_config('expirynotifyhour', 6);
$timenow = time();

$notifytime = usergetmidnight($timenow, $CFG->timezone) + ($expirynotifyhour * 3600);

if ($expirynotifylast > $notifytime) {
if ($verbose) {
mtrace('Manual enrolment notifications were already sent today at '.userdate($expirynotifylast, '', $CFG->timezone).'.');
}
return;
} else if ($timenow < $notifytime) {
if ($verbose) {
mtrace('Manual enrolment notifications will be sent at '.userdate($notifytime, '', $CFG->timezone).'.');
}
return;
}

if ($verbose) {
mtrace('Processing manual enrolment notifications...');
}

// Notify users responsible for enrolment once every day.
$sql = "SELECT ue.*, e.expirynotify, e.notifyall, e.expirythreshold, e.courseid, c.fullname
FROM {user_enrolments} ue
JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual' AND e.expirynotify > 0 AND e.status = :enabled)
JOIN {course} c ON (c.id = e.courseid)
JOIN {user} u ON (u.id = ue.userid AND u.deleted = 0 AND u.suspended = 0)
WHERE ue.status = :active AND ue.timeend > 0 AND ue.timeend > :now1 AND ue.timeend < (e.expirythreshold + :now2)
ORDER BY ue.enrolid ASC, u.lastname ASC, u.firstname ASC, u.id ASC";
$params = array('enabled'=>ENROL_INSTANCE_ENABLED, 'active'=>ENROL_USER_ACTIVE, 'now1'=>$timenow, 'now2'=>$timenow);

$rs = $DB->get_recordset_sql($sql, $params);

$lastenrollid = 0;
$users = array();

foreach($rs as $ue) {
if ($lastenrollid and $lastenrollid != $ue->enrolid) {
$this->notify_expiry_enroller($lastenrollid, $users, $verbose);
$users = array();
}
$lastenrollid = $ue->enrolid;

$enroller = $this->get_enroller($ue->courseid);
$context = context_course::instance($ue->courseid);

$user = $DB->get_record('user', array('id'=>$ue->userid));

$users[] = array('fullname'=>fullname($user, has_capability('moodle/site:viewfullnames', $context, $enroller)), 'timeend'=>$ue->timeend);

if (!$ue->notifyall) {
continue;
}

if ($ue->timeend - $ue->expirythreshold + 86400 < $timenow) {
// Notify enrolled users only once at the start of the threshold.
if ($verbose) {
mtrace(" user $ue->userid was already notified that enrolment in course $ue->courseid expires on ".userdate($ue->timeend, '', $CFG->timezone));
}
continue;
}

$this->notify_expiry_enrolled($user, $ue, $verbose);
}
$rs->close();

if ($lastenrollid and $users) {
$this->notify_expiry_enroller($lastenrollid, $users, $verbose);
}

if ($verbose) {
mtrace('...notification processing finished.');
}
$this->set_config('expirynotifylast', $timenow);
}

/**
* Returns the user who is responsible for manual enrolments in given course.
* Returns the user who is responsible for manual enrolments in given instance.
*
* Usually it is the first editing teacher - the person with "highest authority"
* as defined by sort_by_roleassignment_authority() having 'enrol/manual:manage'
* capability.
*
* @param int $courseid
* @param int $instanceid enrolment instance id
* @return stdClass user record
*/
protected function get_enroller($courseid) {
if ($this->lasternollercourseid == $courseid and $this->lasternoller) {
protected function get_enroller($instanceid) {
global $DB;

if ($this->lasternollerinstanceid == $instanceid and $this->lasternoller) {
return $this->lasternoller;
}

$context = context_course::instance($courseid);
$instance = $DB->get_record('enrol', array('id'=>$instanceid, 'enrol'=>$this->get_name()), '*', MUST_EXIST);
$context = context_course::instance($instance->courseid);

if ($users = get_enrolled_users($context, 'enrol/manual:manage')) {
$users = sort_by_roleassignment_authority($users, $context);
$this->lasternoller = reset($users);
unset($users);
} else {
$this->lasternoller = get_admin();
$this->lasternoller = parent::get_enroller($instanceid);
}

$this->lasternollercourseid = $courseid;
$this->lasternollerinstanceid = $instanceid;

return $this->lasternoller;
}

/**
* Notify user about incoming expiration of their enrolment,
* it is called only if notification of enrolled users (aka students) is enabled in course.
*
* This is executed only once for each expiring enrolment right
* at the start of the expiration threshold.
*
* @param stdClass $user
* @param stdClass $ue
* @param bool $verbose
*/
protected function notify_expiry_enrolled($user, $ue, $verbose) {
global $CFG, $SESSION;

// Some nasty hackery to get strings and dates localised for target user.
$sessionlang = isset($SESSION->lang) ? $SESSION->lang : null;
if (get_string_manager()->translation_exists($user->lang, false)) {
$SESSION->lang = $user->lang;
moodle_setlocale();
}

$enroller = $this->get_enroller($ue->courseid);
$context = context_course::instance($ue->courseid);

$subject = get_string('expirymessageenrolledsubject', 'enrol_manual', null);
$a = new stdClass();
$a->course = format_string($ue->fullname, true, array('context'=>$context));
$a->user = fullname($user, true);
$a->timeend = userdate($ue->timeend, '', $user->timezone);
$a->enroller = fullname($enroller, has_capability('moodle/site:viewfullnames', $context, $user));
$body = get_string('expirymessageenrolledbody', 'enrol_manual', $a);

$message = new stdClass();
$message->notification = 1;
$message->component = 'enrol_manual';
$message->name = 'expiry_notification';
$message->userfrom = $enroller;
$message->userto = $user;
$message->subject = $subject;
$message->fullmessage = $body;
$message->fullmessageformat = FORMAT_MARKDOWN;
$message->fullmessagehtml = markdown_to_html($body);
$message->smallmessage = $subject;
$message->contexturlname = $a->course;
$message->contexturl = (string)new moodle_url('/course/view.php', array('id'=>$ue->courseid));

if (message_send($message)) {
if ($verbose) {
mtrace(" notifying user $ue->userid that enrolment in course $ue->courseid expires on ".userdate($ue->timeend, '', $CFG->timezone));
}
} else {
if ($verbose) {
mtrace(" error notifying user $ue->userid that enrolment in course $ue->courseid expires on ".userdate($ue->timeend, '', $CFG->timezone));
}
}

if ($SESSION->lang !== $sessionlang) {
$SESSION->lang = $sessionlang;
moodle_setlocale();
}
}

/**
* Notify person responsible for enrolments that some user enrolments will be expired soon,
* it is called only if notification of enrollers (aka teachers) is enabled in course.
*
* This is called repeatedly every day for each course if there are any pending expiration
* in the expiration threshold.
*
* @param int $eid
* @param array $users
* @param bool $verbose
*/
protected function notify_expiry_enroller($eid, $users, $verbose) {
global $DB, $SESSION;

$instance = $DB->get_record('enrol', array('id'=>$eid, 'enrol'=>'manual'));
$context = context_course::instance($instance->courseid);
$course = $DB->get_record('course', array('id'=>$instance->courseid));

$enroller = $this->get_enroller($instance->courseid);
$admin = get_admin();

// Some nasty hackery to get strings and dates localised for target user.
$sessionlang = isset($SESSION->lang) ? $SESSION->lang : null;
if (get_string_manager()->translation_exists($enroller->lang, false)) {
$SESSION->lang = $enroller->lang;
moodle_setlocale();
}

foreach($users as $key=>$info) {
$users[$key] = '* '.$info['fullname'].' - '.userdate($info['timeend'], '', $enroller->timezone);
}

$subject = get_string('expirymessageenrollersubject', 'enrol_manual', null);
$a = new stdClass();
$a->course = format_string($course->fullname, true, array('context'=>$context));
$a->threshold = get_string('numdays', '', $instance->expirythreshold / (60*60*24));
$a->users = implode("\n", $users);
$a->extendurl = (string)new moodle_url('/enrol/users.php', array('id'=>$instance->courseid));
$body = get_string('expirymessageenrollerbody', 'enrol_manual', $a);

$message = new stdClass();
$message->notification = 1;
$message->component = 'enrol_manual';
$message->name = 'expiry_notification';
$message->userfrom = $admin;
$message->userto = $enroller;
$message->subject = $subject;
$message->fullmessage = $body;
$message->fullmessageformat = FORMAT_MARKDOWN;
$message->fullmessagehtml = markdown_to_html($body);
$message->smallmessage = $subject;
$message->contexturlname = $a->course;
$message->contexturl = $a->extendurl;

if (message_send($message)) {
if ($verbose) {
mtrace(" notifying user $enroller->id about all expiring manual enrolments in course $instance->courseid");
}
} else {
if ($verbose) {
mtrace(" error notifying user $enroller->id about all expiring manual enrolments in course $instance->courseid");
}
}

if ($SESSION->lang !== $sessionlang) {
$SESSION->lang = $sessionlang;
moodle_setlocale();
}
}

/**
* Gets an array of the user enrolment actions.
*
Expand Down
5 changes: 3 additions & 2 deletions enrol/upgrade.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ required changes in code:
directly from database is not correct any more
* new restore support: ENROL_RESTORE_EXACT, ENROL_RESTORE_NOUSERS
and ENROL_RESTORE_CLASS were removed, implement new restore_* plugin methods instead
* new support for protected group membership linked to enrol instance

other changes:
other changes and new features:
* course enrolment manager now works with disabled plugins too
* new support for protected group membership linked to enrol instance
* new support for expiry notifications - see enrol_plugin::send_expiry_notifications() method


=== 2.2 ===
Expand Down

0 comments on commit 8c04252

Please sign in to comment.