You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1577 lines
53 KiB

<?php
// This file is part of the Certificate module for 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/>.
/**
* Certificate module core interaction API
*
* @package mod
* @subpackage certificate
* @copyright Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once($CFG->dirroot.'/course/lib.php');
require_once($CFG->dirroot.'/grade/lib.php');
require_once($CFG->dirroot.'/grade/querylib.php');
/** The border image folder */
define('CERT_IMAGE_BORDER', 'borders');
/** The watermark image folder */
define('CERT_IMAGE_WATERMARK', 'watermarks');
/** The signature image folder */
define('CERT_IMAGE_SIGNATURE', 'signatures');
/** The seal image folder */
define('CERT_IMAGE_SEAL', 'seals');
/** Set CERT_PER_PAGE to 0 if you wish to display all certificates on the report page */
define('CERT_PER_PAGE', 30);
define('CERT_MAX_PER_PAGE', 200);
/**
* Add certificate instance.
*
* @param stdClass $certificate
* @return int new certificate instance id
*/
function certificate_add_instance($certificate) {
global $DB;
// Create the certificate.
$certificate->timecreated = time();
$certificate->timemodified = $certificate->timecreated;
return $DB->insert_record('certificate', $certificate);
}
/**
* Update certificate instance.
*
* @param stdClass $certificate
* @return bool true
*/
function certificate_update_instance($certificate) {
global $DB;
// Update the certificate.
$certificate->timemodified = time();
$certificate->id = $certificate->instance;
return $DB->update_record('certificate', $certificate);
}
/**
* Given an ID of an instance of this module,
* this function will permanently delete the instance
* and any data that depends on it.
*
* @param int $id
* @return bool true if successful
*/
function certificate_delete_instance($id) {
global $DB;
// Ensure the certificate exists
if (!$certificate = $DB->get_record('certificate', array('id' => $id))) {
return false;
}
// Prepare file record object
if (!$cm = get_coursemodule_from_instance('certificate', $id)) {
return false;
}
$result = true;
$DB->delete_records('certificate_issues', array('certificateid' => $id));
if (!$DB->delete_records('certificate', array('id' => $id))) {
$result = false;
}
// Delete any files associated with the certificate
$context = context_module::instance($cm->id);
$fs = get_file_storage();
$fs->delete_area_files($context->id);
return $result;
}
/**
* This function is used by the reset_course_userdata function in moodlelib.
* This function will remove all posts from the specified certificate
* and clean up any related data.
*
* Written by Jean-Michel Vedrine
*
* @param $data the data submitted from the reset course.
* @return array status array
*/
function certificate_reset_userdata($data) {
global $CFG, $DB;
$componentstr = get_string('modulenameplural', 'certificate');
$status = array();
if (!empty($data->reset_certificate)) {
$sql = "SELECT cert.id
FROM {certificate} cert
WHERE cert.course = :courseid";
$DB->delete_records_select('certificate_issues', "certificateid IN ($sql)", array('courseid' => $data->courseid));
$status[] = array('component' => $componentstr, 'item' => get_string('certificateremoved', 'certificate'), 'error' => false);
}
// Updating dates - shift may be negative too
if ($data->timeshift) {
shift_course_mod_dates('certificate', array('timeopen', 'timeclose'), $data->timeshift, $data->courseid);
$status[] = array('component' => $componentstr, 'item' => get_string('datechanged'), 'error' => false);
}
return $status;
}
/**
* Implementation of the function for printing the form elements that control
* whether the course reset functionality affects the certificate.
*
* Written by Jean-Michel Vedrine
*
* @param $mform form passed by reference
*/
function certificate_reset_course_form_definition(&$mform) {
$mform->addElement('header', 'certificateheader', get_string('modulenameplural', 'certificate'));
$mform->addElement('advcheckbox', 'reset_certificate', get_string('deletissuedcertificates', 'certificate'));
}
/**
* Course reset form defaults.
*
* Written by Jean-Michel Vedrine
*
* @param stdClass $course
* @return array
*/
function certificate_reset_course_form_defaults($course) {
return array('reset_certificate' => 1);
}
/**
* Returns information about received certificate.
* Used for user activity reports.
*
* @param stdClass $course
* @param stdClass $user
* @param stdClass $mod
* @param stdClass $certificate
* @return stdClass the user outline object
*/
function certificate_user_outline($course, $user, $mod, $certificate) {
global $DB;
$result = new stdClass;
if ($issue = $DB->get_record('certificate_issues', array('certificateid' => $certificate->id, 'userid' => $user->id))) {
$result->info = get_string('issued', 'certificate');
$result->time = $issue->timecreated;
} else {
$result->info = get_string('notissued', 'certificate');
}
return $result;
}
/**
* Returns information about received certificate.
* Used for user activity reports.
*
* @param stdClass $course
* @param stdClass $user
* @param stdClass $mod
* @param stdClass $page
* @return string the user complete information
*/
function certificate_user_complete($course, $user, $mod, $certificate) {
global $DB, $OUTPUT;
if ($issue = $DB->get_record('certificate_issues', array('certificateid' => $certificate->id, 'userid' => $user->id))) {
echo $OUTPUT->box_start();
echo get_string('issued', 'certificate') . ": ";
echo userdate($issue->timecreated);
certificate_print_user_files($certificate->id, $user->id);
echo '<br />';
echo $OUTPUT->box_end();
} else {
print_string('notissuedyet', 'certificate');
}
}
/**
* Must return an array of user records (all data) who are participants
* for a given instance of certificate.
*
* @param int $certificateid
* @return stdClass list of participants
*/
function certificate_get_participants($certificateid) {
global $DB;
$sql = "SELECT DISTINCT u.id, u.id
FROM {user} u, {certificate_issues} a
WHERE a.certificateid = :certificateid
AND u.id = a.userid";
return $DB->get_records_sql($sql, array('certificateid' => $certificateid));
}
/**
* @uses FEATURE_GROUPS
* @uses FEATURE_GROUPINGS
* @uses FEATURE_GROUPMEMBERSONLY
* @uses FEATURE_MOD_INTRO
* @uses FEATURE_COMPLETION_TRACKS_VIEWS
* @uses FEATURE_GRADE_HAS_GRADE
* @uses FEATURE_GRADE_OUTCOMES
* @param string $feature FEATURE_xx constant for requested feature
* @return mixed True if module supports feature, null if doesn't know
*/
function certificate_supports($feature) {
switch ($feature) {
case FEATURE_GROUPS: return true;
case FEATURE_GROUPINGS: return true;
case FEATURE_GROUPMEMBERSONLY: return true;
case FEATURE_MOD_INTRO: return true;
case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
case FEATURE_BACKUP_MOODLE2: return true;
default: return null;
}
}
/**
* Function to be run periodically according to the moodle cron
* TODO:This needs to be done
*/
function certificate_cron () {
global $DB;
$sql = "select distinct cm.id as cmid, c.*, ce.id as certificateid, ce.name as certificatename
from {certificate} ce
inner join {course} c on c.id = ce.course
inner join {course_modules} cm on cm.course = c.id and cm.instance = ce.id and cm.module = ?
where c.visible = 1
order by c.id";
echo "\n\nGenerate certificates for user that has completed the course-------------------------\n";
$certmodule = $DB->get_record('modules', array('name' => 'certificate'));
$courses = $DB->get_records_sql($sql, array($certmodule->id));
$sql = "select u.*
from {course_completions} cc
inner join {user} u on u.id = cc.userid
where cc.course = ?
and cc.timecompleted is not null
and cc.userid not in (select ci.userid
from {certificate_issues} ci
where ci.certificateid = ?)
order by u.firstname";
foreach ($courses as $course) {
echo " Processing course {$course->fullname}, certificate: {$course->certificatename}...\n";
$students = $DB->get_records_sql($sql, array($course->id, $course->certificateid));
$total = count($students);
if ($total > 0) {
echo " {$total} students without certificate found, generating certificates for them...\n";
$certificate = $DB->get_record('certificate', array('id' => $course->certificateid));
$cm = get_coursemodule_from_id('certificate', $course->cmid);
foreach ($students as $student) {
if (!$certificate->requiredtime or
(certificate_get_course_time($course->id) >= ($certificate->requiredtime * 60))) {
echo " generating issue certificate for {$student->firstname} {$student->lastname}...";
$certrecord = certificate_get_issue($course, $student, $certificate, $cm);
echo " Done! Certificate {$certrecord->code} generated!\n";
} else {
echo " {$student->firstname} {$student->lastname} has not required course time. Skiped!\n";
}
}
} else {
echo " No students without certificate found! Course skipped.\n";
}
}
return true;
}
/**
* Returns a list of teachers by group
* for sending email alerts to teachers
*
* @param stdClass $certificate
* @param stdClass $user
* @param stdClass $course
* @param stdClass $cm
* @return array the teacher array
*/
function certificate_get_teachers($certificate, $user, $course, $cm) {
global $USER, $DB;
$context = context_module::instance($cm->id);
$potteachers = get_users_by_capability($context, 'mod/certificate:manage',
'', '', '', '', '', '', false, false);
if (empty($potteachers)) {
return array();
}
$teachers = array();
if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS) { // Separate groups are being used
if ($groups = groups_get_all_groups($course->id, $user->id)) { // Try to find all groups
foreach ($groups as $group) {
foreach ($potteachers as $t) {
if ($t->id == $user->id) {
continue; // do not send self
}
if (groups_is_member($group->id, $t->id)) {
$teachers[$t->id] = $t;
}
}
}
} else {
// user not in group, try to find teachers without group
foreach ($potteachers as $t) {
if ($t->id == $USER->id) {
continue; // do not send self
}
if (!groups_get_all_groups($course->id, $t->id)) { //ugly hack
$teachers[$t->id] = $t;
}
}
}
} else {
foreach ($potteachers as $t) {
if ($t->id == $USER->id) {
continue; // do not send self
}
$teachers[$t->id] = $t;
}
}
return $teachers;
}
/**
* Alerts teachers by email of received certificates. First checks
* whether the option to email teachers is set for this certificate.
*
* @param stdClass $course
* @param stdClass $certificate
* @param stdClass $certrecord
* @param stdClass $cm course module
*/
function certificate_email_teachers($course, $certificate, $certrecord, $cm) {
global $USER, $CFG, $DB;
if ($certificate->emailteachers == 0) { // No need to do anything
return;
}
$user = $DB->get_record('user', array('id' => $certrecord->userid));
if ($teachers = certificate_get_teachers($certificate, $user, $course, $cm)) {
$strawarded = get_string('awarded', 'certificate');
foreach ($teachers as $teacher) {
$info = new stdClass;
$info->student = fullname($USER);
$info->course = format_string($course->fullname,true);
$info->certificate = format_string($certificate->name,true);
$info->url = $CFG->wwwroot.'/mod/certificate/report.php?id='.$cm->id;
$from = $USER;
$postsubject = $strawarded . ': ' . $info->student . ' -> ' . $certificate->name;
$posttext = certificate_email_teachers_text($info);
$posthtml = ($teacher->mailformat == 1) ? certificate_email_teachers_html($info) : '';
@email_to_user($teacher, $from, $postsubject, $posttext, $posthtml); // If it fails, oh well, too bad.
}
}
}
/**
* Alerts others by email of received certificates. First checks
* whether the option to email others is set for this certificate.
* Uses the email_teachers info.
* Code suggested by Eloy Lafuente
*
* @param stdClass $course
* @param stdClass $certificate
* @param stdClass $certrecord
* @param stdClass $cm course module
*/
function certificate_email_others($course, $certificate, $certrecord, $cm) {
global $USER, $CFG, $DB;
if ($certificate->emailothers) {
$others = explode(',', $certificate->emailothers);
if ($others) {
$strawarded = get_string('awarded', 'certificate');
foreach ($others as $other) {
$other = trim($other);
if (validate_email($other)) {
$destination = new stdClass;
$destination->email = $other;
$info = new stdClass;
$info->student = fullname($USER);
$info->course = format_string($course->fullname, true);
$info->certificate = format_string($certificate->name, true);
$info->url = $CFG->wwwroot.'/mod/certificate/report.php?id='.$cm->id;
$from = $USER;
$postsubject = $strawarded . ': ' . $info->student . ' -> ' . $certificate->name;
$posttext = certificate_email_teachers_text($info);
$posthtml = certificate_email_teachers_html($info);
@email_to_user($destination, $from, $postsubject, $posttext, $posthtml); // If it fails, oh well, too bad.
}
}
}
}
}
/**
* Creates the text content for emails to teachers -- needs to be finished with cron
*
* @param $info object The info used by the 'emailteachermail' language string
* @return string
*/
function certificate_email_teachers_text($info) {
$posttext = get_string('emailteachermail', 'certificate', $info) . "\n";
return $posttext;
}
/**
* Creates the html content for emails to teachers
*
* @param $info object The info used by the 'emailteachermailhtml' language string
* @return string
*/
function certificate_email_teachers_html($info) {
$posthtml = '<font face="sans-serif">';
$posthtml .= '<p>' . get_string('emailteachermailhtml', 'certificate', $info) . '</p>';
$posthtml .= '</font>';
return $posthtml;
}
/**
* Sends the student their issued certificate from moddata as an email
* attachment.
*
* @param stdClass $course
* @param stdClass $certificate
* @param stdClass $certrecord
* @param stdClass $context
*/
function certificate_email_student($course, $certificate, $certrecord, $context) {
global $DB, $USER;
// Get teachers
if ($users = get_users_by_capability($context, 'moodle/course:update', 'u.*', 'u.id ASC',
'', '', '', '', false, true)) {
$users = sort_by_roleassignment_authority($users, $context);
$teacher = array_shift($users);
}
// If we haven't found a teacher yet, look for a non-editing teacher in this course.
if (empty($teacher) && $users = get_users_by_capability($context, 'moodle/course:update', 'u.*', 'u.id ASC',
'', '', '', '', false, true)) {
$users = sort_by_roleassignment_authority($users, $context);
$teacher = array_shift($users);
}
// Ok, no teachers, use administrator name
if (empty($teacher)) {
$teacher = fullname(get_admin());
}
$info = new stdClass;
$info->username = fullname($USER);
$info->certificate = format_string($certificate->name, true);
$info->course = format_string($course->fullname, true);
$from = fullname($teacher);
$subject = $info->course . ': ' . $info->certificate;
$message = get_string('emailstudenttext', 'certificate', $info) . "\n";
// Make the HTML version more XHTML happy (&amp;)
$messagehtml = text_to_html(get_string('emailstudenttext', 'certificate', $info));
// Remove full-stop at the end if it exists, to avoid "..pdf" being created and being filtered by clean_filename
$certname = rtrim($certificate->name, '.');
$filename = clean_filename("$certname.pdf");
// Get hashed pathname
$fs = get_file_storage();
$component = 'mod_certificate';
$filearea = 'issue';
$filepath = '/';
$files = $fs->get_area_files($context->id, $component, $filearea, $certrecord->id);
foreach ($files as $f) {
$filepathname = $f->get_contenthash();
}
$attachment = 'filedir/'.certificate_path_from_hash($filepathname).'/'.$filepathname;
$attachname = $filename;
return email_to_user($USER, $from, $subject, $message, $messagehtml, $attachment, $attachname);
}
/**
* Retrieve certificate path from hash
*
* @param array $contenthash
* @return string the path
*/
function certificate_path_from_hash($contenthash) {
$l1 = $contenthash[0].$contenthash[1];
$l2 = $contenthash[2].$contenthash[3];
return "$l1/$l2";
}
/**
* Serves certificate issues and other files.
*
* @param stdClass $course
* @param stdClass $cm
* @param stdClass $context
* @param string $filearea
* @param array $args
* @param bool $forcedownload
* @return bool|nothing false if file not found, does not return anything if found - just send the file
*/
function certificate_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $CFG, $DB, $USER;
if ($context->contextlevel != CONTEXT_MODULE) {
return false;
}
if (!$certificate = $DB->get_record('certificate', array('id' => $cm->instance))) {
return false;
}
require_login($course, false, $cm);
require_once($CFG->libdir.'/filelib.php');
if ($filearea === 'issue') {
$certrecord = (int)array_shift($args);
if (!$certrecord = $DB->get_record('certificate_issues', array('id' => $certrecord))) {
return false;
}
if ($USER->id != $certrecord->userid and !has_capability('mod/certificate:manage', $context)) {
return false;
}
$relativepath = implode('/', $args);
$fullpath = "/{$context->id}/mod_certificate/issue/$certrecord->id/$relativepath";
$fs = get_file_storage();
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
return false;
}
send_stored_file($file, 0, 0, true); // download MUST be forced - security!
}
}
/**
* This function returns success or failure of file save
*
* @param string $pdf is the string contents of the pdf
* @param int $certrecordid the certificate issue record id
* @param string $filename pdf filename
* @param int $contextid context id
* @return bool return true if successful, false otherwise
*/
function certificate_save_pdf($pdf, $certrecordid, $filename, $contextid) {
global $DB, $USER;
if (empty($certrecordid)) {
return false;
}
if (empty($pdf)) {
return false;
}
$fs = get_file_storage();
// Prepare file record object
$component = 'mod_certificate';
$filearea = 'issue';
$filepath = '/';
$fileinfo = array(
'contextid' => $contextid, // ID of context
'component' => $component, // usually = table name
'filearea' => $filearea, // usually = table name
'itemid' => $certrecordid, // usually = ID of row in table
'filepath' => $filepath, // any path beginning and ending in /
'filename' => $filename, // any filename
'mimetype' => 'application/pdf', // any filename
'userid' => $USER->id);
// If the file exists, delete it and recreate it. This is to ensure that the
// latest certificate is saved on the server. For example, the student's grade
// may have been updated. This is a quick dirty hack.
if ($fs->file_exists($contextid, $component, $filearea, $certrecordid, $filepath, $filename)) {
$fs->delete_area_files($contextid, $component, $filearea, $certrecordid);
}
$fs->create_file_from_string($fileinfo, $pdf);
return true;
}
/**
* Produces a list of links to the issued certificates. Used for report.
*
* @param stdClass $certificate
* @param int $userid
* @param stdClass $context
* @return string return the user files
*/
function certificate_print_user_files($certificate, $userid, $context) {
global $CFG, $DB, $OUTPUT;
$output = '';
$certrecord = $DB->get_record('certificate_issues', array('userid' => $userid, 'certificateid' => $certificate->id));
$fs = get_file_storage();
$browser = get_file_browser();
$component = 'mod_certificate';
$filearea = 'issue';
$files = $fs->get_area_files($context, $component, $filearea, $certrecord->id);
foreach ($files as $file) {
$filename = $file->get_filename();
$mimetype = $file->get_mimetype();
$link = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$context.'/mod_certificate/issue/'.$certrecord->id.'/'.$filename);
$output = '<img src="'.$OUTPUT->pix_url(file_mimetype_icon($file->get_mimetype())).'" height="16" width="16" alt="'.$file->get_mimetype().'" />&nbsp;'.
'<a href="'.$link.'" >'.s($filename).'</a>';
}
$output .= '<br />';
$output = '<div class="files">'.$output.'</div>';
return $output;
}
/**
* Inserts preliminary user data when a certificate is viewed.
* Prevents form from issuing a certificate upon browser refresh.
*
* @param stdClass $course
* @param stdClass $user
* @param stdClass $certificate
* @param stdClass $cm
* @return stdClass the newly created certificate issue
*/
function certificate_get_issue($course, $user, $certificate, $cm) {
global $DB;
// Check if there is an issue already, should only ever be one
if ($certissue = $DB->get_record('certificate_issues', array('userid' => $user->id, 'certificateid' => $certificate->id))) {
return $certissue;
}
// Create new certificate issue record
$certissue = new stdClass();
$certissue->certificateid = $certificate->id;
$certissue->userid = $user->id;
$certissue->code = certificate_generate_code();
$certissue->timecreated = time();
$certissue->id = $DB->insert_record('certificate_issues', $certissue);
// Email to the teachers and anyone else
certificate_email_teachers($course, $certificate, $certissue, $cm);
certificate_email_others($course, $certificate, $certissue, $cm);
return $certissue;
}
/**
* Returns a list of issued certificates - sorted for report.
*
* @param int $certificateid
* @param string $sort the sort order
* @param bool $groupmode are we in group mode ?
* @param stdClass $cm the course module
* @param int $page offset
* @param int $perpage total per page
* @return stdClass the users
*/
function certificate_get_issues($certificateid, $sort="ci.timecreated ASC", $groupmode, $cm, $page = 0, $perpage = 0) {
global $CFG, $DB;
// get all users that can manage this certificate to exclude them from the report.
$context = context_module::instance($cm->id);
$conditionssql = '';
$conditionsparams = array();
if ($certmanagers = array_keys(get_users_by_capability($context, 'mod/certificate:manage', 'u.id'))) {
list($sql, $params) = $DB->get_in_or_equal($certmanagers, SQL_PARAMS_NAMED, 'cert');
$conditionssql .= "AND NOT u.id $sql \n";
$conditionsparams += $params;
}
$restricttogroup = false;
if ($groupmode) {
$currentgroup = groups_get_activity_group($cm);
if ($currentgroup) {
$restricttogroup = true;
$groupusers = array_keys(groups_get_members($currentgroup, 'u.*'));
if (empty($groupusers)) {
return array();
}
}
}
$restricttogrouping = false;
// if groupmembersonly used, remove users who are not in any group
if (!empty($CFG->enablegroupings) and $cm->groupmembersonly) {
if ($groupingusers = groups_get_grouping_members($cm->groupingid, 'u.id', 'u.id')) {
$restricttogrouping = true;
} else {
return array();
}
}
if ($restricttogroup || $restricttogrouping) {
if ($restricttogroup) {
$allowedusers = $groupusers;
} else if ($restricttogroup && $restricttogrouping) {
$allowedusers = array_intersect($groupusers, $groupingusers);
} else {
$allowedusers = $groupingusers;
}
list($sql, $params) = $DB->get_in_or_equal($allowedusers, SQL_PARAMS_NAMED, 'grp');
$conditionssql .= "AND u.id $sql \n";
$conditionsparams += $params;
}
$page = (int) $page;
$perpage = (int) $perpage;
// Get all the users that have certificates issued, should only be one issue per user for a certificate
$allparams = $conditionsparams + array('certificateid' => $certificateid);
$users = $DB->get_records_sql("SELECT u.*, ci.code, ci.timecreated
FROM {user} u
INNER JOIN {certificate_issues} ci
ON u.id = ci.userid
WHERE u.deleted = 0
AND ci.certificateid = :certificateid
$conditionssql
ORDER BY {$sort}",
$allparams,
$page * $perpage,
$perpage);
return $users;
}
/**
* Returns a list of previously issued certificates--used for reissue.
*
* @param int $certificateid
* @return stdClass the attempts else false if none found
*/
function certificate_get_attempts($certificateid) {
global $DB, $USER;
$sql = "SELECT *
FROM {certificate_issues} i
WHERE certificateid = :certificateid
AND userid = :userid";
if ($issues = $DB->get_records_sql($sql, array('certificateid' => $certificateid, 'userid' => $USER->id))) {
return $issues;
}
return false;
}
/**
* Prints a table of previously issued certificates--used for reissue.
*
* @param stdClass $course
* @param stdClass $certificate
* @param stdClass $attempts
* @return string the attempt table
*/
function certificate_print_attempts($course, $certificate, $attempts) {
global $OUTPUT, $DB;
echo $OUTPUT->heading(get_string('summaryofattempts', 'certificate'));
// Prepare table header
$table = new html_table();
$table->class = 'generaltable';
$table->head = array(get_string('issued', 'certificate'));
$table->align = array('left');
$table->attributes = array("style" => "width:20%; margin:auto");
$gradecolumn = $certificate->printgrade;
if ($gradecolumn) {
$table->head[] = get_string('grade');
$table->align[] = 'center';
$table->size[] = '';
}
// One row for each attempt
foreach ($attempts as $attempt) {
$row = array();
// prepare strings for time taken and date completed
$datecompleted = userdate($attempt->timecreated);
$row[] = $datecompleted;
if ($gradecolumn) {
$attemptgrade = certificate_get_grade($certificate, $course);
$row[] = $attemptgrade;
}
$table->data[$attempt->id] = $row;
}
echo html_writer::table($table);
}
/**
* Get the time the user has spent in the course
*
* @param int $courseid
* @return int the total time spent in seconds
*/
function certificate_get_course_time($courseid) {
global $CFG, $USER;
set_time_limit(0);
$totaltime = 0;
$sql = "l.course = :courseid AND l.userid = :userid";
if ($logs = get_logs($sql, array('courseid' => $courseid, 'userid' => $USER->id), 'l.time ASC', '', '', $totalcount)) {
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->time;
$lasthit = $log->time;
$totaltime = 0;
}
$delay = $log->time - $lasthit;
if ($delay > ($CFG->sessiontimeout * 60)) {
// 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->time;
} else {
$totaltime += $delay;
}
// Now the actual log became the previous log for the next cycle
$lasthit = $log->time;
}
return $totaltime;
}
return 0;
}
/**
* Get all the modules
*
* @return array
*/
function certificate_get_mods() {
global $COURSE, $DB;
$strtopic = get_string("topic");
$strweek = get_string("week");
$strsection = get_string("section");
// Collect modules data
$modinfo = get_fast_modinfo($COURSE);
$mods = $modinfo->get_cms();
$modules = array();
$sections = $modinfo->get_section_info_all();
for ($i = 0; $i <= count($sections) - 1; $i++) {
// should always be true
if (isset($sections[$i])) {
$section = $sections[$i];
if ($section->sequence) {
switch ($COURSE->format) {
case "topics":
$sectionlabel = $strtopic;
break;
case "weeks":
$sectionlabel = $strweek;
break;
default:
$sectionlabel = $strsection;
}
$sectionmods = explode(",", $section->sequence);
foreach ($sectionmods as $sectionmod) {
if (empty($mods[$sectionmod])) {
continue;
}
$mod = $mods[$sectionmod];
$instance = $DB->get_record($mod->modname, array('id' => $mod->instance));
if ($grade_items = grade_get_grade_items_for_activity($mod)) {
$mod_item = grade_get_grades($COURSE->id, 'mod', $mod->modname, $mod->instance);
$item = reset($mod_item->items);
if (isset($item->grademax)){
$modules[$mod->id] = $sectionlabel . ' ' . $section->section . ' : ' . $instance->name;
}
}
}
}
}
}
return $modules;
}
/**
* Search through all the modules for grade data for mod_form.
*
* @return array
*/
function certificate_get_grade_options() {
$gradeoptions['0'] = get_string('no');
$gradeoptions['1'] = get_string('coursegrade', 'certificate');
return $gradeoptions;
}
/**
* Search through all the modules for grade dates for mod_form.
*
* @return array
*/
function certificate_get_date_options() {
$dateoptions['0'] = get_string('no');
$dateoptions['1'] = get_string('issueddate', 'certificate');
$dateoptions['2'] = get_string('completiondate', 'certificate');
return $dateoptions;
}
/**
* Fetch all grade categories from the specified course.
*
* @param int $courseid the course id
* @return array
*/
function certificate_get_grade_categories($courseid) {
$grade_category_options = array();
if ($grade_categories = grade_category::fetch_all(array('courseid' => $courseid))) {
foreach ($grade_categories as $grade_category) {
if (!$grade_category->is_course_category()) {
$grade_category_options[-$grade_category->id] = get_string('category') . ' : ' . $grade_category->get_name();
}
}
}
return $grade_category_options;
}
/**
* Get the course outcomes for for mod_form print outcome.
*
* @return array
*/
function certificate_get_outcomes() {
global $COURSE, $DB;
// get all outcomes in course
$grade_seq = new grade_tree($COURSE->id, false, true, '', false);
if ($grade_items = $grade_seq->items) {
// list of item for menu
$printoutcome = array();
foreach ($grade_items as $grade_item) {
if (isset($grade_item->outcomeid)){
$itemmodule = $grade_item->itemmodule;
$printoutcome[$grade_item->id] = $itemmodule . ': ' . $grade_item->get_name();
}
}
}
if (isset($printoutcome)) {
$outcomeoptions['0'] = get_string('no');
foreach ($printoutcome as $key => $value) {
$outcomeoptions[$key] = $value;
}
} else {
$outcomeoptions['0'] = get_string('nooutcomes', 'certificate');
}
return $outcomeoptions;
}
/**
* Used for course participation report (in case certificate is added).
*
* @return array
*/
function certificate_get_view_actions() {
return array('view', 'view all', 'view report');
}
/**
* Used for course participation report (in case certificate is added).
*
* @return array
*/
function certificate_get_post_actions() {
return array('received');
}
/**
* Get certificate types indexed and sorted by name for mod_form.
*
* @return array containing the certificate type
*/
function certificate_types() {
$types = array();
$names = get_list_of_plugins('mod/certificate/type');
$sm = get_string_manager();
foreach ($names as $name) {
if ($sm->string_exists('type'.$name, 'certificate')) {
$types[$name] = get_string('type'.$name, 'certificate');
} else {
$types[$name] = ucfirst($name);
}
}
asort($types);
return $types;
}
/**
* Get images for mod_form.
*
* @param string $type the image type
* @return array
*/
function certificate_get_images($type) {
global $CFG, $DB;
switch($type) {
case CERT_IMAGE_BORDER :
$path = "$CFG->dirroot/mod/certificate/pix/borders";
$uploadpath = "$CFG->dataroot/mod/certificate/pix/borders";
break;
case CERT_IMAGE_SEAL :
$path = "$CFG->dirroot/mod/certificate/pix/seals";
$uploadpath = "$CFG->dataroot/mod/certificate/pix/seals";
break;
case CERT_IMAGE_SIGNATURE :
$path = "$CFG->dirroot/mod/certificate/pix/signatures";
$uploadpath = "$CFG->dataroot/mod/certificate/pix/signatures";
break;
case CERT_IMAGE_WATERMARK :
$path = "$CFG->dirroot/mod/certificate/pix/watermarks";
$uploadpath = "$CFG->dataroot/mod/certificate/pix/watermarks";
break;
}
// If valid path
if (!empty($path)) {
$options = array();
$options += certificate_scan_image_dir($path);
$options += certificate_scan_image_dir($uploadpath);
// Sort images
ksort($options);
// Add the 'no' option to the top of the array
$options = array_merge(array('0' => get_string('no')), $options);
return $options;
} else {
return array();
}
}
/**
* Prepare to print an activity grade.
*
* @param stdClass $course
* @param int $moduleid
* @param int $userid
* @return stdClass|bool return the mod object if it exists, false otherwise
*/
function certificate_get_mod_grade($course, $moduleid, $userid) {
global $DB;
$cm = $DB->get_record('course_modules', array('id' => $moduleid));
$module = $DB->get_record('modules', array('id' => $cm->module));
if ($grade_item = grade_get_grades($course->id, 'mod', $module->name, $cm->instance, $userid)) {
$item = new grade_item();
$itemproperties = reset($grade_item->items);
foreach ($itemproperties as $key => $value) {
$item->$key = $value;
}
$modinfo = new stdClass;
$modinfo->name = utf8_decode($DB->get_field($module->name, 'name', array('id' => $cm->instance)));
$grade = $item->grades[$userid]->grade;
$item->gradetype = GRADE_TYPE_VALUE;
$item->courseid = $course->id;
$modinfo->points = grade_format_gradevalue($grade, $item, true, GRADE_DISPLAY_TYPE_REAL, $decimals = 2);
$modinfo->percentage = grade_format_gradevalue($grade, $item, true, GRADE_DISPLAY_TYPE_PERCENTAGE, $decimals = 2);
$modinfo->letter = grade_format_gradevalue($grade, $item, true, GRADE_DISPLAY_TYPE_LETTER, $decimals = 0);
if ($grade) {
$modinfo->dategraded = $item->grades[$userid]->dategraded;
} else {
$modinfo->dategraded = time();
}
return $modinfo;
}
return false;
}
/**
* Returns the date to display for the certificate.
*
* @param stdClass $certificate
* @param stdClass $certrecord
* @param stdClass $course
* @param int $userid
* @return string the date
*/
function certificate_get_date($certificate, $certrecord, $course, $userid = null) {
global $DB, $USER;
if (empty($userid)) {
$userid = $USER->id;
}
// Set certificate date to current time, can be overwritten later
$date = $certrecord->timecreated;
if ($certificate->printdate == '2') {
// Get the enrolment end date
$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, array('userid' => $userid, 'courseid' => $course->id))) {
if (!empty($timecompleted->timecompleted)) {
$date = $timecompleted->timecompleted;
}
}
} else if ($certificate->printdate > 2) {
if ($modinfo = certificate_get_mod_grade($course, $certificate->printdate, $userid)) {
$date = $modinfo->dategraded;
}
}
if ($certificate->printdate > 0) {
if ($certificate->datefmt == 1) {
$certificatedate = userdate($date, '%B %d, %Y');
} else if ($certificate->datefmt == 2) {
$suffix = certificate_get_ordinal_number_suffix(userdate($date, '%d'));
$certificatedate = userdate($date, '%B %d' . $suffix . ', %Y');
} else if ($certificate->datefmt == 3) {
$certificatedate = userdate($date, '%d %B %Y');
} else if ($certificate->datefmt == 4) {
$certificatedate = userdate($date, '%B %Y');
} else if ($certificate->datefmt == 5) {
$certificatedate = userdate($date, get_string('strftimedate', 'langconfig'));
}
return $certificatedate;
}
return '';
}
/**
* Helper function to return the suffix of the day of
* the month, eg 'st' if it is the 1st of the month.
*
* @param int the day of the month
* @return string the suffix.
*/
function certificate_get_ordinal_number_suffix($day) {
if (!in_array(($day % 100), array(11, 12, 13))) {
switch ($day % 10) {
// Handle 1st, 2nd, 3rd
case 1: return 'st';
case 2: return 'nd';
case 3: return 'rd';
}
}
return 'th';
}
/**
* Returns the grade to display for the certificate.
*
* @param stdClass $certificate
* @param stdClass $course
* @param int $userid
* @return string the grade result
*/
function certificate_get_grade($certificate, $course, $userid = null) {
global $USER, $DB;
if (empty($userid)) {
$userid = $USER->id;
}
if ($certificate->printgrade > 0) {
if ($certificate->printgrade == 1) {
if ($course_item = grade_item::fetch_course_item($course->id)) {
// String used
$strcoursegrade = get_string('coursegrade', 'certificate');
$grade = new grade_grade(array('itemid' => $course_item->id, 'userid' => $userid));
$course_item->gradetype = GRADE_TYPE_VALUE;
$coursegrade = new stdClass;
$coursegrade->points = grade_format_gradevalue($grade->finalgrade, $course_item, true, GRADE_DISPLAY_TYPE_REAL, $decimals = 2);
$coursegrade->percentage = grade_format_gradevalue($grade->finalgrade, $course_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE, $decimals = 2);
$coursegrade->letter = grade_format_gradevalue($grade->finalgrade, $course_item, true, GRADE_DISPLAY_TYPE_LETTER, $decimals = 0);
if ($certificate->gradefmt == 1) {
$grade = $strcoursegrade . ': ' . $coursegrade->percentage;
} else if ($certificate->gradefmt == 2) {
$grade = $strcoursegrade . ': ' . $coursegrade->points;
} else if ($certificate->gradefmt == 3) {
$grade = $strcoursegrade . ': ' . $coursegrade->letter;
}
return $grade;
}
} else { // Print the mod grade
if ($modinfo = certificate_get_mod_grade($course, $certificate->printgrade, $userid)) {
// String used
$strgrade = get_string('grade', 'certificate');
if ($certificate->gradefmt == 1) {
$grade = $modinfo->name . ' ' . $strgrade . ': ' . $modinfo->percentage;
} else if ($certificate->gradefmt == 2) {
$grade = $modinfo->name . ' ' . $strgrade . ': ' . $modinfo->points;
} else if ($certificate->gradefmt == 3) {
$grade = $modinfo->name . ' ' . $strgrade . ': ' . $modinfo->letter;
}
return $grade;
}
}
} else if ($certificate->printgrade < 0) { // Must be a category id.
if ($category_item = grade_item::fetch(array('itemtype' => 'category', 'iteminstance' => -$certificate->printgrade))) {
$category_item->gradetype = GRADE_TYPE_VALUE;
$grade = new grade_grade(array('itemid' => $category_item->id, 'userid' => $userid));
$category_grade = new stdClass;
$category_grade->points = grade_format_gradevalue($grade->finalgrade, $category_item, true, GRADE_DISPLAY_TYPE_REAL, $decimals = 2);
$category_grade->percentage = grade_format_gradevalue($grade->finalgrade, $category_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE, $decimals = 2);
$category_grade->letter = grade_format_gradevalue($grade->finalgrade, $category_item, true, GRADE_DISPLAY_TYPE_LETTER, $decimals = 0);
if ($certificate->gradefmt == 1) {
$formattedgrade = $category_grade->percentage;
} else if ($certificate->gradefmt == 2) {
$formattedgrade = $category_grade->points;
} else if ($certificate->gradefmt == 3) {
$formattedgrade = $category_grade->letter;
}
return $formattedgrade;
}
}
return '';
}
/**
* Returns the outcome to display on the certificate
*
* @param stdClass $certificate
* @param stdClass $course
* @return string the outcome
*/
function certificate_get_outcome($certificate, $course) {
global $USER, $DB;
if ($certificate->printoutcome > 0) {
if ($grade_item = new grade_item(array('id' => $certificate->printoutcome))) {
$outcomeinfo = new stdClass;
$outcomeinfo->name = $grade_item->get_name();
$outcome = new grade_grade(array('itemid' => $grade_item->id, 'userid' => $USER->id));
$outcomeinfo->grade = grade_format_gradevalue($outcome->finalgrade, $grade_item, true, GRADE_DISPLAY_TYPE_REAL);
return $outcomeinfo->name . ': ' . $outcomeinfo->grade;
}
}
return '';
}
/**
* Returns the code to display on the certificate.
*
* @param stdClass $course
* @param stdClass $certrecord
* @return string the code
*/
function certificate_get_code($certificate, $certrecord) {
if ($certificate->printnumber) {
return $certrecord->code;
}
return '';
}
/**
* Sends text to output given the following params.
*
* @param stdClass $pdf
* @param int $x horizontal position
* @param int $y vertical position
* @param char $align L=left, C=center, R=right
* @param string $font any available font in font directory
* @param char $style ''=normal, B=bold, I=italic, U=underline
* @param int $size font size in points
* @param string $text the text to print
* @param int $width horizontal dimension of text block
*/
function certificate_print_text($pdf, $x, $y, $align, $font='freeserif', $style, $size = 10, $text, $width = 0) {
$pdf->setFont($font, $style, $size);
$pdf->SetXY($x, $y);
$pdf->writeHTMLCell($width, 0, '', '', $text, 0, 0, 0, true, $align);
}
/**
* Creates rectangles for line border for A4 size paper.
*
* @param stdClass $pdf
* @param stdClass $certificate
*/
function certificate_draw_frame($pdf, $certificate) {
if ($certificate->bordercolor > 0) {
if ($certificate->bordercolor == 1) {
$color = array(0, 0, 0); // black
}
if ($certificate->bordercolor == 2) {
$color = array(153, 102, 51); // brown
}
if ($certificate->bordercolor == 3) {
$color = array(0, 51, 204); // blue
}
if ($certificate->bordercolor == 4) {
$color = array(0, 180, 0); // green
}
switch ($certificate->orientation) {
case 'L':
// create outer line border in selected color
$pdf->SetLineStyle(array('width' => 1.5, 'color' => $color));
$pdf->Rect(10, 10, 277, 190);
// create middle line border in selected color
$pdf->SetLineStyle(array('width' => 0.2, 'color' => $color));
$pdf->Rect(13, 13, 271, 184);
// create inner line border in selected color
$pdf->SetLineStyle(array('width' => 1.0, 'color' => $color));
$pdf->Rect(16, 16, 265, 178);
break;
case 'P':
// create outer line border in selected color
$pdf->SetLineStyle(array('width' => 1.5, 'color' => $color));
$pdf->Rect(10, 10, 190, 277);
// create middle line border in selected color
$pdf->SetLineStyle(array('width' => 0.2, 'color' => $color));
$pdf->Rect(13, 13, 184, 271);
// create inner line border in selected color
$pdf->SetLineStyle(array('width' => 1.0, 'color' => $color));
$pdf->Rect(16, 16, 178, 265);
break;
}
}
}
/**
* Creates rectangles for line border for letter size paper.
*
* @param stdClass $pdf
* @param stdClass $certificate
*/
function certificate_draw_frame_letter($pdf, $certificate) {
if ($certificate->bordercolor > 0) {
if ($certificate->bordercolor == 1) {
$color = array(0, 0, 0); //black
}
if ($certificate->bordercolor == 2) {
$color = array(153, 102, 51); //brown
}
if ($certificate->bordercolor == 3) {
$color = array(0, 51, 204); //blue
}
if ($certificate->bordercolor == 4) {
$color = array(0, 180, 0); //green
}
switch ($certificate->orientation) {
case 'L':
// create outer line border in selected color
$pdf->SetLineStyle(array('width' => 4.25, 'color' => $color));
$pdf->Rect(28, 28, 736, 556);
// create middle line border in selected color
$pdf->SetLineStyle(array('width' => 0.2, 'color' => $color));
$pdf->Rect(37, 37, 718, 538);
// create inner line border in selected color
$pdf->SetLineStyle(array('width' => 2.8, 'color' => $color));
$pdf->Rect(46, 46, 700, 520);
break;
case 'P':
// create outer line border in selected color
$pdf->SetLineStyle(array('width' => 1.5, 'color' => $color));
$pdf->Rect(25, 20, 561, 751);
// create middle line border in selected color
$pdf->SetLineStyle(array('width' => 0.2, 'color' => $color));
$pdf->Rect(40, 35, 531, 721);
// create inner line border in selected color
$pdf->SetLineStyle(array('width' => 1.0, 'color' => $color));
$pdf->Rect(51, 46, 509, 699);
break;
}
}
}
/**
* Prints border images from the borders folder in PNG or JPG formats.
*
* @param stdClass $pdf;
* @param stdClass $certificate
* @param int $x x position
* @param int $y y position
* @param int $w the width
* @param int $h the height
*/
function certificate_print_image($pdf, $certificate, $type, $x, $y, $w, $h) {
global $CFG;
switch($type) {
case CERT_IMAGE_BORDER :
$attr = 'borderstyle';
$path = "$CFG->dirroot/mod/certificate/pix/$type/$certificate->borderstyle";
$uploadpath = "$CFG->dataroot/mod/certificate/pix/$type/$certificate->borderstyle";
break;
case CERT_IMAGE_SEAL :
$attr = 'printseal';
$path = "$CFG->dirroot/mod/certificate/pix/$type/$certificate->printseal";
$uploadpath = "$CFG->dataroot/mod/certificate/pix/$type/$certificate->printseal";
break;
case CERT_IMAGE_SIGNATURE :
$attr = 'printsignature';
$path = "$CFG->dirroot/mod/certificate/pix/$type/$certificate->printsignature";
$uploadpath = "$CFG->dataroot/mod/certificate/pix/$type/$certificate->printsignature";
break;
case CERT_IMAGE_WATERMARK :
$attr = 'printwmark';
$path = "$CFG->dirroot/mod/certificate/pix/$type/$certificate->printwmark";
$uploadpath = "$CFG->dataroot/mod/certificate/pix/$type/$certificate->printwmark";
break;
}
// Has to be valid
if (!empty($path)) {
switch ($certificate->$attr) {
case '0' :
case '' :
break;
default :
if (file_exists($path)) {
$pdf->Image($path, $x, $y, $w, $h);
}
if (file_exists($uploadpath)) {
$pdf->Image($uploadpath, $x, $y, $w, $h);
}
break;
}
}
}
function custom_random_string($length=15) {
$pool = 'ABCDEFGHJKMNOPQRSTUVWXYZ';
$pool .= 'abcdefghjkmnopqrstuvwxyz';
$pool .= '0123456789';
$poollen = strlen($pool);
$string = '';
for($i=0; $i < $length; $i++) {
$string .= substr($pool, (mt_rand()%($poollen)), 1);
}
return $string;
}
/**
* Generates a 10-digit code of random letters and numbers.
*
* @return string
*/
function certificate_generate_code() {
global $DB;
$uniquecodefound = false;
$code = custom_random_string(10);
while (!$uniquecodefound) {
if (!$DB->record_exists('certificate_issues', array('code' => $code))) {
$uniquecodefound = true;
} else {
$code = custom_random_string(10);
}
}
return $code;
}
/**
* Scans directory for valid images
*
* @param string the path
* @return array
*/
function certificate_scan_image_dir($path) {
// Array to store the images
$options = array();
// Start to scan directory
if (is_dir($path)) {
if ($handle = opendir($path)) {
while (false !== ($file = readdir($handle))) {
if (strpos($file, '.png', 1) || strpos($file, '.jpg', 1) ) {
$i = strpos($file, '.');
if ($i > 1) {
// Set the name
$options[$file] = substr($file, 0, $i);
}
}
}
closedir($handle);
}
}
return $options;
}