From c71d350b30dfa8ea05b229c886286e5c01b8e980 Mon Sep 17 00:00:00 2001 From: Dmitrii Metelkin Date: Tue, 21 Nov 2023 23:55:23 +1100 Subject: [PATCH] issue #127: add support for mod_certificate --- ...backup_local_recompletion_plugin.class.php | 15 +- ...estore_local_recompletion_plugin.class.php | 23 ++ classes/plugins/mod_certificate.php | 213 ++++++++++++++++++ classes/privacy/provider.php | 40 +++- .../archived_certificate_issues.php | 109 +++++++++ .../entities/certificate_issues.php | 139 ++++++++++++ classes/task/check_recompletion.php | 18 +- db/install.xml | 16 +- db/upgrade.php | 26 +++ lang/en/local_recompletion.php | 10 + version.php | 4 +- 11 files changed, 598 insertions(+), 15 deletions(-) create mode 100644 classes/plugins/mod_certificate.php create mode 100644 classes/reportbuilder/datasource/archived_certificate_issues.php create mode 100644 classes/reportbuilder/entities/certificate_issues.php diff --git a/backup/moodle2/backup_local_recompletion_plugin.class.php b/backup/moodle2/backup_local_recompletion_plugin.class.php index 45955d5..41b9e5d 100644 --- a/backup/moodle2/backup_local_recompletion_plugin.class.php +++ b/backup/moodle2/backup_local_recompletion_plugin.class.php @@ -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', @@ -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; } diff --git a/backup/moodle2/restore_local_recompletion_plugin.class.php b/backup/moodle2/restore_local_recompletion_plugin.class.php index c7e10e7..f914d87 100644 --- a/backup/moodle2/restore_local_recompletion_plugin.class.php +++ b/backup/moodle2/restore_local_recompletion_plugin.class.php @@ -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; } @@ -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. */ @@ -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(); } } diff --git a/classes/plugins/mod_certificate.php b/classes/plugins/mod_certificate.php new file mode 100644 index 0000000..64a19e4 --- /dev/null +++ b/classes/plugins/mod_certificate.php @@ -0,0 +1,213 @@ +. + +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; + } +} diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index 325c334..ead3974 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -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; } @@ -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)]); + } } } @@ -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); @@ -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); } @@ -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); } } @@ -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; } /** @@ -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. @@ -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); } /** diff --git a/classes/reportbuilder/datasource/archived_certificate_issues.php b/classes/reportbuilder/datasource/archived_certificate_issues.php new file mode 100644 index 0000000..cba801f --- /dev/null +++ b/classes/reportbuilder/datasource/archived_certificate_issues.php @@ -0,0 +1,109 @@ +. + +namespace local_recompletion\reportbuilder\datasource; + +use core_course\reportbuilder\local\entities\course_category; +use core_reportbuilder\datasource; +use core_reportbuilder\local\entities\course; +use core_reportbuilder\local\entities\user; +use local_recompletion\reportbuilder\entities\certificate_issues; + +/** + * Mod_certificate archive datasource. + * + * @package local_recompletion + * @author Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class archived_certificate_issues extends datasource { + + /** + * Return user friendly name of the datasource + * + * @return string + */ + public static function get_name(): string { + return get_string('datasource:local_recompletion_cert', 'local_recompletion'); + } + + /** + * Initialise. + */ + protected function initialise(): void { + $certificates = new certificate_issues(); + $tablealias = $certificates->get_table_alias('local_recompletion_cert'); + $this->add_entity($certificates); + + $this->set_main_table('local_recompletion_cert', $tablealias); + + // Join the course entity. + $courseentity = new course(); + $coursealias = $courseentity->get_table_alias('course'); + $this->add_entity($courseentity + ->add_join("JOIN {course} {$coursealias} ON {$coursealias}.id = {$tablealias}.course")); + + // Join the course category entity. + $coursecatentity = new course_category(); + $categoriesalias = $coursecatentity->get_table_alias('course_categories'); + $this->add_entity($coursecatentity + ->add_join("JOIN {course_categories} {$categoriesalias} ON {$categoriesalias}.id = {$coursealias}.category")); + + // Join the user entity. + $userentity = new user(); + $useralias = $userentity->get_table_alias('user'); + $this->add_entity($userentity + ->add_join("JOIN {user} {$useralias} ON {$useralias}.id = {$tablealias}.userid")); + + $this->add_all_from_entities(); + } + + /** + * Return the columns that will be added to the report once is created + * + * @return string[] + */ + public function get_default_columns(): array { + return [ + 'user:fullnamewithlink', + 'course:coursefullnamewithlink', + 'certificate_issues:certificate', + 'certificate_issues:code', + 'certificate_issues:issueddate', + 'certificate_issues:printdate', + ]; + } + + /** + * Return the filters that will be added to the report once is created + * + * @return string[] + */ + public function get_default_filters(): array { + return [ + 'course:courseselector', + ]; + } + + /** + * Return the conditions that will be added to the report once is created + * + * @return string[] + */ + public function get_default_conditions(): array { + return []; + } +} diff --git a/classes/reportbuilder/entities/certificate_issues.php b/classes/reportbuilder/entities/certificate_issues.php new file mode 100644 index 0000000..9144a63 --- /dev/null +++ b/classes/reportbuilder/entities/certificate_issues.php @@ -0,0 +1,139 @@ +. + +namespace local_recompletion\reportbuilder\entities; + +use core_reportbuilder\local\entities\base; +use core_reportbuilder\local\helpers\format; +use core_reportbuilder\local\report\column; +use core_renderer; +use html_writer; +use lang_string; + +/** + * Report builder entity for mod_certificate archived records. + * + * @package local_recompletion + * @author Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class certificate_issues extends base { + + /** + * Database tables that this entity uses and their default aliases + * + * @return string[] Array of $tablename => $alias + */ + protected function get_default_table_aliases(): array { + return [ + 'local_recompletion_cert' => 'rcert' + ]; + } + + /** + * The default title for this entity + * + * @return lang_string + */ + protected function get_default_entity_title(): lang_string { + return new lang_string('entity:local_recompletion_cert', 'local_recompletion'); + } + + /** + * Initialise. + * + * @return \core_reportbuilder\local\entities\base + */ + public function initialise(): base { + $columns = $this->get_all_columns(); + foreach ($columns as $column) { + $this->add_column($column); + } + + return $this; + } + + /** + * Returns list of available columns. + * + * @return column[] + */ + protected function get_all_columns(): array { + $tablealias = $this->get_table_alias('local_recompletion_cert'); + + // Course module. + $columns[] = (new column( + 'certificate', + new lang_string('pluginname', 'certificate'), + $this->get_entity_name() + )) + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_INTEGER) + ->add_fields("{$tablealias}.certificateid, {$tablealias}.course") + ->set_is_sortable(true) + ->add_callback(static function($value, $row): string { + global $PAGE; + + $renderer = new core_renderer($PAGE, RENDERER_TARGET_GENERAL); + $modinfo = get_fast_modinfo($row->course); + + if (!empty($modinfo) && !empty($modinfo->get_instances_of('certificate') + && !empty($modinfo->get_instances_of('certificate')[$row->certificateid]))) { + $cm = $modinfo->get_instances_of('certificate')[$row->certificateid]; + $modulename = get_string('modulename', $cm->modname); + $activityicon = $renderer->pix_icon('monologo', $modulename, $cm->modname, ['class' => 'icon']); + + return $activityicon . html_writer::link($cm->url, format_string($cm->name), []); + } else { + return (string) $row->certificateid; + } + }); + + $columns[] = (new column( + 'code', + new lang_string('code', 'certificate'), + $this->get_entity_name() + )) + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TEXT) + ->add_field("{$tablealias}.code") + ->set_is_sortable(true); + + $columns[] = (new column( + 'issueddate', + new lang_string('issueddate', 'certificate'), + $this->get_entity_name() + )) + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TIMESTAMP) + ->add_field("{$tablealias}.timecreated") + ->set_is_sortable(true) + ->add_callback([format::class, 'userdate']); + + $columns[] = (new column( + 'printdate', + new lang_string('printdate', 'certificate'), + $this->get_entity_name() + )) + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TIMESTAMP) + ->add_field("{$tablealias}.printdate") + ->set_is_sortable(true) + ->add_callback([format::class, 'userdate']); + + return $columns; + } +} diff --git a/classes/task/check_recompletion.php b/classes/task/check_recompletion.php index 80684d8..59b4bc1 100644 --- a/classes/task/check_recompletion.php +++ b/classes/task/check_recompletion.php @@ -268,6 +268,15 @@ public function reset_user($userid, $course, $config = null) { } } + $plugins = local_recompletion_get_supported_plugins(); + foreach ($plugins as $plugin) { + $fqn = 'local_recompletion\\plugins\\' . $plugin; + $error = $fqn::reset($userid, $course, $config); + if (!empty($errors)) { + $errors[] = $error; + } + } + // Archive and delete course completion. $this->reset_completions($userid, $course, $config); @@ -284,15 +293,6 @@ public function reset_user($userid, $course, $config = null) { } } - $plugins = local_recompletion_get_supported_plugins(); - foreach ($plugins as $plugin) { - $fqn = 'local_recompletion\\plugins\\' . $plugin; - $error = $fqn::reset($userid, $course, $config); - if (!empty($errors)) { - $errors[] = $error; - } - } - // Now notify user. $this->notify_user($userid, $course, $config); diff --git a/db/install.xml b/db/install.xml index 6eab944..fc87bcc 100644 --- a/db/install.xml +++ b/db/install.xml @@ -1,5 +1,5 @@ - @@ -486,5 +486,19 @@ + + + + + + + + + + + + + +
diff --git a/db/upgrade.php b/db/upgrade.php index 11d40a7..cf553f5 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -947,5 +947,31 @@ function xmldb_local_recompletion_upgrade($oldversion) { upgrade_plugin_savepoint(true, 2023110500, 'local', 'recompletion'); } + if ($oldversion < 2023112100) { + + // Define table local_recompletion_cert to be created. + $table = new xmldb_table('local_recompletion_cert'); + + // Adding fields to table local_recompletion_cert. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('certificateid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('code', XMLDB_TYPE_CHAR, '40', null, null, null, null); + $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('printdate', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + + // Adding keys to table local_recompletion_cert. + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + + // Conditionally launch create table for local_recompletion_cert. + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + + // Recompletion savepoint reached. + upgrade_plugin_savepoint(true, 2023112100, 'local', 'recompletion'); + } + return true; } diff --git a/lang/en/local_recompletion.php b/lang/en/local_recompletion.php index 2de86bc..8389fc8 100644 --- a/lang/en/local_recompletion.php +++ b/lang/en/local_recompletion.php @@ -176,6 +176,8 @@ $string['privacy:metadata:maxattempts'] = 'Max number of attempts'; $string['privacy:metadata:retake'] = 'Retake'; $string['privacy:metadata:score'] = 'Score'; +$string['privacy:metadata:local_recompletion_cert'] = 'Archive of previous certificate issues.'; +$string['privacy:metadata:local_recompletion_cert:timecreated'] = 'The time that the certificate issue created'; $string['noassigngradepermission'] = 'Your completion was reset, but this course contains an assignment that could not be reset, please ask your teacher to do this for you if required.'; $string['editcompletion'] = 'Edit course completion date'; $string['editcompletion_desc'] = 'Modify the course completion date for the following users:'; @@ -265,3 +267,11 @@ $string['coursecertificateverifywarn'] = 'Attention: Deleting the issued certificates without archiving will result in the fact that issued certificates cannot be verified anymore in Moodle. Please only delete the certificates if this is acceptable for you.'; $string['archivecoursecertificate'] = 'Archive issued certificates (mod_coursecertificate)'; $string['archivecoursecertificate_help'] = 'Should issued certificates be archived? Archived certificates will remain in tool_certificate_issues table, but will have archived status.'; +$string['certificate'] = 'Certificates (mod_certificate)'; +$string['certificate_help'] = 'Should issued certificates be deleted?'; +$string['deletecertificate'] = 'Delete issued certificates'; +$string['certificateverifywarn'] = 'Attention: Deleting the issued certificates without archiving will result in the fact that issued certificates cannot be verified anymore in Moodle. Please only delete the certificates if this is acceptable for you.'; +$string['archivecertificate'] = 'Archive issued certificates (mod_certificate)'; +$string['archivecertificate_help'] = 'Should issued certificates be archived?'; +$string['entity:local_recompletion_cert'] = 'Archive of issued certificates (mod_certificate)'; +$string['datasource:local_recompletion_cert'] = 'Archive of issued certificates (mod_certificate)'; diff --git a/version.php b/version.php index 7c60a91..3eed52f 100644 --- a/version.php +++ b/version.php @@ -24,8 +24,8 @@ defined('MOODLE_INTERNAL') || die; -$plugin->version = 2023111402; -$plugin->release = 2023111402; +$plugin->version = 2023112100; +$plugin->release = 2023112100; $plugin->maturity = MATURITY_STABLE; $plugin->requires = 2022112805; // Requires 4.1. $plugin->component = 'local_recompletion';