. /** * Provides functionality needed by htmlcert activities. * * @package mod_htmlcert * @copyright 2016 Mark Nelson , 2021 Klaus-Uwe Mitterer * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace mod_htmlcert; defined('MOODLE_INTERNAL') || die(); /** * Class certificate. * * Helper functionality for certificates. * * @package mod_htmlcert * @copyright 2016 Mark Nelson , 2021 Klaus-Uwe Mitterer * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class certificate { /** * Send the file inline to the browser. */ const DELIVERY_OPTION_INLINE = 'I'; /** * Send to the browser and force a file download */ const DELIVERY_OPTION_DOWNLOAD = 'D'; /** * @var string the print protection variable */ const PROTECTION_PRINT = 'print'; /** * @var string the modify protection variable */ const PROTECTION_MODIFY = 'modify'; /** * @var string the copy protection variable */ const PROTECTION_COPY = 'copy'; /** * @var int the number of issues that will be displayed on each page in the report * If you want to display all htmlcerts on a page set this to 0. */ const HTMLCERT_PER_PAGE = '50'; /** * Handles setting the protection field for the htmlcert * * @param \stdClass $data * @return string the value to insert into the protection field */ public static function set_protection($data) { $protection = array(); if (!empty($data->protection_print)) { $protection[] = self::PROTECTION_PRINT; } if (!empty($data->protection_modify)) { $protection[] = self::PROTECTION_MODIFY; } if (!empty($data->protection_copy)) { $protection[] = self::PROTECTION_COPY; } // Return the protection string. return implode(', ', $protection); } /** * Handles uploading an image for the htmlcert module. * * @param int $draftitemid the draft area containing the files * @param int $contextid the context we are storing this image in * @param string $filearea indentifies the file area. */ public static function upload_files($draftitemid, $contextid, $filearea = 'image') { global $CFG; // Save the file if it exists that is currently in the draft area. require_once($CFG->dirroot . '/lib/filelib.php'); file_save_draft_area_files($draftitemid, $contextid, 'mod_htmlcert', $filearea, 0); } /** * Get the time the user has spent in the course. * * @param int $courseid * @param int $userid * @return int the total time spent in seconds */ public static function get_course_time(int $courseid, int $userid = 0): int { global $CFG, $DB, $USER; if (empty($userid)) { $userid = $USER->id; } $logmanager = get_log_manager(); $readers = $logmanager->get_readers(); $enabledreaders = get_config('tool_log', 'enabled_stores'); if (empty($enabledreaders)) { return 0; } $enabledreaders = explode(',', $enabledreaders); // Go through all the readers until we find one that we can use. foreach ($enabledreaders as $enabledreader) { $reader = $readers[$enabledreader]; if ($reader instanceof \logstore_legacy\log\store) { $logtable = 'log'; $coursefield = 'course'; $timefield = 'time'; break; } else if ($reader instanceof \core\log\sql_internal_table_reader) { $logtable = $reader->get_internal_log_table_name(); $coursefield = 'courseid'; $timefield = 'timecreated'; break; } } // If we didn't find a reader then return 0. if (!isset($logtable)) { return 0; } $sql = "SELECT id, $timefield FROM {{$logtable}} WHERE userid = :userid AND $coursefield = :courseid ORDER BY $timefield ASC"; $params = array('userid' => $userid, 'courseid' => $courseid); $totaltime = 0; if ($logs = $DB->get_recordset_sql($sql, $params)) { foreach ($logs as $log) { if (!isset($login)) { // For the first time $login is not set so the first log is also the first login. $login = $log->$timefield; $lasthit = $log->$timefield; $totaltime = 0; } $delay = $log->$timefield - $lasthit; if ($delay > $CFG->sessiontimeout) { // The difference between the last log and the current log is more than // the timeout Register session value so that we have found a session! $login = $log->$timefield; } else { $totaltime += $delay; } // Now the actual log became the previous log for the next cycle. $lasthit = $log->$timefield; } return $totaltime; } return 0; } /** * Returns a list of issued htmlcerts. * * @param int $htmlcertid * @param bool $groupmode are we in group mode * @param \stdClass $cm the course module * @param int $limitfrom * @param int $limitnum * @param string $sort * @return array the users */ public static function get_issues($htmlcertid, $groupmode, $cm, $limitfrom, $limitnum, $sort = '') { global $DB; // Get the conditional SQL. list($conditionssql, $conditionsparams) = self::get_conditional_issues_sql($cm, $groupmode); // If it is empty then return an empty array. if (empty($conditionsparams)) { return array(); } // Add the conditional SQL and the htmlcertid to form all used parameters. $allparams = $conditionsparams + array('htmlcertid' => $htmlcertid); // Return the issues. $context = \context_module::instance($cm->id); $extrafields = \core_user\fields::for_identity($context)->get_required_fields(); $ufields = \core_user\fields::for_userpic()->including(...$extrafields); [ 'selects' => $userfieldsselects, 'joins' => $userfieldsjoin, 'params' => $userfieldsparams ] = (array) $ufields->get_sql('u', true); $allparams = array_merge($allparams, $userfieldsparams); $sql = "SELECT ci.id as issueid, ci.code, ci.timecreated $userfieldsselects FROM {user} u INNER JOIN {htmlcert_issues} ci ON u.id = ci.userid $userfieldsjoin WHERE u.deleted = 0 AND ci.htmlcertid = :htmlcertid $conditionssql"; if ($sort) { $sql .= "ORDER BY " . $sort; } else { $sql .= "ORDER BY " . $DB->sql_fullname(); } return $DB->get_records_sql($sql, $allparams, $limitfrom, $limitnum); } /** * Returns the total number of issues for a given htmlcert. * * @param int $htmlcertid * @param \stdClass $cm the course module * @param bool $groupmode the group mode * @return int the number of issues */ public static function get_number_of_issues($htmlcertid, $cm, $groupmode) { global $DB; // Get the conditional SQL. list($conditionssql, $conditionsparams) = self::get_conditional_issues_sql($cm, $groupmode); // If it is empty then return 0. if (empty($conditionsparams)) { return 0; } // Add the conditional SQL and the htmlcertid to form all used parameters. $allparams = $conditionsparams + array('htmlcertid' => $htmlcertid); // Return the number of issues. $sql = "SELECT COUNT(u.id) as count FROM {user} u INNER JOIN {htmlcert_issues} ci ON u.id = ci.userid WHERE u.deleted = 0 AND ci.htmlcertid = :htmlcertid $conditionssql"; return $DB->count_records_sql($sql, $allparams); } /** * Returns an array of the conditional variables to use in the get_issues SQL query. * * @param \stdClass $cm the course module * @param bool $groupmode are we in group mode ? * @return array the conditional variables */ public static function get_conditional_issues_sql($cm, $groupmode) { global $DB, $USER; // Get all users that can manage this htmlcert to exclude them from the report. $context = \context_module::instance($cm->id); $conditionssql = ''; $conditionsparams = array(); // Get all users that can manage this certificate to exclude them from the report. $certmanagers = array_keys(get_users_by_capability($context, 'mod/htmlcert:manage', 'u.id')); $certmanagers = array_merge($certmanagers, array_keys(get_admins())); list($sql, $params) = $DB->get_in_or_equal($certmanagers, SQL_PARAMS_NAMED, 'cert'); $conditionssql .= "AND NOT u.id $sql \n"; $conditionsparams += $params; if ($groupmode) { $canaccessallgroups = has_capability('moodle/site:accessallgroups', $context); $currentgroup = groups_get_activity_group($cm); // If we are viewing all participants and the user does not have access to all groups then return nothing. if (!$currentgroup && !$canaccessallgroups) { return array('', array()); } if ($currentgroup) { if (!$canaccessallgroups) { // Guest users do not belong to any groups. if (isguestuser()) { return array('', array()); } // Check that the user belongs to the group we are viewing. $usersgroups = groups_get_all_groups($cm->course, $USER->id, $cm->groupingid); if ($usersgroups) { if (!isset($usersgroups[$currentgroup])) { return array('', array()); } } else { // They belong to no group, so return an empty array. return array('', array()); } } $groupusers = array_keys(groups_get_members($currentgroup, 'u.*')); if (empty($groupusers)) { return array('', array()); } list($sql, $params) = $DB->get_in_or_equal($groupusers, SQL_PARAMS_NAMED, 'grp'); $conditionssql .= "AND u.id $sql "; $conditionsparams += $params; } } return array($conditionssql, $conditionsparams); } /** * Get number of certificates for a user. * * @param int $userid * @return int */ public static function get_number_of_certificates_for_user($userid) { global $DB; $sql = "SELECT COUNT(*) FROM {htmlcert} c INNER JOIN {htmlcert_issues} ci ON c.id = ci.htmlcertid WHERE ci.userid = :userid"; return $DB->count_records_sql($sql, array('userid' => $userid)); } /** * Gets the certificates for the user. * * @param int $userid * @param int $limitfrom * @param int $limitnum * @param string $sort * @return array */ public static function get_certificates_for_user($userid, $limitfrom, $limitnum, $sort = '') { global $DB; if (empty($sort)) { $sort = 'ci.timecreated DESC'; } $sql = "SELECT c.id, c.name, co.fullname as coursename, ci.code, ci.timecreated FROM {htmlcert} c INNER JOIN {htmlcert_issues} ci ON c.id = ci.htmlcertid INNER JOIN {course} co ON c.course = co.id WHERE ci.userid = :userid ORDER BY $sort"; return $DB->get_records_sql($sql, array('userid' => $userid), $limitfrom, $limitnum); } /** * Issues a certificate to a user. * * @param int $certificateid The ID of the certificate * @param int $userid The ID of the user to issue the certificate to * @return int The ID of the issue */ public static function issue_certificate($certificateid, $userid) { global $DB; $issue = new \stdClass(); $issue->userid = $userid; $issue->htmlcertid = $certificateid; $issue->code = self::generate_code(); $issue->emailed = 0; $issue->timecreated = time(); // Insert the record into the database. return $DB->insert_record('htmlcert_issues', $issue); } /** * Generates a 10-digit code of random letters and numbers. * * @return string */ public static function generate_code() { global $DB; $uniquecodefound = false; $code = random_string(10); while (!$uniquecodefound) { if (!$DB->record_exists('htmlcert_issues', array('code' => $code))) { $uniquecodefound = true; } else { $code = random_string(10); } } return $code; } }