Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,23 @@ Once the Completion Progress block is installed, you can use it in a course as f
6. (Optional) Configure how the block should appear

Hidden items will not appear in the Completion Progress block until they are visible to students. This is useful for a scheduled release of activities.

## New features

- Overview table can show Matric Number and Email columns; these can be toggled per block.
- Reminder emails: lecturers can enable scheduled reminder emails for students below a configured progress threshold.
- Reminder frequency options: daily, weekly, monthly, or yearly.
- Emails include the student's current completion percentage and a link to the course.

### Configuration details

Block settings (per instance):
- Overview columns: toggle Matric Number and Email columns in the Overview table.
- Reminder emails: enable/disable scheduled reminder emails for students.
- Send reminder emails: choose daily, weekly, monthly, or yearly frequency.
- Send when progress is below (%): integer threshold from 0 to 100; students with progress below this value are emailed.

Operational notes:
- Reminder emails are sent by Moodle scheduled tasks (cron).
- Reminders respect block group restrictions and only target users who can view the block (student role).
- If no emails are sent in a run, the last-sent timestamp is not updated, so the next run can try again.
22 changes: 22 additions & 0 deletions block_completion_progress.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,28 @@ public function specialization() {
}
}

/**
* Normalize reminder settings when saving the block config.
*
* @param stdClass $data
* @param bool $nolongerused
*/
public function instance_config_save($data, $nolongerused = false) {
$current = $this->config ?? (object)[];
$enabled = !empty($data->reminderenabled);
$wasenabled = !empty($current->reminderenabled);
$frequency = $data->reminderfrequency ?? null;
$threshold = $data->reminderthreshold ?? null;
$prevfrequency = $current->reminderfrequency ?? null;
$prevthreshold = $current->reminderthreshold ?? null;

if (!$enabled || !$wasenabled || $frequency !== $prevfrequency || $threshold !== $prevthreshold) {
$data->reminderlastsent = 0;
}

parent::instance_config_save($data, $nolongerused);
}

/**
* Controls whether multiple instances of the block are allowed on a page
*
Expand Down
15 changes: 15 additions & 0 deletions classes/defaults.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,19 @@ abstract class defaults {
* Default number of seconds to cache completion percentages for overview pages.
*/
const OVERVIEWCACHETIME = 3600;

/**
* Default reminder email enabled state.
*/
const REMINDERENABLED = 0;

/**
* Default reminder threshold percentage.
*/
const REMINDERTHRESHOLD = 100;

/**
* Default reminder frequency.
*/
const REMINDERFREQUENCY = 'weekly';
}
48 changes: 47 additions & 1 deletion classes/table/overview.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ public function __construct(completion_progress $progress, $groups, $roleid, $bu

parent::__construct('block-completion_progress-overview');

$blockconfig = $this->progress->get_block_config();
$showidnumber = !isset($blockconfig->showidnumber) || (int)$blockconfig->showidnumber === 1;
$showemail = !isset($blockconfig->showemail) || (int)$blockconfig->showemail === 1;

$tablecolumns = [];
$tableheaders = [];

Expand All @@ -95,6 +99,14 @@ public function __construct(completion_progress $progress, $groups, $roleid, $bu

$tablecolumns[] = 'fullname';
$tableheaders[] = get_string('fullname');
if ($showidnumber) {
$tablecolumns[] = 'idnumber';
$tableheaders[] = get_string('matricnumber', 'block_completion_progress');
}
if ($showemail) {
$tablecolumns[] = 'email';
$tableheaders[] = get_string('email');
}

if (get_config('block_completion_progress', 'showlastincourse') != 0) {
$tablecolumns[] = 'timeaccess';
Expand All @@ -118,6 +130,12 @@ public function __construct(completion_progress $progress, $groups, $roleid, $bu

$this->set_attribute('class', 'overviewTable');
$this->column_class('fullname', 'col-fullname');
if ($showidnumber) {
$this->column_class('idnumber', 'col-idnumber');
}
if ($showemail) {
$this->column_class('email', 'col-email');
}
$this->column_class('timeaccess', 'col-timeaccess');
$this->column_class('progressbar', 'col-progressbar');
$this->column_class('progress', 'col-progress');
Expand Down Expand Up @@ -151,7 +169,7 @@ public function __construct(completion_progress $progress, $groups, $roleid, $bu
$params['cachemin'] = time() - $cachetime;
$params['bi'] = $this->progress->get_block_instance()->id;
$this->set_sql(
"DISTINCT $picturefields, l.timeaccess, b.percentage AS progress, b.timemodified AS progressage",
"DISTINCT $picturefields, u.idnumber, u.email, l.timeaccess, b.percentage AS progress, b.timemodified AS progressage",
"{user} u {$enroljoin->joins} {$rolejoin} " .
"LEFT JOIN {user_lastaccess} l ON l.userid = u.id AND l.courseid = :courseid " .
"LEFT JOIN {block_completion_progress} b ON b.userid = u.id AND " .
Expand Down Expand Up @@ -224,6 +242,34 @@ public function col_fullname($row) {
}
}

/**
* Format the user idnumber.
* @param object $row
* @return string
*/
public function col_idnumber($row) {
if ($this->is_downloading()) {
return $row->idnumber;
}
return s($row->idnumber);
}

/**
* Format the email address.
* @param object $row
* @return string
*/
public function col_email($row) {
if ($this->is_downloading()) {
return $row->email;
}
if (!$row->email) {
return '';
}
$email = s($row->email);
return \html_writer::link('mailto:' . $email, $email);
}

/**
* Format the time last accessed value.
* @param object $row
Expand Down
Loading