|
| 1 | +<?php |
| 2 | +// This file is part of Moodle - http://moodle.org/ |
| 3 | +// |
| 4 | +// Moodle is free software: you can redistribute it and/or modify |
| 5 | +// it under the terms of the GNU General Public License as published by |
| 6 | +// the Free Software Foundation, either version 3 of the License, or |
| 7 | +// (at your option) any later version. |
| 8 | +// |
| 9 | +// Moodle is distributed in the hope that it will be useful, |
| 10 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | +// GNU General Public License for more details. |
| 13 | +// |
| 14 | +// You should have received a copy of the GNU General Public License |
| 15 | +// along with Moodle. If not, see <http://www.gnu.org/licenses/>. |
| 16 | + |
| 17 | +/** |
| 18 | + * A scheduled task for issuing certificates that have requested someone get emailed. |
| 19 | + * |
| 20 | + * @package mod_customcert |
| 21 | + * @copyright 2024 Oscar Nadjar <[email protected]> |
| 22 | + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
| 23 | + */ |
| 24 | +namespace mod_customcert\task; |
| 25 | + |
| 26 | +use mod_customcert\helper; |
| 27 | + |
| 28 | +/** |
| 29 | + * A scheduled task for issuing certificates that have requested someone get emailed. |
| 30 | + * |
| 31 | + * @package mod_customcert |
| 32 | + * @copyright 2024 Oscar Nadjar <[email protected]> |
| 33 | + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
| 34 | + */ |
| 35 | +class issue_certificates_task extends \core\task\scheduled_task { |
| 36 | + |
| 37 | + /** |
| 38 | + * Get a descriptive name for this task (shown to admins). |
| 39 | + * |
| 40 | + * @return string |
| 41 | + */ |
| 42 | + public function get_name() { |
| 43 | + return get_string('issuecertificate', 'customcert'); |
| 44 | + } |
| 45 | + |
| 46 | + /** |
| 47 | + * Execute. |
| 48 | + */ |
| 49 | + public function execute() { |
| 50 | + global $DB; |
| 51 | + |
| 52 | + // Get the certificatesperrun, includeinnotvisiblecourses, and certificateexecutionperiod configurations. |
| 53 | + $certificatesperrun = (int)get_config('customcert', 'certificatesperrun'); |
| 54 | + $includeinnotvisiblecourses = (bool)get_config('customcert', 'includeinnotvisiblecourses'); |
| 55 | + $certificateexecutionperiod = (int)get_config('customcert', 'certificateexecutionperiod'); |
| 56 | + $offset = (int)get_config('customcert', 'certificate_offset'); |
| 57 | + |
| 58 | + // We are going to issue certificates that have requested someone get emailed. |
| 59 | + $emailotherslengthsql = $DB->sql_length('c.emailothers'); |
| 60 | + $sql = "SELECT c.*, ct.id as templateid, ct.name as templatename, ct.contextid, co.id as courseid, |
| 61 | + co.fullname as coursefullname, co.shortname as courseshortname |
| 62 | + FROM {customcert} c |
| 63 | + JOIN {customcert_templates} ct ON c.templateid = ct.id |
| 64 | + JOIN {course} co ON c.course = co.id |
| 65 | + JOIN {course_categories} cat ON co.category = cat.id |
| 66 | + LEFT JOIN {customcert_issues} ci ON c.id = ci.customcertid"; |
| 67 | + |
| 68 | + // Add conditions to exclude certificates from hidden courses. |
| 69 | + $sql .= " WHERE (c.emailstudents = :emailstudents |
| 70 | + OR c.emailteachers = :emailteachers |
| 71 | + OR $emailotherslengthsql >= 3)"; |
| 72 | + |
| 73 | + $params = ['emailstudents' => 1, 'emailteachers' => 1]; |
| 74 | + |
| 75 | + // Check the includeinnotvisiblecourses configuration. |
| 76 | + if (!$includeinnotvisiblecourses) { |
| 77 | + // Exclude certificates from hidden courses. |
| 78 | + $sql .= " AND co.visible = 1 AND cat.visible = 1"; |
| 79 | + } |
| 80 | + // Add condition based on certificate execution period. |
| 81 | + if ($certificateexecutionperiod > 0) { |
| 82 | + // Include courses with no end date or end date greater than the specified period. |
| 83 | + $sql .= " AND (co.enddate > :enddate OR (co.enddate = 0 AND ci.timecreated > :enddate2))"; |
| 84 | + $params['enddate'] = time() - $certificateexecutionperiod; |
| 85 | + $params['enddate2'] = $params['enddate']; |
| 86 | + } |
| 87 | + |
| 88 | + $sql .= " GROUP BY c.id, ct.id, ct.name, ct.contextid, co.id, co.fullname, co.shortname"; |
| 89 | + |
| 90 | + // Execute the SQL query. |
| 91 | + $customcerts = $DB->get_records_sql($sql, $params, $offset, $certificatesperrun); |
| 92 | + |
| 93 | + // When we get to the end of the list, reset the offset. |
| 94 | + set_config('certificate_offset', !empty($customcerts) ? $offset + $certificatesperrun : 0, 'customcert'); |
| 95 | + if (empty($customcerts)) { |
| 96 | + return; |
| 97 | + } |
| 98 | + |
| 99 | + foreach ($customcerts as $customcert) { |
| 100 | + |
| 101 | + // Check if the certificate is hidden, quit early. |
| 102 | + $cm = get_course_and_cm_from_instance($customcert->id, 'customcert', $customcert->course)[1]; |
| 103 | + if (!$cm->visible) { |
| 104 | + continue; |
| 105 | + } |
| 106 | + |
| 107 | + // Do not process an empty certificate. |
| 108 | + $sql = "SELECT ce.* |
| 109 | + FROM {customcert_elements} ce |
| 110 | + JOIN {customcert_pages} cp ON cp.id = ce.pageid |
| 111 | + JOIN {customcert_templates} ct ON ct.id = cp.templateid |
| 112 | + WHERE ct.contextid = :contextid"; |
| 113 | + if (!$DB->record_exists_sql($sql, ['contextid' => $customcert->contextid])) { |
| 114 | + continue; |
| 115 | + } |
| 116 | + |
| 117 | + // Get the context. |
| 118 | + $context = \context::instance_by_id($customcert->contextid); |
| 119 | + |
| 120 | + // Get a list of all the issues. |
| 121 | + $sql = "SELECT u.id |
| 122 | + FROM {customcert_issues} ci |
| 123 | + JOIN {user} u |
| 124 | + ON ci.userid = u.id |
| 125 | + WHERE ci.customcertid = :customcertid |
| 126 | + AND ci.emailed = 1"; |
| 127 | + $issuedusers = $DB->get_records_sql($sql, ['customcertid' => $customcert->id]); |
| 128 | + |
| 129 | + // Now, get a list of users who can Manage the certificate. |
| 130 | + $userswithmanage = get_users_by_capability($context, 'mod/customcert:manage', 'u.id'); |
| 131 | + |
| 132 | + // Get the context of the Custom Certificate module. |
| 133 | + $cmcontext = \context_module::instance($cm->id); |
| 134 | + |
| 135 | + // Now, get a list of users who can view and issue the certificate but have not yet. |
| 136 | + // Get users with the mod/customcert:receiveissue capability in the Custom Certificate module context. |
| 137 | + $userswithissue = get_users_by_capability($cmcontext, 'mod/customcert:receiveissue'); |
| 138 | + // Get users with mod/customcert:view capability. |
| 139 | + $userswithview = get_users_by_capability($cmcontext, 'mod/customcert:view'); |
| 140 | + // Users with both mod/customcert:view and mod/customcert:receiveissue cabapilities. |
| 141 | + $userswithissueview = array_intersect_key($userswithissue, $userswithview); |
| 142 | + |
| 143 | + foreach ($userswithissueview as $enroluser) { |
| 144 | + // Check if the user has already been issued and emailed. |
| 145 | + if (in_array($enroluser->id, array_keys((array)$issuedusers))) { |
| 146 | + continue; |
| 147 | + } |
| 148 | + |
| 149 | + // Don't want to issue to teachers. |
| 150 | + if (in_array($enroluser->id, array_keys((array)$userswithmanage))) { |
| 151 | + continue; |
| 152 | + } |
| 153 | + |
| 154 | + // Now check if the certificate is not visible to the current user. |
| 155 | + $cm = get_fast_modinfo($customcert->courseid, $enroluser->id)->instances['customcert'][$customcert->id]; |
| 156 | + if (!$cm->uservisible) { |
| 157 | + continue; |
| 158 | + } |
| 159 | + |
| 160 | + // Check that they have passed the required time. |
| 161 | + if (!empty($customcert->requiredtime)) { |
| 162 | + if (\mod_customcert\certificate::get_course_time($customcert->courseid, |
| 163 | + $enroluser->id) < ($customcert->requiredtime * 60)) { |
| 164 | + continue; |
| 165 | + } |
| 166 | + } |
| 167 | + |
| 168 | + // Ensure the cert hasn't already been issued, e.g via the UI (view.php) - a race condition. |
| 169 | + $issue = $DB->get_record('customcert_issues', |
| 170 | + ['userid' => $enroluser->id, 'customcertid' => $customcert->id], 'id, emailed'); |
| 171 | + |
| 172 | + // Ok, issue them the certificate. |
| 173 | + $issueid = empty($issue) ? |
| 174 | + \mod_customcert\certificate::issue_certificate($customcert->id, $enroluser->id) : $issue->id; |
| 175 | + |
| 176 | + // Validate issueid and one last check for emailed. |
| 177 | + if (!empty($issueid) && empty($issue->emailed)) { |
| 178 | + // We create a new adhoc task to send the email. |
| 179 | + $task = new \mod_customcert\task\email_certificate_task(); |
| 180 | + $task->set_custom_data(['issueid' => $issueid, 'customcertid' => $customcert->id]); |
| 181 | + $useadhoc = get_config('customcert', 'useadhoc'); |
| 182 | + if ($useadhoc) { |
| 183 | + \core\task\manager::queue_adhoc_task($task); |
| 184 | + } else { |
| 185 | + $task->execute(); |
| 186 | + } |
| 187 | + } |
| 188 | + } |
| 189 | + } |
| 190 | + } |
| 191 | +} |
0 commit comments