AutorÃa | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://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 <http://www.gnu.org/licenses/>./*** This file manages the public functions of this module** @package mod_custommailing* @author jeanfrancois@cblue.be,olivier@cblue.be* @copyright 2021 CBlue SPRL* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/use core\notification;use mod_custommailing\Mailing;use mod_custommailing\MailingLog;define('MAILING_MODE_NONE', 0);define('MAILING_MODE_FIRSTLAUNCH', 1);define('MAILING_MODE_REGISTRATION', 2);define('MAILING_MODE_COMPLETE', 3);define('MAILING_MODE_DAYSFROMINSCRIPTIONDATE', 4);define('MAILING_MODE_DAYSFROMLASTCONNECTION', 5);define('MAILING_MODE_DAYSFROMFIRSTLAUNCH', 6);define('MAILING_MODE_DAYSFROMLASTLAUNCH', 7);define('MAILING_MODE_SEND_CERTIFICATE', 8);define('MAILING_STATUS_DISABLED', 0);define('MAILING_STATUS_ENABLED', 1);define('MAILING_LOG_IDLE', 0);define('MAILING_LOG_PROCESSING', 1);define('MAILING_LOG_SENT', 2);define('MAILING_LOG_FAILED', 3);define('MAILING_SOURCE_MODULE', 1);define('MAILING_SOURCE_COURSE', 2);define('MAILING_SOURCE_CERT', 3);/*** @param $custommailing* @return bool|int* @throws coding_exception* @throws dml_exception*/function custommailing_add_instance($custommailing) {global $CFG, $DB;$custommailing->timecreated = time();$custommailing->timemodified = time();// Check if course has completion enabled, and enable it if not (and user has permission to do so)$course = $DB->get_record('course', ['id' => $custommailing->course]);if (empty($course->enablecompletion)) {if (empty($CFG->enablecompletion)) {// Completion tracking is disabled in Moodlenotification::error(get_string('coursecompletionnotenabled', 'custommailing'));} else {// Completion tracking is enabled in Moodleif (has_capability('moodle/course:update', context_course::instance($course->id))) {$data = ['id' => $course->id, 'enablecompletion' => '1'];$DB->update_record('course', $data);rebuild_course_cache($course->id);notification::warning(get_string('coursecompletionenabled', 'custommailing'));} else {notification::error(get_string('coursecompletionnotenabled', 'custommailing'));}}}return $DB->insert_record('custommailing', $custommailing);}/*** @param $custommailing* @return bool* @throws dml_exception*/function custommailing_update_instance($custommailing) {global $DB;$custommailing->timemodified = time();$custommailing->id = $custommailing->instance;return $DB->update_record('custommailing', $custommailing);}/*** @param $id* @return bool* @throws dml_exception*/function custommailing_delete_instance($id) {global $DB;if (!$custommailing = $DB->get_record('custommailing', ['id' => $id])) {return false;}$result = true;// Delete any dependent mailing here.Mailing::deleteAll($custommailing->id);if (!$DB->delete_records('custommailing', ['id' => $custommailing->id])) {$result = false;}return $result;}/*** @param $feature* @return bool|null*/function custommailing_supports($feature) {switch($feature) {case FEATURE_GRADE_HAS_GRADE:return false;case FEATURE_GRADE_OUTCOMES:return false;case FEATURE_ADVANCED_GRADING:return false;case FEATURE_CONTROLS_GRADE_VISIBILITY:return false;case FEATURE_COMPLETION_TRACKS_VIEWS:return false;case FEATURE_COMPLETION_HAS_RULES:return false;case FEATURE_NO_VIEW_LINK:return false;case FEATURE_IDNUMBER:return true;case FEATURE_GROUPS:return false;case FEATURE_GROUPINGS:return false;case FEATURE_MOD_INTRO:return false;case FEATURE_MODEDIT_DEFAULT_COMPLETION:return false;case FEATURE_COMMENT:return false;case FEATURE_RATE:return false;case FEATURE_BACKUP_MOODLE2:return true;case FEATURE_SHOW_DESCRIPTION:return false;case FEATURE_USES_QUESTIONS:return false;default:return false;}}/*** @param mixed $only [false for all OR modname (scorm, quiz, etc...)]* @return array* @throws moodle_exception*/function custommailing_get_activities ($only= false) {global $COURSE, $PAGE;$course_module_context = $PAGE->context;$activities = [];foreach ($modinfo = get_fast_modinfo($COURSE)->get_cms() as $cm) {if ($cm->id != $course_module_context->instanceid) {if (!$only || $cm->modname == $only) {$activities[(int) $cm->id] = format_string($cm->name);}}}return $activities;}/*** @param int $courseid* @return array* @throws dml_exception*/function custommailing_getcustomcertsfromcourse($courseid){global $DB;$certs = [];$result = $DB->get_records('customcert', ['course' => $courseid]);foreach ($result as $cert) {$certs[(int) $cert->id] = format_string($cert->name);}return $certs;}/*** @param int $customcertmoduleid* @return false|mixed|stdClass* @throws dml_exception*/function custommailing_getCmFromCustomcertInstance($customcertmoduleid){global $DB;$module = $DB->get_record('modules', ['name' => 'customcert']);if (!empty($module->id)) {return $DB->get_record('course_modules', ['instance' => $customcertmoduleid, 'module' => $module->id]);} else {return false;}}/*** @param int $customcertid* @return false|mixed|stdClass* @throws dml_exception*/function custommailing_getCustomcert($customcertid){global $DB;return $DB->get_record('customcert', ['id' => $customcertid]);}/*** @throws coding_exception* @throws dml_exception* @throws moodle_exception*/function custommailing_logs_generate(){global $DB;$mailings = Mailing::getAllToSend();foreach ($mailings as $mailing) {$sql = custommailing_getsql($mailing);if (!empty($sql['sql']) && !empty($sql['params'])) {$users = $DB->get_records_sql($sql['sql'], $sql['params']);if (is_array($users)) {foreach ($users as $user) {if (validate_email($user->email) && !$DB->get_record('custommailing_logs', ['custommailingmailingid' => $mailing->id, 'emailtouserid' => $user->id])) {$record = new stdClass();$record->custommailingmailingid = (int) $mailing->id;$record->emailtouserid = (int) $user->id;$record->emailstatus = MAILING_LOG_PROCESSING;$record->timecreated = time();MailingLog::create($record);}}}}}}/*** @param object $mailing* @return false|string* @throws coding_exception* @throws dml_exception* @throws moodle_exception*/function custommailing_getsql($mailing){$sql = false;$params = [];$config = get_config('custommailing');if (!empty($config->debugmode)) {$delay_range = 'MINUTE';} else {$delay_range = 'DAY';}// target module completionif (!empty($mailing->targetmoduleid)) {if (!empty($mailing->targetmodulestatus)) {$sql_where = " AND (cmc.completionstate IN (0,3) OR cmc.completionstate IS NULL)";} else {$sql_where = " AND cmc.completionstate IN (1,2)";}} else {$sql_where = '';}// mailing modesif ($mailing->mailingmode == MAILING_MODE_FIRSTLAUNCH && !empty($mailing->targetmoduleid)) {$sql = "SELECT DISTINCT u.*FROM {user} uJOIN {logstore_standard_log} lsl ON lsl.userid = u.id AND lsl.contextlevel = 70 AND lsl.contextinstanceid = :targetmoduleid AND lsl.action = 'viewed'";$params['targetmoduleid'] = $mailing->targetmoduleid;} elseif ($mailing->mailingmode == MAILING_MODE_REGISTRATION && !empty($mailing->courseid)) {// retroactive modeif (!$mailing->retroactive) {$sql_retro = " AND ue.timecreated >= :timecreated AND (ue.timestart = 0 OR ue.timestart >= :timestart) AND (ue.timeend = 0 OR ue.timeend >= :timeend)";$params['timecreated'] = $mailing->timecreated;$params['timestart'] = $mailing->timecreated;$params['timeend'] = $mailing->timecreated;} else {$sql_retro = '';}$sql = "SELECT DISTINCT u.*FROM {user} uJOIN {user_enrolments} ue ON ue.userid = u.idJOIN {enrol} e ON e.id = ue.enrolidWHERE e.courseid = :courseid $sql_retro";$params['courseid'] = $mailing->courseid;} elseif ($mailing->mailingmode == MAILING_MODE_COMPLETE && !empty($mailing->targetmoduleid)) {$sql = "SELECT DISTINCT u.*FROM {user} uJOIN {course_modules_completion} cmc ON cmc.userid = u.id AND cmc.coursemoduleid = :targetmoduleid";$params['targetmoduleid'] = $mailing->targetmoduleid;} elseif ($mailing->mailingmode == MAILING_MODE_DAYSFROMINSCRIPTIONDATE && !empty($mailing->mailingdelay)) {// retroactive modeif (!$mailing->retroactive) {$sql_retro = " AND ue.timecreated >= :timecreated AND (ue.timestart = 0 OR ue.timestart >= :timestart) AND (ue.timeend = 0 OR ue.timeend >= :timeend)";$params['timecreated'] = $mailing->timecreated;$params['timestart'] = $mailing->timecreated;$params['timeend'] = $mailing->timecreated;} else {$sql_retro = '';}$start = new \DateTime();$interval_duration = "P" . $mailing->mailingdelay . "D";if ($delay_range == 'MINUTE') {$interval_duration = "PT" . $mailing->mailingdelay . "M";}$start->sub(new DateInterval($interval_duration));$sql = "SELECT DISTINCT u.*FROM {user} uJOIN {user_enrolments} ue ON ue.userid = u.idJOIN {enrol} e ON e.id = ue.enrolidWHERE e.courseid = :courseid AND ue.timestart < " . $start->getTimestamp() . " $sql_retro";$params['courseid'] = $mailing->courseid;} elseif ($mailing->mailingmode == MAILING_MODE_DAYSFROMLASTCONNECTION && !empty($mailing->courseid)) {// retroactive modeif (!$mailing->retroactive) {$sql_retro = " AND lsl.timecreated >= :timecreated1";$params['timecreated1'] = $mailing->timecreated;} else {$sql_retro = '';}$start = new \DateTime();$interval_duration = "P" . $mailing->mailingdelay . "D";if ($delay_range == 'MINUTE') {$interval_duration = "PT" . $mailing->mailingdelay . "M";}$start->sub(new DateInterval($interval_duration));$sql = "SELECT DISTINCT u.*FROM {user} uJOIN {course} c ON c.id = :courseidJOIN {logstore_standard_log} lsl ON lsl.userid = u.id AND lsl.contextlevel = 50 AND lsl.action = 'viewed' AND lsl.courseid = c.idWHERE lsl.timecreated < :timecreated2 $sql_retro";$params['courseid'] = $mailing->courseid;$params['timecreated2'] = $start->getTimestamp();} elseif ($mailing->mailingmode == MAILING_MODE_DAYSFROMFIRSTLAUNCH && !empty($mailing->targetmoduleid) && !empty($mailing->mailingdelay)) {// retroactive mode$join = "JOIN {user_enrolments} ue ON ue.userid = u.idJOIN {enrol} e ON e.id = ue.enrolidJOIN {course} c ON c.id = e.courseid";$sql_where .= " AND c.id = :courseid ";$params['courseid'] = $mailing->courseid;if (!$mailing->retroactive) {$sql_where .= " AND ue.timecreated >= :timecreated1 AND (ue.timestart = 0 OR ue.timestart >= :timestart) AND (ue.timeend = 0 OR ue.timeend >= :timeend)";$params['timecreated1'] = $mailing->timecreated;$params['timestart'] = $mailing->timecreated;$params['timeend'] = $mailing->timecreated;}$start = new \DateTime();$interval_duration = "P" . $mailing->mailingdelay . "D";if ($delay_range == 'MINUTE') {$interval_duration = "PT" . $mailing->mailingdelay . "M";}$start->sub(new DateInterval($interval_duration));//ToDo : other modules than scormif (empty($mailing->targetmodulestatus)) {$sql = "SELECT DISTINCT u.*FROM {user} u$joinJOIN {logstore_standard_log} lsl ON lsl.userid = u.id AND lsl.contextlevel = 70 AND lsl.contextinstanceid = :targetmoduleid AND lsl.action = 'launched' AND lsl.target = 'sco'LEFT JOIN {course_modules_completion} cmc ON cmc.userid = u.id AND cmc.coursemoduleid = :coursemoduleidWHERE lsl.timecreated < :timecreated $sql_where";} else {$sql = "SELECT DISTINCT u.*FROM {user} u$joinLEFT JOIN {logstore_standard_log} lsl ON lsl.userid = u.id AND lsl.contextlevel = 70 AND lsl.contextinstanceid = :targetmoduleid AND lsl.action = 'launched' AND lsl.target = 'sco'LEFT JOIN {course_modules_completion} cmc ON cmc.userid = u.id AND cmc.coursemoduleid = :coursemoduleidWHERE (lsl.timecreated < :timecreated OR lsl.timecreated IS NULL) $sql_where";}$params['targetmoduleid'] = $mailing->targetmoduleid;$params['coursemoduleid'] = $mailing->targetmoduleid;$params['timecreated'] = $start->getTimestamp();} elseif ($mailing->mailingmode == MAILING_MODE_DAYSFROMLASTLAUNCH && !empty($mailing->targetmoduleid) && !empty($mailing->mailingdelay)) {// retroactive modeif (!$mailing->retroactive) {$join_retro = " JOIN {user_enrolments} ue ON ue.userid = u.idJOIN {enrol} e ON e.id = ue.enrolidJOIN {course} c ON c.id = e.courseid ";$sql_where .= " c.id = :courseid AND ue.timecreated >= :timecreated AND (ue.timestart = 0 OR ue.timestart >= :timestart) AND (ue.timeend = 0 OR ue.timeend >= :timeend)";$params['courseid'] = $mailing->courseid;$params['timecreated'] = $mailing->timecreated;$params['timestart'] = $mailing->timecreated;$params['timeend'] = $mailing->timecreated;} else {$join_retro = '';}$start = new \DateTime();$interval_duration = "P" . $mailing->mailingdelay . "D";if ($delay_range == 'MINUTE') {$interval_duration = "PT" . $mailing->mailingdelay . "M";}$start->sub(new DateInterval($interval_duration));//ToDo : other modules than scorm$sql = "SELECT DISTINCT u.*FROM {user} u$join_retroJOIN {logstore_standard_log} lsl ON lsl.userid = u.id AND lsl.contextlevel = 70 AND lsl.contextinstanceid = :contextinstanceid AND lsl.action = 'launched' AND lsl.target = 'sco'LEFT JOIN {course_modules_completion} cmc ON cmc.userid = u.id AND cmc.coursemoduleid = :coursemoduleidWHERE lsl.timecreated < :timecreated $sql_where";$params['contextinstanceid'] = $mailing->targetmoduleid;$params['coursemoduleid'] = $mailing->targetmoduleid;$params['timecreated'] = $start->getTimestamp();} elseif ($mailing->mailingmode == MAILING_MODE_SEND_CERTIFICATE && !empty($mailing->customcertmoduleid)) {custommailing_certifications($mailing->customcertmoduleid, $mailing->courseid);// retroactive modeif (!$mailing->retroactive) {$join_retro = " JOIN {user_enrolments} ue ON ue.userid = u.idJOIN {enrol} e ON e.id = ue.enrolidJOIN {course} c ON c.id = e.courseid ";$sql_where = " WHERE c.id = :courseid AND ue.timecreated >= :timecreated1 AND (ue.timestart = 0 OR ue.timestart >= :timestart) AND (ue.timeend = 0 OR ue.timeend >= :timeend)";$params['courseid'] = $mailing->courseid;$params['timecreated1'] = $mailing->timecreated;$params['timestart'] = $mailing->timecreated;$params['timeend'] = $mailing->timecreated;} else {$join_retro = '';$sql_where = '';}$sql = "SELECT DISTINCT u.*FROM {user} u$join_retroJOIN {customcert_issues} ci ON ci.userid = u.id AND ci.customcertid = $mailing->customcertmoduleid$sql_where";}$return = ['sql' => $sql,'params' => $params];return $return;}/*** Process custommailing_logs MAILING_LOG_SENT records* Send email to each user** @throws dml_exception*/function custommailing_crontask() {global $DB;custommailing_logs_generate();$ids_to_update = [];$sql = "SELECT u.*, u.id as userid, rm.mailingsubject, rm.mailingcontent, rl.id as logid, rm.customcertmoduleidFROM {user} uJOIN {custommailing_logs} rl ON rl.emailtouserid = u.idJOIN {custommailing_mailing} rm ON rm.id = rl.custommailingmailingidWHERE rl.emailstatus < :mailing_log_sent";$logs = $DB->get_recordset_sql($sql, ['mailing_log_sent' => MAILING_LOG_SENT]);foreach ($logs as $log) {if (!empty($log->customcertmoduleid)) {$attachment = custommailing_getcertificate($log->userid, $log->customcertmoduleid);} else {$attachment = new stdClass();$attachment->file = '';$attachment->filename = '';}$log->mailingcontent = str_replace(['%firstname%', '%lastname%'], [$log->firstname, $log->lastname], $log->mailingcontent);if (email_to_user($log, core_user::get_support_user(), $log->mailingsubject, strip_tags($log->mailingcontent), $log->mailingcontent, $attachment->file, $attachment->filename)) {$ids_to_update[] = $log->logid;}}$logs->close();// Set emailstatus to MAILING_LOG_SENT on each sended emailif (is_array($ids_to_update) && count($ids_to_update)) {list($insql, $sqlparams) = $DB->get_in_or_equal($ids_to_update, SQL_PARAMS_NAMED);$sqlparams['mailing_log_sent'] = MAILING_LOG_SENT;$DB->execute("UPDATE {custommailing_logs} SET emailstatus = :mailing_log_sent WHERE id $insql", $sqlparams);}}/*** get certificate (REQUIRE mod/customcert)** @param int $userid* @param int $customcertid* @return stdClass* @throws dml_exception*/function custommailing_getcertificate($userid, $customcertid) {global $DB;$sql = "SELECT c.*, ct.id as templateid, ct.name as templatename, ct.contextid, co.id as courseid,co.fullname as coursefullname, co.shortname as courseshortnameFROM {customcert} cJOIN {customcert_templates} ct ON c.templateid = ct.idJOIN {course} co ON c.course = co.idJOIN {customcert_issues} ci ON ci.customcertid = c.idWHERE ci.userid = :userid AND c.id = :certid";$customcert = $DB->get_record_sql($sql, ['userid' => $userid, 'certid' => $customcertid]);$template = new \stdClass();$template->id = $customcert->templateid;$template->name = $customcert->templatename;$template->contextid = $customcert->contextid;$template = new \mod_customcert\template($template);$filecontents = $template->generate_pdf(false, $userid, true);// Set the name of the file we are going to send.$filename = $customcert->courseshortname . '_' . $customcert->name;$filename = \core_text::entities_to_utf8($filename);$filename = strip_tags($filename);$filename = rtrim($filename, '.');$filename = str_replace('&', '_', $filename) . '.pdf';// Create the file we will be sending.$tempdir = make_temp_directory('certificate/attachment');$tempfile = $tempdir . '/' . md5(microtime() . $userid) . '.pdf';file_put_contents($tempfile, $filecontents);$attach = new stdClass();$attach->file = $tempfile;$attach->filename = $filename;return $attach;}/*** @param int $customcertid* @param int $courseid* @throws coding_exception* @throws dml_exception* @throws moodle_exception*/function custommailing_certifications($customcertid, $courseid){global $DB;$sql = "SELECT u.*FROM {user} uJOIN {course} c ON c.id = :courseidJOIN {enrol} e ON e.courseid = c.idJOIN {user_enrolments} ue ON ue.userid = u.id AND ue.enrolid = e.id";$users = $DB->get_records_sql($sql, ['courseid' => $courseid]);foreach ($users as $user) {custommailing_certification($user->id, $customcertid, $courseid);}}/*** @param int $userid* @param int $customcertid* @param int $courseid* @throws coding_exception* @throws dml_exception* @throws moodle_exception*/function custommailing_certification($userid, $customcertid, $courseid){global $DB;$sql = "SELECT cm.*, m.name, md.name AS modnameFROM {course_modules} cmJOIN {modules} md ON md.id = cm.moduleJOIN {customcert} m ON m.id = cm.instanceWHERE cm.instance = :cminstance AND md.name = :modulename AND cm.course = :courseid";$cm = $DB->get_record_sql($sql, ['cminstance' => $customcertid, 'modulename' => 'customcert', 'courseid' => $courseid]);// $cm = get_coursemodule_from_id('customcert', $cmid, $courseid, false, MUST_EXIST);$modinfo = get_fast_modinfo($courseid);$cminfo = $modinfo->get_cm($cm->id);$ainfomod = new \core_availability\info_module($cminfo);if ($ainfomod->is_available($availabilityinfo, false, $userid)) {$customcertissue = new stdClass();$customcertissue->customcertid = $customcertid;$customcertissue->userid = $userid;$customcertissue->code = \mod_customcert\certificate::generate_code();$customcertissue->timecreated = time();if (!$DB->record_exists('customcert_issues', ['userid' => $userid, 'customcertid' => $customcertid])) {$DB->insert_record('customcert_issues', $customcertissue);}}}