Skip to content

Commit

Permalink
Merge pull request #129 from dmitriim/issue127
Browse files Browse the repository at this point in the history
issue #127: add support for mod_certificate
  • Loading branch information
danmarsden authored Nov 21, 2023
2 parents 965c7be + c71d350 commit e0d0152
Show file tree
Hide file tree
Showing 11 changed files with 598 additions and 15 deletions.
15 changes: 14 additions & 1 deletion backup/moodle2/backup_local_recompletion_plugin.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ protected function define_course_plugin_structure() {
}
$lessonoverride->annotate_ids('user', 'userid');

// Now deal with hvp archive tables.
// Now deal with hotpot archive tables.
$hotpotattempts = new backup_nested_element('hotpotattempts');
$hotpotattempt = new backup_nested_element('hotpotattempt', array('id'), array(
'hotpotid', 'userid', 'starttime', 'endtime', 'score', 'penalties', 'attempt', 'timestart',
Expand All @@ -260,6 +260,19 @@ protected function define_course_plugin_structure() {
}
$hotpotattempt->annotate_ids('user', 'userid');

// Now deal mod_certificate archive table.
$certificates = new backup_nested_element('certificates');
$certificate = new backup_nested_element('certificate', array('id'), array(
'userid', 'certificateid', 'code', 'timecreated', 'printdate', 'course'));

$recompletion->add_child($certificates);
$certificates->add_child($certificate);

if ($usercompletion) {
$certificate->set_source_table('local_recompletion_cert', array('course' => backup::VAR_COURSEID));
}
$certificate->annotate_ids('user', 'userid');

return $plugin;
}

Expand Down
23 changes: 23 additions & 0 deletions backup/moodle2/restore_local_recompletion_plugin.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ protected function define_course_plugin_structure() {
$paths[] = new restore_path_element('recompletion_lessonbrach', $elepath.'/lessonbraches/lessonbrach');
$paths[] = new restore_path_element('recompletion_lessonoverride', $elepath.'/lessonoverrides/lessonoverride');
$paths[] = new restore_path_element('recompletion_hpa', $elepath.'/hotpotattempts/hotpotattempt');
$paths[] = new restore_path_element('recompletion_cert', $elepath.'/certificates/certificate');

return $paths;
}
Expand Down Expand Up @@ -310,6 +311,20 @@ public function process_recompletion_hpa($data) {
$DB->insert_record('local_recompletion_hpa', $data);
}

/**
* Process local_recompletion_cert table.
* @param stdClass $data
*/
public function process_recompletion_cert($data) {
global $DB;

$data = (object) $data;
$data->course = $this->task->get_courseid();
$data->userid = $this->get_mappingid('user', $data->userid);

$DB->insert_record('local_recompletion_cert', $data);
}

/**
* We call the after restore_course to update the coursemodule ids we didn't know when creating.
*/
Expand Down Expand Up @@ -394,5 +409,13 @@ protected function after_restore_course() {
$DB->update_record('local_recompletion_hpa', $rc);
}
$rcm->close();

// Fix certificates.
$rcm = $DB->get_recordset('local_recompletion_cert', array('course' => $this->task->get_courseid()));
foreach ($rcm as $rc) {
$rc->certificateid = $this->get_mappingid('certificate', $rc->certificateid);
$DB->update_record('local_recompletion_cert', $rc);
}
$rcm->close();
}
}
213 changes: 213 additions & 0 deletions classes/plugins/mod_certificate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
<?php
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.

namespace local_recompletion\plugins;

use stdClass;
use lang_string;
use admin_setting_configselect;
use admin_setting_configcheckbox;
use admin_settingpage;
use core\output\notification;
use MoodleQuickForm;

defined('MOODLE_INTERNAL') || die;

require_once($CFG->dirroot.'/local/recompletion/locallib.php');

/**
* Certificate handler event.
*
* @package local_recompletion
* @author 2023 Dmitrii Metelkin
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_certificate {

/**
* Add params to form.
*
* @param MoodleQuickForm $mform
*/
public static function editingform(MoodleQuickForm $mform): void {
global $OUTPUT;

if (!self::installed()) {
return;
}
$config = get_config('local_recompletion');

$cba = [];
$cba[] = $mform->createElement('radio', 'certificate', '',
get_string('donothing', 'local_recompletion'), LOCAL_RECOMPLETION_NOTHING);
$cba[] = $mform->createElement('radio', 'certificate', '',
get_string('deletecertificate', 'local_recompletion'), LOCAL_RECOMPLETION_DELETE);

$mform->addGroup($cba, 'certificate', get_string('certificate', 'local_recompletion'), [' '], false);
$mform->addHelpButton('certificate', 'certificate', 'local_recompletion');
$mform->setDefault('certificate', $config->certificate);

$mform->addElement('checkbox', 'archivecertificate',
get_string('archivecertificate', 'local_recompletion'));
$mform->setDefault('archivecertificate', $config->archivecertificate);

$verifywarngroup = [];
$verifywarn = new notification(
get_string('certificateverifywarn', 'local_recompletion'),
notification::NOTIFY_WARNING);
$verifywarn->set_show_closebutton(false);
$verifywarngroup[] =
$mform->createElement('static', 'certificateverifywarn', '', $OUTPUT->render($verifywarn));
$mform->addGroup($verifywarngroup, 'certificateverifywarngroup', '', ' ', false);

$mform->disabledIf('certificate', 'enable');
$mform->disabledIf('archivecertificate', 'enable');
$mform->hideIf('archivecertificate', 'certificate', 'noteq', LOCAL_RECOMPLETION_DELETE);
$mform->hideIf('certificateverifywarn', 'certificate', 'noteq', LOCAL_RECOMPLETION_DELETE);
}

/**
* Add site level settings for this plugin.
*
* @param admin_settingpage $settings
*/
public static function settings(admin_settingpage $settings) {

if (!self::installed()) {
return;
}

$choices = [
LOCAL_RECOMPLETION_NOTHING => get_string('donothing', 'local_recompletion'),
LOCAL_RECOMPLETION_DELETE => get_string('customcertresetcertificates', 'local_recompletion')
];

$settings->add(new admin_setting_configselect('local_recompletion/certificate',
new lang_string('certificate', 'local_recompletion'),
new lang_string('certificate_help', 'local_recompletion'), LOCAL_RECOMPLETION_NOTHING, $choices));

$settings->add(new admin_setting_configcheckbox('local_recompletion/archivecertificate',
new lang_string('archivecertificate', 'local_recompletion'),
new lang_string('archivecertificate_help', 'local_recompletion'), 1));
}

/**
* Reset records.
*
* @param int $userid - user id
* @param stdClass $course - course record.
* @param stdClass $config - recompletion config.
*/
public static function reset(int $userid, stdClass $course, stdClass $config): void {
global $DB;

if (!self::installed()) {
return;
}

if (empty($config->certificate)) {
return;
}

if ($config->certificate == LOCAL_RECOMPLETION_DELETE) {
$params = [
'userid' => $userid,
'courseid' => $course->id,
];

if ($config->archivecertificate) {

// Archive the issued certificates.
$sql = "SELECT ci.*, c.printdate
FROM {certificate_issues} ci
JOIN {certificate} c ON c.id = ci.certificateid
WHERE ci.userid = :userid AND ci.certificateid IN (SELECT id FROM {certificate} WHERE course = :courseid)";
$issuedcerts = $DB->get_records_sql($sql, $params);

foreach (array_keys($issuedcerts) as $ic) {
$issuedcerts[$ic]->course = $course->id;
// Depending on activity settings actual date printed on a certificate can be different
// to a date of issue. Let's try to build printed date and archive it as well for future verification.
$issuedcerts[$ic]->printdate = self::certificate_get_date(
$issuedcerts[$ic]->timecreated,
$issuedcerts[$ic]->printdate,
(object) ['id' => $course->id],
$userid
);
}

// Archive records.
$DB->insert_records('local_recompletion_cert', $issuedcerts);
}

// Finally delete records.
$selectsql = 'userid = :userid AND certificateid IN (SELECT id FROM {certificate} WHERE course = :courseid)';
$DB->delete_records_select('certificate_issues', $selectsql, $params);
}
}

/**
* Helper function to check if it's installed.
* @return bool
*/
public static function installed(): bool {
global $CFG;

if (!file_exists($CFG->dirroot . '/mod/certificate/version.php')) {
return false;
}

return true;
}

/**
* Returns the date to display for the certificate.
*
* This is pretty much replication of certificate_get_date from locallib.php of mod_certificate.
*
* @param string $issueddate Issue date.
* @param string $printdate Print date setting.
* @param stdClass $course Course object.
* @param int $userid User ID.
*
* @return string the date
*/
protected static function certificate_get_date(string $issueddate, string $printdate, stdClass $course, int $userid): string {
global $DB, $CFG;

require_once($CFG->dirroot . '/mod/certificate/locallib.php');

$date = $issueddate;

if ($printdate == '2') {
$sql = "SELECT MAX(c.timecompleted) as timecompleted
FROM {course_completions} c
WHERE c.userid = :userid
AND c.course = :courseid";
if ($timecompleted = $DB->get_record_sql($sql, ['userid' => $userid, 'courseid' => $course->id])) {
if (!empty($timecompleted->timecompleted)) {
$date = $timecompleted->timecompleted;
}
}
} else if ($printdate > 2) {
if ($modinfo = certificate_get_mod_grade($course, $printdate, $userid)) {
$date = $modinfo->dategraded;
}
}

return $date;
}
}
40 changes: 38 additions & 2 deletions classes/privacy/provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@ public static function get_metadata(collection $collection) : collection {
'score' => 'privacy:metadata:score',
], 'privacy:metadata:local_recompletion_hpa');

$collection->add_database_table('local_recompletion_cert', [
'userid' => 'privacy:metadata:userid',
'timecreated' => 'privacy:metadata:local_recompletion_cert:timecreated',
'course' => 'privacy:metadata:course',
], 'privacy:metadata:local_recompletion_cert');

return $collection;
}

Expand Down Expand Up @@ -367,6 +373,14 @@ public static function export_user_data(approved_contextlist $contextlist) {
[get_string('recompletion', 'local_recompletion'), 'hotpot_attempts'],
(object)[array_map([self::class, 'transform_db_row_to_session_data'], $records)]);
}

$records = $DB->get_records('local_recompletion_cert', $params);
foreach ($records as $record) {
$context = \context_course::instance($record->course);
writer::with_context($context)->export_data(
[get_string('recompletion', 'local_recompletion'), 'certificate_issues'],
(object)[array_map([self::class, 'transform_db_row_to_session_data'], $records)]);
}
}
}

Expand All @@ -381,7 +395,7 @@ public static function export_user_data(approved_contextlist $contextlist) {
*/
private static function transform_db_row_to_session_data(stdClass $dbrow) : stdClass {
$times = array('timeenrolled', 'timestarted', 'timecompleted', 'timemodified', 'timemodifiedoffline',
'timestart', 'timefinish', 'timeseen', 'starttime', 'endtime');
'timestart', 'timefinish', 'timeseen', 'starttime', 'endtime', 'timecreated');
foreach ($times as $time) {
if (isset($dbrow->$time) && (!empty($dbrow->$time))) {
$dbrow->$time = transform::datetime($dbrow->$time);
Expand Down Expand Up @@ -421,6 +435,7 @@ public static function delete_data_for_all_users_in_context(\context $context) {
$DB->delete_records('local_recompletion_lb', $params);
$DB->delete_records('local_recompletion_lo', $params);
$DB->delete_records('local_recompletion_hpa', $params);
$DB->delete_records('local_recompletion_cert', $params);

self::delete_hp5_activity_records($courseid);
}
Expand Down Expand Up @@ -457,6 +472,7 @@ public static function delete_data_for_user(approved_contextlist $contextlist) {
$DB->delete_records('local_recompletion_lb', $params);
$DB->delete_records('local_recompletion_lo', $params);
$DB->delete_records('local_recompletion_hpa', $params);
$DB->delete_records('local_recompletion_cert', $params);
}
}

Expand Down Expand Up @@ -557,6 +573,12 @@ public static function get_contexts_for_userid(int $userid) : contextlist {
JOIN {local_recompletion_hpa} rc ON rc.course = c.id and rc.userid = :userid";
$contextlist->add_from_sql($sql, $params);

$sql = "SELECT ctx.id
FROM {course} c
JOIN {context} ctx ON c.id = ctx.instanceid AND ctx.contextlevel = :contextlevel
JOIN {local_recompletion_cert} rc ON rc.course = c.id and rc.userid = :userid";
$contextlist->add_from_sql($sql, $params);

return $contextlist;
}
/**
Expand Down Expand Up @@ -682,6 +704,13 @@ public static function get_users_in_context(userlist $userlist) {
JOIN {context} ctx ON c.id = ctx.instanceid AND ctx.contextlevel = :contextlevel
WHERE ctx.id = :contextid";
$userlist->add_from_sql('userid', $sql, $params);

$sql = "SELECT rc.userid
FROM {local_recompletion_cert} rc
JOIN {course} c ON rc.course = c.id
JOIN {context} ctx ON c.id = ctx.instanceid AND ctx.contextlevel = :contextlevel
WHERE ctx.id = :contextid";
$userlist->add_from_sql('userid', $sql, $params);
}
/**
* Delete multiple users within a single context.
Expand Down Expand Up @@ -821,7 +850,14 @@ public static function delete_data_for_users(approved_userlist $userlist) {
JOIN {course} c ON rc.course = c.id
JOIN {context} ctx ON c.id = ctx.instanceid AND ctx.contextlevel = :contextlevel
WHERE ctx.id = :contextid AND rc.userid $insql";
$DB->delete_records_select('local_recompletion_lo', "id $sql", $params);
$DB->delete_records_select('local_recompletion_hpa', "id $sql", $params);

$sql = "SELECT rc.id
FROM {local_recompletion_cert} rc
JOIN {course} c ON rc.course = c.id
JOIN {context} ctx ON c.id = ctx.instanceid AND ctx.contextlevel = :contextlevel
WHERE ctx.id = :contextid AND rc.userid $insql";
$DB->delete_records_select('local_recompletion_cert', "id $sql", $params);
}

/**
Expand Down
Loading

0 comments on commit e0d0152

Please sign in to comment.