Compare commits

...

60 commits

Author SHA1 Message Date
Mark Nelson
be34b27ff9 Fixed size of 'Changed' in CHANGES.md 2020-11-26 23:31:42 +08:00
Mark Nelson
2253884856 Bumped version 2020-11-26 23:00:04 +08:00
Mark Nelson
e5059a63f4 Update CHANGES.md (#390) 2020-11-26 22:11:28 +08:00
uvigo-atic
13a1012ffb Added username to userfield form element (#390) 2020-11-26 22:11:19 +08:00
Mark Nelson
24ac8add66 Updated CHANGES.md (#276) 2020-11-26 21:43:02 +08:00
Mark Nelson
98a2c53463 Update 'emailteachers_help' and 'emailothers_help' language strings (#276) 2020-11-26 21:35:44 +08:00
Mark Nelson
69817ce07a Do not email out certificates with no elements (#276) 2020-11-26 21:13:43 +08:00
Mark Nelson
b56c13b67b Use null coalescing operator in element factory 2020-11-26 18:22:52 +08:00
Mark Nelson
90241238fb Update 'emailstudents_help' language string (#276) 2020-11-26 17:55:54 +08:00
Mark Nelson
5034faa2e4 Bump .travis.yml to use v3 of moodle-plugin-ci 2020-11-09 16:29:14 +01:00
Mark Nelson
0f6282a1b1 Update repo used by Travis 2020-11-09 16:28:54 +01:00
Mark Nelson
7e9de4aada Update CHANGES.md 2020-11-04 19:32:50 +01:00
Brendan Heywood
42d3b3a50e PDFs should be inline not attachments #153 2020-11-04 19:16:22 +01:00
Mark Nelson
c9b83759bc Add CSS class for mobile app (#378) 2020-10-12 13:36:33 +02:00
Mark Nelson
f1a55ce6db Removed multiple empty lines 2020-09-20 16:00:55 +02:00
Mark Nelson
d0e75bb6fc Updated CHANGES.md 2020-09-20 15:55:05 +02:00
Mark Nelson
2b210b4bb8 Fixed issue with PDF being generated without a name (#333) 2020-09-20 15:41:05 +02:00
Mark Nelson
4765c11a46 Fixed exception loading template image that has no image selected (#369) 2020-09-20 14:58:18 +02:00
Mark Nelson
03d56eddae Do not email those who can manage the certificate (#376) 2020-09-20 14:29:31 +02:00
Guillermo Gomez
d37bfab4cd Implement get_objectid_mapping (#374) 2020-09-20 14:17:08 +02:00
Mark Nelson
d8f275d588 Updated CHANGES.md 2020-08-08 19:38:57 +02:00
Mark Nelson
616bc4f43b Changed 'enrollment' to 'enrolment' to conform to Moodle core (#328)
Also minor changes like ordering strings alphabetically and adding
full-stops.
2020-08-08 19:36:17 +02:00
Sergey Evstegneiev
b3433e598c Adding enrollment start & end date options (#328) 2020-08-08 19:35:50 +02:00
Mark Nelson
05ba53ad3a Fix stale file warning 2020-08-08 18:11:04 +02:00
Mark Nelson
545ed8bbbd Ignore multiple certificate codes (#363) 2020-08-08 15:53:00 +02:00
Mark Nelson
2697ba9fb5 Remove trailing spaces 2020-08-08 15:47:00 +02:00
Thong Bui
4447e2ac6f Fix get position when browser shrinking (#343) 2020-08-08 15:28:28 +02:00
Mark Nelson
4a85da7297 Updated CHANGES.md 2020-06-28 18:37:50 +02:00
Mark Nelson
112a027849 Mark certificates as viewed in mobile app (#342) 2020-05-30 13:10:18 +02:00
Mark Nelson
feb3bac856 Refactor element_helper::get_grade_items() to be nicer to read 2020-05-08 21:24:10 +02:00
Mark Nelson
88d0449d62 Fix docs in Grade element name 2020-05-08 17:50:36 +02:00
Mark Nelson
ba0bfd3a7e The Grade item name element works with all grade items (#346) 2020-05-08 17:27:46 +02:00
Mark Nelson
8d3d78307f Extend unit test (#329) 2020-05-08 17:23:14 +02:00
Mark Nelson
6ba2efa3b0 Add the ability to select Outcomes in the Grade element (#329) 2020-05-08 17:23:08 +02:00
Mark Nelson
a7d372a26a Removed 'exampledata' string 2020-05-08 16:57:28 +02:00
Mark Nelson
14e480f7e9 Bumped version 2020-03-17 00:21:51 +01:00
Mark Nelson
a379c93dd7 Fixed broken logic (#298) 2020-03-16 17:52:19 +01:00
Mark Nelson
185389b983 Loading templates copies system images to the course (#298)
Before it would just reference the system images. However, this meant
backup/restore was broken as it did not include the images the template
was using.
2020-03-15 19:53:05 +01:00
Mark Nelson
3dc83cb306 Updated CHANGES.md 2020-03-12 14:54:58 +01:00
Mark Nelson
a2c30e3667 Added extra Behat steps for new elements (#309) 2020-03-11 14:18:42 +01:00
Mark Nelson
ad7d4099b4 Avoid use of old array syntax (#331) 2020-03-10 17:41:30 +01:00
Sergey
1c9f4786fb Fix of showing names of custom user fields (#326)
Show normal human-readable name of custom user profile fields instead of internal shortname
2020-03-09 14:57:35 +01:00
Mark Nelson
2f8bb1eb29 Do not allow '0' as a value for width or height in QR code (#321) 2020-03-09 14:53:11 +01:00
Mark Nelson
01bfe2f660 Fix foreign key violation (#331) 2020-03-09 14:32:10 +01:00
Mark Nelson
28a8b99f31 Bumped version 2020-02-01 16:00:06 +01:00
Mark Nelson
9810b48b25 Updated CHANGES.md 2020-02-01 15:55:22 +01:00
Mark Nelson
28b2210b19 Fixed typo in Behat file name 2020-02-01 15:48:42 +01:00
Mark Nelson
62639d1754 Re-add code column (#264) 2020-02-01 15:48:08 +01:00
mwithheld
e7352bf715 Do not fail if multiple certificate issues (#304)
Fix another location of this problem.
2019-12-16 16:18:24 +01:00
mwithheld
b28877f5ee Do not fail if multiple certificate issues (#295)
Ignore multiple matches for a user-customcert combo in the customcert_issues table.
In our case, the scheduled task died when:
$issueid = $DB->get_field('customcert_issues', 'id', array('userid' => $enroluser->id, 'customcertid' => $customcert->id));
returned 2 rows, which shouldn't happen, but did anyway.

This means in rare circumstances (i.e. the race condition) the user may get multiple certificates (e.g. one via the UI, one by email) but that is better than not getting one at all and the whole job dying.
2019-12-16 16:18:12 +01:00
Mark Nelson
bc2fe29354 Changed references to old github name 2019-12-16 15:11:36 +01:00
Arnaud Trouvé
74f1cd732d Add userfullname variable for email subject (#316) 2019-12-16 15:01:52 +01:00
Mark Nelson
9d97d7e6fb Bumped version 2019-06-17 17:08:06 +08:00
Mark Nelson
bd91807fe2 Updated CHANGES.md 2019-06-17 14:40:59 +08:00
Mark Nelson
88ef8f25fa Add ability to specify the current date for date related elements (#289) 2019-06-17 13:34:56 +08:00
Mark Nelson
c420d3b943 String improvements for date range element
Also added a help button to reduce the wall
of text.
2019-06-17 12:42:04 +08:00
Mark Nelson
5a6993009b Fixed PHPDoc reference to edit_element_form 2019-06-17 12:16:05 +08:00
Mark Nelson
08b10ebdad Use negative numbers for constants in date range element
The reason being that we may have a module that has
an id matching one of these values.
2019-06-17 11:30:58 +08:00
Mark Nelson
1511c079ab Specify OS in travis 2019-05-29 00:19:47 +08:00
Mark Nelson
a26d58bf33 Fix complaints found by local_moodlecheck 2019-05-29 00:13:17 +08:00
39 changed files with 732 additions and 199 deletions

View file

@ -2,6 +2,7 @@ language: php
# For javascript behat tests we need sudo # For javascript behat tests we need sudo
sudo: true sudo: true
dist: trusty
cache: cache:
directories: directories:
@ -32,7 +33,7 @@ before_install:
- nvm install 8.9 - nvm install 8.9
- nvm use 8.9 - nvm use 8.9
- cd ../.. - cd ../..
- composer create-project -n --no-dev --prefer-dist blackboard-open-source/moodle-plugin-ci ci ^2 - composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3
- export PATH="$(cd ci/bin; pwd):$(cd ci/vendor/bin; pwd):$PATH" - export PATH="$(cd ci/bin; pwd):$(cd ci/vendor/bin; pwd):$PATH"
install: install:

View file

@ -2,7 +2,85 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
Note - All hash comments refer to the issue number. Eg. #169 refers to https://github.com/markn86/moodle-mod_customcert/issues/169. Note - All hash comments refer to the issue number. Eg. #169 refers to https://github.com/mdjnelson/moodle-mod_customcert/issues/169.
## [3.6.7] - 2020-11-26
### Added
- Added ability to select outcomes in the Grade element (#329).
- The Grade Item Name element now works with all grade items, whereas before it was just activities (#346).
- Added enrolment start and end dates to the date element (#328).
- Added username to userfield form element (#390).
### Changed
- Removed unnecessary and confusing 'exampledata' string.
- Do not email those who can manage the certificate (#376).
- Do not force the PDF to be downloaded, instead send the file inline to the browser (#153).
- Updated the 'emailstudents_help', 'emailteachers_help' and 'emailothers_help' strings to warn users about prematurely emailing the certificate (#276).
- Do not email out certificates that contain no elements (#276).
### Fixed
- Certificates now get marked as viewed via the mobile app (#342).
- Fix repositioning elements page when resizing the browser (#343).
- Prevent error when duplicate issues exist when using the code element (#363).
- Implemented get_objectid_mapping for the course_module_viewed.php event to avoid warning (#374).
- Fixed exception being thrown when loading a template that has an image element but no image selected (#369).
- Fixed issue with PDF being generated without a name (#333).
## [3.6.6] - 2020-03-12
### Added
- Added extra Behat steps for new elements (#309).
### Changed
- When copying a site template the site images are also copied to the course context and then those copied images are used.
Before, the elements would simply point to the site images. However, this meant when performing a backup/restore the
images were not stored in the backup file (#298).
### Fixed
- Fixed the displaying of names of a custom user field (#326).
- Do not allow '0' as a value for width or height in QR code (#321).
## [3.6.5] - 2020-03-09
### Fixed
- Fixed foreign key violation (#331).
## [3.6.4] - 2020-02-01
### Added
- Re-added 'code' column to user report (#264).
- Add 'userfullname' variable for email subject (#316).
### Fixed
- Do not fail if multiple certificate issues (#304) and (#295).
## [3.6.3] - 2019-06-17
### Added
- Added ability to specify the current date for date related elements (#289).
### Changed
- String improvements for the 'Date range' element.
### Fixed
- Use negative numbers for constants in the 'Date range' element. The reason being that we may have a module
that has an id matching one of these positive values. Sites which are using the 'Date range' element (sites
which are **not** using this element do **not** have to do anything) will need to re-edit each element, select
the date item again and save. An upgrade step was not created because it is impossible to tell if the site does
actually want the constant or if they actually want the date for the module.
## [3.6.2] - 2019-05-28 ## [3.6.2] - 2019-05-28

View file

@ -15,7 +15,7 @@ This requires Git being installed. If you do not have Git installed, please visi
Once you have Git installed, simply visit your Moodle mod directory and clone the repository using the following command. Once you have Git installed, simply visit your Moodle mod directory and clone the repository using the following command.
``` ```
git clone https://github.com/markn86/moodle-mod_customcert.git customcert git clone https://github.com/mdjnelson/moodle-mod_customcert.git customcert
``` ```
Then checkout the branch corresponding to the version of Moodle you are using with the following command. Make sure to replace MOODLE_32_STABLE with the version of Moodle you are using. Then checkout the branch corresponding to the version of Moodle you are using with the following command. Make sure to replace MOODLE_32_STABLE with the version of Moodle you are using.

View file

@ -47,18 +47,18 @@ class element_factory {
$classname = '\\customcertelement_' . $element->element . '\\element'; $classname = '\\customcertelement_' . $element->element . '\\element';
$data = new \stdClass(); $data = new \stdClass();
$data->id = isset($element->id) ? $element->id : null; $data->id = $element->id ?? null;
$data->pageid = isset($element->pageid) ? $element->pageid : null; $data->pageid = $element->pageid ?? null;
$data->name = isset($element->name) ? $element->name : get_string('pluginname', 'customcertelement_' . $element->element); $data->name = $element->name ?? get_string('pluginname', 'customcertelement_' . $element->element);
$data->element = $element->element; $data->element = $element->element;
$data->data = isset($element->data) ? $element->data : null; $data->data = $element->data ?? null;
$data->font = isset($element->font) ? $element->font : null; $data->font = $element->font ?? null;
$data->fontsize = isset($element->fontsize) ? $element->fontsize : null; $data->fontsize = $element->fontsize ?? null;
$data->colour = isset($element->colour) ? $element->colour : null; $data->colour = $element->colour ?? null;
$data->posx = isset($element->posx) ? $element->posx : null; $data->posx = $element->posx ?? null;
$data->posy = isset($element->posy) ? $element->posy : null; $data->posy = $element->posy ?? null;
$data->width = isset($element->width) ? $element->width : null; $data->width = $element->width ?? null;
$data->refpoint = isset($element->refpoint) ? $element->refpoint : null; $data->refpoint = $element->refpoint ?? null;
// Ensure the necessary class exists. // Ensure the necessary class exists.
if (class_exists($classname)) { if (class_exists($classname)) {

View file

@ -462,70 +462,40 @@ class element_helper {
* @return array the array of gradeable items in the course * @return array the array of gradeable items in the course
*/ */
public static function get_grade_items($course) { public static function get_grade_items($course) {
global $DB;
// Array to store the grade items. // Array to store the grade items.
$modules = array(); $arrgradeitems = array();
// Collect course modules data.
$modinfo = get_fast_modinfo($course);
$mods = $modinfo->get_cms();
$sections = $modinfo->get_section_info_all();
// Create the section label depending on course format.
$sectionlabel = get_string('section');
if ($course->format == 'topics') {
$sectionlabel = get_string('topic');
} else if ($course->format == 'weeks') {
$sectionlabel = get_string('week');
}
// Loop through each course section.
for ($i = 0; $i <= count($sections) - 1; $i++) {
// Confirm the index exists, should always be true.
if (isset($sections[$i])) {
// Get the individual section.
$section = $sections[$i];
// Get the mods for this section.
$sectionmods = explode(",", $section->sequence);
// Loop through the section mods.
foreach ($sectionmods as $sectionmod) {
// Should never happen unless DB is borked.
if (empty($mods[$sectionmod])) {
continue;
}
$mod = $mods[$sectionmod];
$instance = $DB->get_record($mod->modname, array('id' => $mod->instance));
// Get the grade items for this activity.
if ($gradeitems = grade_get_grade_items_for_activity($mod)) {
$moditem = grade_get_grades($course->id, 'mod', $mod->modname, $mod->instance);
$gradeitem = reset($moditem->items);
if (isset($gradeitem->grademax)) {
$modules[$mod->id] = $sectionlabel . ' ' . $section->section . ' : ' . $instance->name;
}
}
}
}
}
// Get other non-module related grade items.
if ($gradeitems = \grade_item::fetch_all(['courseid' => $course->id])) { if ($gradeitems = \grade_item::fetch_all(['courseid' => $course->id])) {
$arrgradeitems = [];
foreach ($gradeitems as $gi) { foreach ($gradeitems as $gi) {
// Skip the course and mod items since we already have them. if ($gi->is_course_item()) {
if ($gi->itemtype == 'mod' || $gi->itemtype == 'course') { continue; // Skipping for legacy reasons - this was added to individual elements.
continue; }
if ($gi->is_external_item()) {
$cm = get_coursemodule_from_instance($gi->itemmodule, $gi->iteminstance, $course->id);
$modcontext = \context_module::instance($cm->id);
$modname = format_string($cm->name, true, array('context' => $modcontext));
}
if ($gi->is_external_item() && !$gi->is_outcome_item()) {
// Due to legacy reasons we are storing the course module ID here rather than the grade item id.
// If we were to change we would need to provide upgrade steps to convert cm->id to gi->id.
$arrgradeitems[$cm->id] = get_string('activity', 'mod_customcert') . ' : ' . $gi->get_name();
} else if ($gi->is_external_item() && $gi->is_outcome_item()) {
// Get the name of the activity.
$optionname = get_string('gradeoutcome', 'mod_customcert') . ' : ' . $modname . " - " . $gi->get_name();
$arrgradeitems['gradeitem:' . $gi->id] = $optionname;
} else {
$arrgradeitems['gradeitem:' . $gi->id] = get_string('gradeitem', 'grades') . ' : ' . $gi->get_name(true);
} }
$arrgradeitems['gradeitem:' . $gi->id] = get_string('gradeitem', 'grades') . ' : ' . $gi->get_name(true);
} }
// Alphabetise this. // Alphabetise this.
asort($arrgradeitems); asort($arrgradeitems);
// Merge results.
$modules = $modules + $arrgradeitems;
} }
return $modules; return $arrgradeitems;
} }
/** /**

View file

@ -42,4 +42,13 @@ class course_module_viewed extends \core\event\course_module_viewed {
$this->data['objecttable'] = 'customcert'; $this->data['objecttable'] = 'customcert';
parent::init(); parent::init();
} }
public static function get_objectid_mapping() {
return array('db' => 'customcert', 'restore' => 'customcert');
}
public static function get_other_mapping() {
// No need to map.
return false;
}
} }

View file

@ -74,6 +74,7 @@ class report_table extends \table_sql {
$columns[] = $extrafield; $columns[] = $extrafield;
} }
$columns[] = 'timecreated'; $columns[] = 'timecreated';
$columns[] = 'code';
$headers = []; $headers = [];
$headers[] = get_string('fullname'); $headers[] = get_string('fullname');
@ -81,6 +82,7 @@ class report_table extends \table_sql {
$headers[] = get_user_field_name($extrafield); $headers[] = get_user_field_name($extrafield);
} }
$headers[] = get_string('receiveddate', 'customcert'); $headers[] = get_string('receiveddate', 'customcert');
$headers[] = get_string('code', 'customcert');
// Check if we were passed a filename, which means we want to download it. // Check if we were passed a filename, which means we want to download it.
if ($download) { if ($download) {
@ -101,6 +103,7 @@ class report_table extends \table_sql {
$this->define_headers($headers); $this->define_headers($headers);
$this->collapsible(false); $this->collapsible(false);
$this->sortable(true); $this->sortable(true);
$this->no_sorting('code');
$this->no_sorting('download'); $this->no_sorting('download');
$this->is_downloadable(true); $this->is_downloadable(true);
@ -135,6 +138,16 @@ class report_table extends \table_sql {
return userdate($user->timecreated); return userdate($user->timecreated);
} }
/**
* Generate the code column.
*
* @param \stdClass $user
* @return string
*/
public function col_code($user) {
return $user->code;
}
/** /**
* Generate the download column. * Generate the download column.
* *

View file

@ -69,6 +69,18 @@ class email_certificate_task extends \core\task\scheduled_task {
$htmlrenderer = $PAGE->get_renderer('mod_customcert', 'email', 'htmlemail'); $htmlrenderer = $PAGE->get_renderer('mod_customcert', 'email', 'htmlemail');
$textrenderer = $PAGE->get_renderer('mod_customcert', 'email', 'textemail'); $textrenderer = $PAGE->get_renderer('mod_customcert', 'email', 'textemail');
foreach ($customcerts as $customcert) { foreach ($customcerts as $customcert) {
// Do not process an empty certificate.
$sql = "SELECT ce.*
FROM {customcert_elements} ce
JOIN {customcert_pages} cp
ON cp.id = ce.pageid
JOIN {customcert_templates} ct
ON ct.id = cp.templateid
WHERE ct.contextid = :contextid";
if (!$DB->record_exists_sql($sql, ['contextid' => $customcert->contextid])) {
continue;
}
// Get the context. // Get the context.
$context = \context::instance_by_id($customcert->contextid); $context = \context::instance_by_id($customcert->contextid);
@ -112,6 +124,11 @@ class email_certificate_task extends \core\task\scheduled_task {
continue; continue;
} }
// Don't want to email those with the capability to manage the certificate.
if (has_capability('mod/customcert:manage', $context, $enroluser->id)) {
continue;
}
// Only email those with the capability to receive the certificate. // Only email those with the capability to receive the certificate.
if (!has_capability('mod/customcert:receiveissue', $context, $enroluser->id)) { if (!has_capability('mod/customcert:receiveissue', $context, $enroluser->id)) {
continue; continue;
@ -127,7 +144,7 @@ class email_certificate_task extends \core\task\scheduled_task {
// Ensure the cert hasn't already been issued, e.g via the UI (view.php) - a race condition. // Ensure the cert hasn't already been issued, e.g via the UI (view.php) - a race condition.
$issueid = $DB->get_field('customcert_issues', 'id', $issueid = $DB->get_field('customcert_issues', 'id',
array('userid' => $enroluser->id, 'customcertid' => $customcert->id)); array('userid' => $enroluser->id, 'customcertid' => $customcert->id), IGNORE_MULTIPLE);
if (empty($issueid)) { if (empty($issueid)) {
// Ok, issue them the certificate. // Ok, issue them the certificate.
$issueid = \mod_customcert\certificate::issue_certificate($customcert->id, $enroluser->id); $issueid = \mod_customcert\certificate::issue_certificate($customcert->id, $enroluser->id);
@ -160,6 +177,7 @@ class email_certificate_task extends \core\task\scheduled_task {
// Now, email the people we need to. // Now, email the people we need to.
foreach ($issuedusers as $user) { foreach ($issuedusers as $user) {
$userfullname = fullname($user); $userfullname = fullname($user);
$info->userfullname = $userfullname;
// Now, get the PDF. // Now, get the PDF.
$template = new \stdClass(); $template = new \stdClass();

View file

@ -283,6 +283,16 @@ class template {
$pdf->SetAutoPageBreak(true, 0); $pdf->SetAutoPageBreak(true, 0);
// Remove full-stop at the end, if it exists, to avoid "..pdf" being created and being filtered by clean_filename. // Remove full-stop at the end, if it exists, to avoid "..pdf" being created and being filtered by clean_filename.
$filename = rtrim($this->name, '.'); $filename = rtrim($this->name, '.');
// This is the logic the TCPDF library uses when processing the name. This makes names
// such as 'الشهادة' become empty, so set a default name in these cases.
$filename = preg_replace('/[\s]+/', '_', $filename);
$filename = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $filename);
if (empty($filename)) {
$filename = get_string('certificate', 'customcert');
}
$filename = clean_filename($filename . '.pdf'); $filename = clean_filename($filename . '.pdf');
// Loop through the pages and display their content. // Loop through the pages and display their content.
foreach ($pages as $page) { foreach ($pages as $page) {
@ -305,10 +315,12 @@ class template {
} }
} }
} }
if ($return) { if ($return) {
return $pdf->Output('', 'S'); return $pdf->Output('', 'S');
} }
$pdf->Output($filename, 'D');
$pdf->Output($filename, 'I');
} }
} }

View file

@ -1,5 +1,5 @@
{ {
"name": "markn86/moodle-mod_customcert", "name": "mdjnelson/moodle-mod_customcert",
"type": "moodle-mod", "type": "moodle-mod",
"require": { "require": {
"composer/installers": "~1.0" "composer/installers": "~1.0"

View file

@ -23,7 +23,7 @@
</FIELDS> </FIELDS>
<KEYS> <KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Primary key for customcert"/> <KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Primary key for customcert"/>
<KEY NAME="template" TYPE="foreign" FIELDS="templateid" REFTABLE="customcert_template" REFFIELDS="id"/> <KEY NAME="template" TYPE="foreign" FIELDS="templateid" REFTABLE="customcert_templates" REFFIELDS="id"/>
</KEYS> </KEYS>
</TABLE> </TABLE>
<TABLE NAME="customcert_templates" COMMENT="Stores each customcert template"> <TABLE NAME="customcert_templates" COMMENT="Stores each customcert template">
@ -67,7 +67,7 @@
</FIELDS> </FIELDS>
<KEYS> <KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Primary key for customcert_pages"/> <KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Primary key for customcert_pages"/>
<KEY NAME="template" TYPE="foreign" FIELDS="templateid" REFTABLE="customcert_template" REFFIELDS="id"/> <KEY NAME="template" TYPE="foreign" FIELDS="templateid" REFTABLE="customcert_templates" REFFIELDS="id"/>
</KEYS> </KEYS>
</TABLE> </TABLE>
<TABLE NAME="customcert_elements" COMMENT="Stores the elements for a given page"> <TABLE NAME="customcert_elements" COMMENT="Stores the elements for a given page">

View file

@ -30,7 +30,7 @@ $addons = [
'issueview' => [ // Handler unique name. 'issueview' => [ // Handler unique name.
'displaydata' => [ 'displaydata' => [
'icon' => $CFG->wwwroot . '/mod/customcert/pix/icon.png', 'icon' => $CFG->wwwroot . '/mod/customcert/pix/icon.png',
'class' => '', 'class' => 'core-course-module-customcert-handler',
], ],
'delegate' => 'CoreCourseModuleDelegate', // Delegate (where to display the link to the plugin). 'delegate' => 'CoreCourseModuleDelegate', // Delegate (where to display the link to the plugin).
'method' => 'mobile_view_activity', // Main function in \mod_customcert\output\mobile. 'method' => 'mobile_view_activity', // Main function in \mod_customcert\output\mobile.

View file

@ -146,5 +146,25 @@ function xmldb_customcert_upgrade($oldversion) {
upgrade_mod_savepoint(true, 2018051705, 'customcert'); upgrade_mod_savepoint(true, 2018051705, 'customcert');
} }
if ($oldversion < 2018120305) {
$table = new xmldb_table('customcert');
$index = new xmldb_index('templateid', XMLDB_INDEX_UNIQUE, ['templateid']);
if ($dbman->index_exists($table, $index)) {
$dbman->drop_index($table, $index);
}
$key = new xmldb_key('templateid', XMLDB_KEY_FOREIGN, ['templateid'], 'customcert_templates', ['id']);
$dbman->add_key($table, $key);
$table = new xmldb_table('customcert_pages');
$index = new xmldb_index('templateid', XMLDB_INDEX_UNIQUE, ['templateid']);
if ($dbman->index_exists($table, $index)) {
$dbman->drop_index($table, $index);
}
$key = new xmldb_key('templateid', XMLDB_KEY_FOREIGN, ['templateid'], 'customcert_templates', ['id']);
$dbman->add_key($table, $key);
upgrade_mod_savepoint(true, 2018120305, 'customcert');
}
return true; return true;
} }

View file

@ -54,7 +54,7 @@ class element extends \mod_customcert\element {
$customcert = $DB->get_record('customcert', array('templateid' => $page->templateid), '*', MUST_EXIST); $customcert = $DB->get_record('customcert', array('templateid' => $page->templateid), '*', MUST_EXIST);
// Now we can get the issue for this user. // Now we can get the issue for this user.
$issue = $DB->get_record('customcert_issues', array('userid' => $user->id, 'customcertid' => $customcert->id), $issue = $DB->get_record('customcert_issues', array('userid' => $user->id, 'customcertid' => $customcert->id),
'*', MUST_EXIST); '*', IGNORE_MULTIPLE);
$code = $issue->code; $code = $issue->code;
} }

View file

@ -51,6 +51,21 @@ define('CUSTOMCERT_DATE_COURSE_START', '-3');
*/ */
define('CUSTOMCERT_DATE_COURSE_END', '-4'); define('CUSTOMCERT_DATE_COURSE_END', '-4');
/**
* Date - Current date
*/
define('CUSTOMCERT_DATE_CURRENT_DATE', '-5');
/**
* Date - Enrollment start
*/
define('CUSTOMCERT_DATE_ENROLMENT_START', '-6');
/**
* Date - Entrollment end
*/
define('CUSTOMCERT_DATE_ENROLMENT_END', '-7');
require_once($CFG->dirroot . '/lib/grade/constants.php'); require_once($CFG->dirroot . '/lib/grade/constants.php');
/** /**
@ -73,10 +88,14 @@ class element extends \mod_customcert\element {
// Get the possible date options. // Get the possible date options.
$dateoptions = array(); $dateoptions = array();
$dateoptions[CUSTOMCERT_DATE_ISSUE] = get_string('issueddate', 'customcertelement_date'); $dateoptions[CUSTOMCERT_DATE_ISSUE] = get_string('issueddate', 'customcertelement_date');
$dateoptions[CUSTOMCERT_DATE_CURRENT_DATE] = get_string('currentdate', 'customcertelement_date');
$completionenabled = $CFG->enablecompletion && ($COURSE->id == SITEID || $COURSE->enablecompletion); $completionenabled = $CFG->enablecompletion && ($COURSE->id == SITEID || $COURSE->enablecompletion);
if ($completionenabled) { if ($completionenabled) {
$dateoptions[CUSTOMCERT_DATE_COMPLETION] = get_string('completiondate', 'customcertelement_date'); $dateoptions[CUSTOMCERT_DATE_COMPLETION] = get_string('completiondate', 'customcertelement_date');
} }
$dateoptions[CUSTOMCERT_DATE_ENROLMENT_START] = get_string('enrolmentstartdate', 'customcertelement_date');
$dateoptions[CUSTOMCERT_DATE_ENROLMENT_END] = get_string('enrolmentenddate', 'customcertelement_date');
$dateoptions[CUSTOMCERT_DATE_COURSE_START] = get_string('coursestartdate', 'customcertelement_date'); $dateoptions[CUSTOMCERT_DATE_COURSE_START] = get_string('coursestartdate', 'customcertelement_date');
$dateoptions[CUSTOMCERT_DATE_COURSE_END] = get_string('courseenddate', 'customcertelement_date'); $dateoptions[CUSTOMCERT_DATE_COURSE_END] = get_string('courseenddate', 'customcertelement_date');
$dateoptions[CUSTOMCERT_DATE_COURSE_GRADE] = get_string('coursegradedate', 'customcertelement_date'); $dateoptions[CUSTOMCERT_DATE_COURSE_GRADE] = get_string('coursegradedate', 'customcertelement_date');
@ -141,10 +160,12 @@ class element extends \mod_customcert\element {
$customcert = $DB->get_record('customcert', array('templateid' => $page->templateid), '*', MUST_EXIST); $customcert = $DB->get_record('customcert', array('templateid' => $page->templateid), '*', MUST_EXIST);
// Now we can get the issue for this user. // Now we can get the issue for this user.
$issue = $DB->get_record('customcert_issues', array('userid' => $user->id, 'customcertid' => $customcert->id), $issue = $DB->get_record('customcert_issues', array('userid' => $user->id, 'customcertid' => $customcert->id),
'*', MUST_EXIST); '*', IGNORE_MULTIPLE);
if ($dateitem == CUSTOMCERT_DATE_ISSUE) { if ($dateitem == CUSTOMCERT_DATE_ISSUE) {
$date = $issue->timecreated; $date = $issue->timecreated;
} else if ($dateitem == CUSTOMCERT_DATE_CURRENT_DATE) {
$date = time();
} else if ($dateitem == CUSTOMCERT_DATE_COMPLETION) { } else if ($dateitem == CUSTOMCERT_DATE_COMPLETION) {
// Get the last completion date. // Get the last completion date.
$sql = "SELECT MAX(c.timecompleted) as timecompleted $sql = "SELECT MAX(c.timecompleted) as timecompleted
@ -156,6 +177,26 @@ class element extends \mod_customcert\element {
$date = $timecompleted->timecompleted; $date = $timecompleted->timecompleted;
} }
} }
} else if ($dateitem == CUSTOMCERT_DATE_ENROLMENT_START) {
// Get the enrolment start date.
$sql = "SELECT ue.timestart FROM {enrol} e JOIN {user_enrolments} ue ON ue.enrolid = e.id
WHERE e.courseid = :courseid
AND ue.userid = :userid";
if ($timestart = $DB->get_record_sql($sql, array('userid' => $issue->userid, 'courseid' => $courseid))) {
if (!empty($timestart->timestart)) {
$date = $timestart->timestart;
}
}
} else if ($dateitem == CUSTOMCERT_DATE_ENROLMENT_END) {
// Get the enrolment end date.
$sql = "SELECT ue.timeend FROM {enrol} e JOIN {user_enrolments} ue ON ue.enrolid = e.id
WHERE e.courseid = :courseid
AND ue.userid = :userid";
if ($timeend = $DB->get_record_sql($sql, array('userid' => $issue->userid, 'courseid' => $courseid))) {
if (!empty($timeend->timeend)) {
$date = $timeend->timeend;
}
}
} else if ($dateitem == CUSTOMCERT_DATE_COURSE_START) { } else if ($dateitem == CUSTOMCERT_DATE_COURSE_START) {
$date = $DB->get_field('course', 'startdate', array('id' => $courseid)); $date = $DB->get_field('course', 'startdate', array('id' => $courseid));
} else if ($dateitem == CUSTOMCERT_DATE_COURSE_END) { } else if ($dateitem == CUSTOMCERT_DATE_COURSE_END) {
@ -190,12 +231,7 @@ class element extends \mod_customcert\element {
// Ensure that a date has been set. // Ensure that a date has been set.
if (!empty($date)) { if (!empty($date)) {
$date = $this->get_date_format_string($date, $dateformat); \mod_customcert\element_helper::render_content($pdf, $this, $this->get_date_format_string($date, $dateformat));
// If we are previewing, we want to let the user know it's an example date so they don't get confused.
if ($preview) {
$date = get_string('exampledata', 'customcert', 'date') . ' ' . $date;
}
\mod_customcert\element_helper::render_content($pdf, $this, $date);
} }
} }

View file

@ -26,6 +26,9 @@ $string['completiondate'] = 'Completion date';
$string['courseenddate'] = 'Course end date'; $string['courseenddate'] = 'Course end date';
$string['coursegradedate'] = 'Course grade date'; $string['coursegradedate'] = 'Course grade date';
$string['coursestartdate'] = 'Course start date'; $string['coursestartdate'] = 'Course start date';
$string['enrolmentenddate'] = 'Enrolment end date';
$string['enrolmentstartdate'] = 'Enrolment start date';
$string['currentdate'] = 'Current date';
$string['dateformat'] = 'Date format'; $string['dateformat'] = 'Date format';
$string['dateformat_help'] = 'This is the format of the date that will be displayed'; $string['dateformat_help'] = 'This is the format of the date that will be displayed';
$string['dateitem'] = 'Date item'; $string['dateitem'] = 'Date item';

View file

@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
$plugin->version = 2018120300; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2018120301; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2018120300; // Requires this Moodle version (3.6). $plugin->requires = 2018120300; // Requires this Moodle version (3.6).
$plugin->component = 'customcertelement_date'; $plugin->component = 'customcertelement_date';

View file

@ -77,32 +77,37 @@ class element extends \mod_customcert\element {
/** /**
* Date - Issue * Date - Issue
*/ */
const DATE_ISSUE = 1; const DATE_ISSUE = -1;
/** /**
* Date - Completion * Date - Completion
*/ */
const DATE_COMPLETION = 2; const DATE_COMPLETION = -2;
/** /**
* Date - Course start * Date - Course start
*/ */
const DATE_COURSE_START = 3; const DATE_COURSE_START = -3;
/** /**
* Date - Course end * Date - Course end
*/ */
const DATE_COURSE_END = 4; const DATE_COURSE_END = -4;
/** /**
* Date - Course grade date * Date - Course grade date
*/ */
const DATE_COURSE_GRADE = 5; const DATE_COURSE_GRADE = -5;
/**
* Date - Course current date
*/
const DATE_CURRENT_DATE = -6;
/** /**
* This function renders the form elements when adding a customcert element. * This function renders the form elements when adding a customcert element.
* *
* @param \mod_customcert\edit_element_form $mform the edit_form instance * @param \MoodleQuickForm $mform the edit form instance
*/ */
public function render_form_elements($mform) { public function render_form_elements($mform) {
global $COURSE; global $COURSE;
@ -110,6 +115,7 @@ class element extends \mod_customcert\element {
// Get the possible date options. // Get the possible date options.
$dateoptions = array(); $dateoptions = array();
$dateoptions[self::DATE_ISSUE] = get_string('issueddate', 'customcertelement_daterange'); $dateoptions[self::DATE_ISSUE] = get_string('issueddate', 'customcertelement_daterange');
$dateoptions[self::DATE_CURRENT_DATE] = get_string('currentdate', 'customcertelement_daterange');
$dateoptions[self::DATE_COMPLETION] = get_string('completiondate', 'customcertelement_daterange'); $dateoptions[self::DATE_COMPLETION] = get_string('completiondate', 'customcertelement_daterange');
$dateoptions[self::DATE_COURSE_START] = get_string('coursestartdate', 'customcertelement_daterange'); $dateoptions[self::DATE_COURSE_START] = get_string('coursestartdate', 'customcertelement_daterange');
$dateoptions[self::DATE_COURSE_END] = get_string('courseenddate', 'customcertelement_daterange'); $dateoptions[self::DATE_COURSE_END] = get_string('courseenddate', 'customcertelement_daterange');
@ -178,6 +184,7 @@ class element extends \mod_customcert\element {
$rangeoptions['startdate']['type'] = PARAM_INT; $rangeoptions['startdate']['type'] = PARAM_INT;
$rangeoptions['enddate']['type'] = PARAM_INT; $rangeoptions['enddate']['type'] = PARAM_INT;
$rangeoptions['recurring']['type'] = PARAM_INT; $rangeoptions['recurring']['type'] = PARAM_INT;
$rangeoptions['recurring']['helpbutton'] = ['recurring', 'customcertelement_daterange'];
$rangeoptions['datestring']['type'] = PARAM_NOTAGS; $rangeoptions['datestring']['type'] = PARAM_NOTAGS;
$rangeoptions['rangedelete']['type'] = PARAM_BOOL; $rangeoptions['rangedelete']['type'] = PARAM_BOOL;
@ -209,7 +216,7 @@ class element extends \mod_customcert\element {
/** /**
* Sets the data on the form when editing an element. * Sets the data on the form when editing an element.
* *
* @param \mod_customcert\edit_element_form $mform the edit_form instance * @param \MoodleQuickForm $mform the edit form instance
*/ */
public function definition_after_data($mform) { public function definition_after_data($mform) {
if (!empty($this->get_data()) && !$mform->isSubmitted()) { if (!empty($this->get_data()) && !$mform->isSubmitted()) {
@ -345,6 +352,10 @@ class element extends \mod_customcert\element {
$date = $issue->timecreated; $date = $issue->timecreated;
break; break;
case self::DATE_CURRENT_DATE:
$date = time();
break;
case self::DATE_COMPLETION: case self::DATE_COMPLETION:
// Get the last completion date. // Get the last completion date.
$sql = "SELECT MAX(c.timecompleted) as timecompleted $sql = "SELECT MAX(c.timecompleted) as timecompleted
@ -442,6 +453,8 @@ class element extends \mod_customcert\element {
} }
/** /**
* Returns whether or not a range is recurring.
*
* @param \stdClass $range Range object. * @param \stdClass $range Range object.
* *
* @return bool * @return bool
@ -597,6 +610,7 @@ class element extends \mod_customcert\element {
/** /**
* Format date string based on different types of placeholders. * Format date string based on different types of placeholders.
* *
* @param string $datestring The date string
* @param array $formatdata A list of format data. * @param array $formatdata A list of format data.
* *
* @return string * @return string

View file

@ -24,28 +24,30 @@
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
$string['addrange'] = 'Add another range';
$string['completiondate'] = 'Completion date'; $string['completiondate'] = 'Completion date';
$string['courseenddate'] = 'Course end date'; $string['courseenddate'] = 'Course end date';
$string['coursegradedate'] = 'Course grade date'; $string['coursegradedate'] = 'Course grade date';
$string['coursestartdate'] = 'Course start date'; $string['coursestartdate'] = 'Course start date';
$string['currentdate'] = 'Current date';
$string['dateitem'] = 'Date item'; $string['dateitem'] = 'Date item';
$string['dateitem_help'] = 'This will be the date that is printed on the certificate'; $string['dateitem_help'] = 'This will be the date that is printed on the certificate';
$string['dateranges'] = 'Date ranges'; $string['dateranges'] = 'Date ranges';
$string['datestring'] = 'String';
$string['end'] = 'End';
$string['error:atleastone'] = 'You must have at least one date range configured';
$string['error:datestring'] = 'You must provide string representation for the date range';
$string['error:enddate'] = 'End date must occur after the start date';
$string['error:recurring'] = 'Recurring range must not be longer than 12 months';
$string['fallbackstring'] = 'Fallback string'; $string['fallbackstring'] = 'Fallback string';
$string['fallbackstring_help'] = 'This string will be displayed if no date range applies to a date. If the fallback string is not set, then there will be no output at all.'; $string['fallbackstring_help'] = 'This string will be displayed if no date range applies to a date. If the fallback string is not set, then there will be no output at all.';
$string['help'] = 'Configure a string representation for each date range. Make sure your ranges do not overlap, otherwise the first matched date range will be applied. If no date range matches then the fallback string will be displayed (if it is set).<br/><br /> If you mark a date range as recurring, then the configured year will not be considered.'; $string['help'] = 'Configure a string representation for your date ranges.<br /><br />If your ranges overlap the first matched date range will be applied.';
$string['placeholders'] = 'The following placeholders can be used in the string representation or fallback string. <br/><br /> {{range_first_year}} - first year of the matched range,<br/> {{range_last_year}} - last year of the matched range,<br/> {{recurring_range_first_year}} - first year of the matched recurring period,<br/> {{recurring_range_last_year}} - last year of the matched recurring period,<br/> {{current_year}} - the current year,<br/> {{date_year}} - a year of the users\'s date.';
$string['issueddate'] = 'Issued date'; $string['issueddate'] = 'Issued date';
$string['placeholders'] = 'The following placeholders can be used in the string representation or fallback string. <br/><br /> {{range_first_year}} - first year of the matched range,<br/> {{range_last_year}} - last year of the matched range,<br/> {{recurring_range_first_year}} - first year of the matched recurring period,<br/> {{recurring_range_last_year}} - last year of the matched recurring period,<br/> {{current_year}} - the current year,<br/> {{date_year}} - a year of the users\'s date.';
$string['pluginname'] = 'Date range'; $string['pluginname'] = 'Date range';
$string['privacy:metadata'] = 'The Date range plugin does not store any personal data.';
$string['start'] = 'Start';
$string['end'] = 'End';
$string['datestring'] = 'String';
$string['error:atleastone'] = 'You must have at least one date range configured';
$string['error:datestring'] = 'You must provide string representation for the datarange';
$string['error:enddate'] = 'End date must be after Start date';
$string['error:recurring'] = 'Recurring range must not be longer than 12 months';
$string['preview'] = 'Preview {$a}'; $string['preview'] = 'Preview {$a}';
$string['recurring'] = 'Recurring?'; $string['privacy:metadata'] = 'The Date range plugin does not store any personal data.';
$string['setdeleted'] = 'Delete?'; $string['recurring'] = 'Recurring';
$string['addrange'] = 'Add another range'; $string['recurring_help'] = 'If you mark a date range as recurring then the configured year will not be considered.';
$string['setdeleted'] = 'Delete';
$string['start'] = 'Start';

View file

@ -27,6 +27,13 @@ global $CFG;
require_once($CFG->dirroot . '/mod/customcert/element/daterange/tests/fixtures/fake_datarange_element.php'); require_once($CFG->dirroot . '/mod/customcert/element/daterange/tests/fixtures/fake_datarange_element.php');
/**
* Test datarange element.
*
* @package customcertelement_daterange
* @copyright 2018 Dmitrii Metelkin <dmitriim@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class customcertelement_daterange_element_test extends advanced_testcase { class customcertelement_daterange_element_test extends advanced_testcase {
/** /**

View file

@ -24,6 +24,13 @@
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
/**
* Fake datarange element for testing.
*
* @package customcertelement_daterange
* @copyright 2018 Dmitrii Metelkin <dmitriim@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class fake_datarange_element extends \customcertelement_daterange\element { class fake_datarange_element extends \customcertelement_daterange\element {
/** /**

View file

@ -107,8 +107,7 @@ class element extends \mod_customcert\element {
// If we are previewing this certificate then just show a demonstration grade. // If we are previewing this certificate then just show a demonstration grade.
if ($preview) { if ($preview) {
$courseitem = \grade_item::fetch_course_item($courseid); $courseitem = \grade_item::fetch_course_item($courseid);
$grade = grade_format_gradevalue('100', $courseitem, true, $gradeinfo->gradeformat); $grade = grade_format_gradevalue('100', $courseitem, true, $gradeinfo->gradeformat);;
$grade = get_string('exampledata', 'customcert', 'grade') . ' ' . $grade;
} else { } else {
if ($gradeitem == CUSTOMCERT_GRADE_COURSE) { if ($gradeitem == CUSTOMCERT_GRADE_COURSE) {
$grade = \mod_customcert\element_helper::get_course_grade_info( $grade = \mod_customcert\element_helper::get_course_grade_info(

View file

@ -110,20 +110,40 @@ class element extends \mod_customcert\element {
} }
/** /**
* Helper function that returns the category name. * Helper function that returns the grade item name.
* *
* @return string * @return string
*/ */
protected function get_grade_item_name() : string { protected function get_grade_item_name() : string {
global $DB; global $DB;
// Get the course module information. $gradeitem = $this->get_data();
$cm = $DB->get_record('course_modules', array('id' => $this->get_data()), '*', MUST_EXIST);
$module = $DB->get_record('modules', array('id' => $cm->module), '*', MUST_EXIST);
// Get the name of the item. if (strpos($gradeitem, 'gradeitem:') === 0) {
$itemname = $DB->get_field($module->name, 'name', array('id' => $cm->instance), MUST_EXIST); $gradeitemid = substr($gradeitem, 10);
$gradeitem = \grade_item::fetch(['id' => $gradeitemid]);
return format_string($itemname, true, ['context' => \mod_customcert\element_helper::get_context($this->get_id())]); return $gradeitem->get_name();
} else {
if (!$cm = $DB->get_record('course_modules', array('id' => $gradeitem))) {
return '';
}
if (!$module = $DB->get_record('modules', array('id' => $cm->module))) {
return '';
}
$params = [
'itemtype' => 'mod',
'itemmodule' => $module->name,
'iteminstance' => $cm->instance,
'courseid' => $cm->course,
'itemnumber' => 0
];
$gradeitem = \grade_item::fetch($params);
return $gradeitem->get_name();
}
} }
} }

View file

@ -382,14 +382,14 @@ class element extends \mod_customcert\element {
// Loop through the files uploaded in the system context. // Loop through the files uploaded in the system context.
if ($files = $fs->get_area_files(\context_system::instance()->id, 'mod_customcert', 'image', false, 'filename', false)) { if ($files = $fs->get_area_files(\context_system::instance()->id, 'mod_customcert', 'image', false, 'filename', false)) {
foreach ($files as $hash => $file) { foreach ($files as $hash => $file) {
$arrfiles[$file->get_id()] = $file->get_filename(); $arrfiles[$file->get_id()] = get_string('systemimage', 'customcertelement_image', $file->get_filename());
} }
} }
// Loop through the files uploaded in the course context. // Loop through the files uploaded in the course context.
if ($files = $fs->get_area_files(\context_course::instance($COURSE->id)->id, 'mod_customcert', 'image', false, if ($files = $fs->get_area_files(\context_course::instance($COURSE->id)->id, 'mod_customcert', 'image', false,
'filename', false)) { 'filename', false)) {
foreach ($files as $hash => $file) { foreach ($files as $hash => $file) {
$arrfiles[$file->get_id()] = $file->get_filename(); $arrfiles[$file->get_id()] = get_string('courseimage', 'customcertelement_image', $file->get_filename());
} }
} }
@ -398,4 +398,60 @@ class element extends \mod_customcert\element {
return $arrfiles; return $arrfiles;
} }
/**
* This handles copying data from another element of the same type.
*
* @param \stdClass $data the form data
* @return bool returns true if the data was copied successfully, false otherwise
*/
public function copy_element($data) {
global $COURSE, $DB, $SITE;
$imagedata = json_decode($data->data);
// If we are in the site context we don't have to do anything, the image is already there.
if ($COURSE->id == $SITE->id) {
return true;
}
$coursecontext = \context_course::instance($COURSE->id);
$systemcontext = \context_system::instance();
$fs = get_file_storage();
// Check that a file has been selected.
if (isset($imagedata->filearea)) {
// If the course file doesn't exist, copy the system file to the course context.
if (!$coursefile = $fs->get_file(
$coursecontext->id,
'mod_customcert',
$imagedata->filearea,
$imagedata->itemid,
$imagedata->filepath,
$imagedata->filename
)) {
$systemfile = $fs->get_file(
$systemcontext->id,
'mod_customcert',
$imagedata->filearea,
$imagedata->itemid,
$imagedata->filepath,
$imagedata->filename
);
// We want to update the context of the file if it doesn't exist in the course context.
$fieldupdates = [
'contextid' => $coursecontext->id
];
$coursefile = $fs->create_file_from_storedfile($fieldupdates, $systemfile);
}
// Set the image to the copied file in the course.
$imagedata->fileid = $coursefile->get_id();
$DB->set_field('customcert_elements', 'data', $this->save_unique_data($imagedata), ['id' => $this->get_id()]);
}
return true;
}
} }

View file

@ -24,6 +24,7 @@
$string['alphachannel'] = 'Alpha channel'; $string['alphachannel'] = 'Alpha channel';
$string['alphachannel_help'] = 'This value determines how transparent the image is. You can set the alpha channel from 0 (fully transparent) to 1 (fully opaque).'; $string['alphachannel_help'] = 'This value determines how transparent the image is. You can set the alpha channel from 0 (fully transparent) to 1 (fully opaque).';
$string['courseimage'] = 'Course image: {$a}';
$string['height'] = 'Height'; $string['height'] = 'Height';
$string['height_help'] = 'Height of the image in mm. If equal to zero, it is automatically calculated.'; $string['height_help'] = 'Height of the image in mm. If equal to zero, it is automatically calculated.';
$string['image'] = 'Image'; $string['image'] = 'Image';
@ -31,5 +32,6 @@ $string['invalidheight'] = 'The height has to be a valid number greater than or
$string['invalidwidth'] = 'The width has to be a valid number greater than or equal to 0.'; $string['invalidwidth'] = 'The width has to be a valid number greater than or equal to 0.';
$string['pluginname'] = 'Image'; $string['pluginname'] = 'Image';
$string['privacy:metadata'] = 'The Image plugin does not store any personal data.'; $string['privacy:metadata'] = 'The Image plugin does not store any personal data.';
$string['systemimage'] = 'Site image: {$a}';
$string['width'] = 'Width'; $string['width'] = 'Width';
$string['width_help'] = 'Width of the image in mm. If equal to zero, it is automatically calculated.'; $string['width_help'] = 'Width of the image in mm. If equal to zero, it is automatically calculated.';

View file

@ -37,7 +37,9 @@ require_once($CFG->libdir . '/tcpdf/tcpdf_barcodes_2d.php');
*/ */
class element extends \mod_customcert\element { class element extends \mod_customcert\element {
/**
* @var string The barcode type.
*/
const BARCODETYPE = 'QRCODE'; const BARCODETYPE = 'QRCODE';
/** /**
@ -70,8 +72,12 @@ class element extends \mod_customcert\element {
$errors = []; $errors = [];
// Check if height is not set, or not numeric or less than 0. // Check if height is not set, or not numeric or less than 0.
if ((!isset($data['height'])) || (!is_numeric($data['height'])) || ($data['height'] < 0)) { if ((!isset($data['height'])) || (!is_numeric($data['height'])) || ($data['height'] <= 0)) {
$errors['height'] = get_string('invalidheight', 'customcertelement_qrcode'); $errors['height'] = get_string('invalidheight', 'mod_customcert');
}
if ((!isset($data['width'])) || (!is_numeric($data['width'])) || ($data['width'] <= 0)) {
$errors['width'] = get_string('invalidwidth', 'mod_customcert');
} }
if ($this->showposxy) { if ($this->showposxy) {

View file

@ -45,6 +45,7 @@ class element extends \mod_customcert\element {
$userfields = array( $userfields = array(
'firstname' => get_user_field_name('firstname'), 'firstname' => get_user_field_name('firstname'),
'lastname' => get_user_field_name('lastname'), 'lastname' => get_user_field_name('lastname'),
'username' => get_user_field_name('username'),
'email' => get_user_field_name('email'), 'email' => get_user_field_name('email'),
'city' => get_user_field_name('city'), 'city' => get_user_field_name('city'),
'country' => get_user_field_name('country'), 'country' => get_user_field_name('country'),
@ -65,7 +66,7 @@ class element extends \mod_customcert\element {
$arrcustomfields = \availability_profile\condition::get_custom_profile_fields(); $arrcustomfields = \availability_profile\condition::get_custom_profile_fields();
$customfields = array(); $customfields = array();
foreach ($arrcustomfields as $key => $customfield) { foreach ($arrcustomfields as $key => $customfield) {
$customfields[$customfield->id] = $key; $customfields[$customfield->id] = $customfield->name;
} }
// Combine the two. // Combine the two.
$fields = $userfields + $customfields; $fields = $userfields + $customfields;

View file

@ -22,6 +22,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
$string['activity'] = 'Activity';
$string['addcertpage'] = 'Add page'; $string['addcertpage'] = 'Add page';
$string['addelement'] = 'Add element'; $string['addelement'] = 'Add element';
$string['awardedto'] = 'Awarded to'; $string['awardedto'] = 'Awarded to';
@ -83,12 +84,11 @@ $string['emailstudentcertificatelinktext'] = 'View certificate';
$string['emailstudentgreeting'] = 'Dear {$a}'; $string['emailstudentgreeting'] = 'Dear {$a}';
$string['emailstudentsubject'] = '{$a->coursefullname}: {$a->certificatename}'; $string['emailstudentsubject'] = '{$a->coursefullname}: {$a->certificatename}';
$string['emailstudents'] = 'Email students'; $string['emailstudents'] = 'Email students';
$string['emailstudents_help'] = 'If set this will email the students a copy of the certificate when it becomes available.'; $string['emailstudents_help'] = 'If set this will email the students a copy of the certificate when it becomes available. <strong>Warning:</strong> Setting this to \'Yes\' before you have finished creating the certificate will email the student an incomplete certificate.';
$string['emailteachers'] = 'Email teachers'; $string['emailteachers'] = 'Email teachers';
$string['emailteachers_help'] = 'If set this will email the teachers a copy of the certificate when it becomes available.'; $string['emailteachers_help'] = 'If set this will email the teachers a copy of the certificate when it becomes available. <strong>Warning:</strong> Setting this to \'Yes\' before you have finished creating the certificate will email the teacher an incomplete certificate.';
$string['emailothers'] = 'Email others'; $string['emailothers'] = 'Email others';
$string['emailothers_help'] = 'If set this will email the email addresses listed here (separated by a comma) with a copy of the certificate when it becomes available.'; $string['emailothers_help'] = 'If set this will email the email addresses listed here (separated by a comma) with a copy of the certificate when it becomes available. <strong>Warning:</strong> Setting this field before you have finished creating the certificate will email the addresses an incomplete certificate.';
$string['exampledata'] = 'Example {$a}:';
$string['exampledatawarning'] = 'Some of these values may just be an example to ensure positioning of the elements is possible.'; $string['exampledatawarning'] = 'Some of these values may just be an example to ensure positioning of the elements is possible.';
$string['font'] = 'Font'; $string['font'] = 'Font';
$string['font_help'] = 'The font used when generating this element.'; $string['font_help'] = 'The font used when generating this element.';
@ -97,6 +97,7 @@ $string['fontcolour_help'] = 'The colour of the font.';
$string['fontsize'] = 'Size'; $string['fontsize'] = 'Size';
$string['fontsize_help'] = 'The size of the font in points.'; $string['fontsize_help'] = 'The size of the font in points.';
$string['getcustomcert'] = 'View certificate'; $string['getcustomcert'] = 'View certificate';
$string['gradeoutcome'] = 'Outcome';
$string['height'] = 'Height'; $string['height'] = 'Height';
$string['height_help'] = 'This is the height of the certificate PDF in mm. For reference an A4 piece of paper is 297mm high and a letter is 279mm high.'; $string['height_help'] = 'This is the height of the certificate PDF in mm. For reference an A4 piece of paper is 297mm high and a letter is 279mm high.';
$string['invalidcode'] = 'Invalid code supplied.'; $string['invalidcode'] = 'Invalid code supplied.';

View file

@ -34,6 +34,7 @@ define('NO_MOODLE_COOKIES', true);
require_once('../../../config.php'); require_once('../../../config.php');
require_once($CFG->libdir . '/filelib.php'); require_once($CFG->libdir . '/filelib.php');
require_once($CFG->libdir . '/completionlib.php');
require_once($CFG->dirroot . '/webservice/lib.php'); require_once($CFG->dirroot . '/webservice/lib.php');
// Allow CORS requests. // Allow CORS requests.
@ -54,6 +55,7 @@ if (empty($enabledfiledownload)) {
} }
$cm = get_coursemodule_from_instance('customcert', $certificateid, 0, false, MUST_EXIST); $cm = get_coursemodule_from_instance('customcert', $certificateid, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
$certificate = $DB->get_record('customcert', ['id' => $certificateid], '*', MUST_EXIST); $certificate = $DB->get_record('customcert', ['id' => $certificateid], '*', MUST_EXIST);
$template = $DB->get_record('customcert_templates', ['id' => $certificate->templateid], '*', MUST_EXIST); $template = $DB->get_record('customcert_templates', ['id' => $certificate->templateid], '*', MUST_EXIST);
@ -80,6 +82,10 @@ if (!$issue) {
} }
\mod_customcert\certificate::issue_certificate($certificate->id, $USER->id); \mod_customcert\certificate::issue_certificate($certificate->id, $USER->id);
// Set the custom certificate as viewed.
$completion = new completion_info($course);
$completion->set_module_viewed($cm);
} }
// Now we want to generate the PDF. // Now we want to generate the PDF.

View file

@ -119,6 +119,42 @@ Feature: Being able to manage elements in a certificate template
| Width | 20 | | Width | 20 |
| Reference point location | Top left | | Reference point location | Top left |
And I press "Save changes" And I press "Save changes"
# Date range.
And I add the element "Date range" to page "1" of the "Custom certificate 1" certificate template
And I set the following fields to these values:
| Date item | Course start date |
| Font | Helvetica |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
| Fallback string | {{range_first_year}} |
| id_startdate_0_day | 24 |
| id_startdate_0_month | October |
| id_startdate_0_year | 2015 |
| id_enddate_0_day | 21 |
| id_enddate_0_month | March |
| id_enddate_0_year | 2016 |
| String | Oct to March |
And I press "Save changes"
And I should see "Date range" in the "elementstable" "table"
And I click on ".edit-icon" "css_element" in the "Date range" "table_row"
And the following fields match these values:
| Date item | Course start date |
| Font | Helvetica |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
| Fallback string | {{range_first_year}} |
| id_startdate_0_day | 24 |
| id_startdate_0_month | October |
| id_startdate_0_year | 2015 |
| id_enddate_0_day | 21 |
| id_enddate_0_month | March |
| id_enddate_0_year | 2016 |
| String | Oct to March |
And I press "Save changes"
# Digital signature. # Digital signature.
And I add the element "Digital signature" to page "1" of the "Custom certificate 1" certificate template And I add the element "Digital signature" to page "1" of the "Custom certificate 1" certificate template
And I set the following fields to these values: And I set the following fields to these values:
@ -144,44 +180,44 @@ Feature: Being able to manage elements in a certificate template
# Grade. # Grade.
And I add the element "Grade" to page "1" of the "Custom certificate 1" certificate template And I add the element "Grade" to page "1" of the "Custom certificate 1" certificate template
And I set the following fields to these values: And I set the following fields to these values:
| Grade item | Topic 0 : Assignment 1 | | Grade item | Activity : Assignment 1 |
| Grade format | Percentage | | Grade format | Percentage |
| Font | Helvetica | | Font | Helvetica |
| Size | 20 | | Size | 20 |
| Colour | #045ECD | | Colour | #045ECD |
| Width | 20 | | Width | 20 |
| Reference point location | Top left | | Reference point location | Top left |
And I press "Save changes" And I press "Save changes"
And I should see "Grade" in the "elementstable" "table" And I should see "Grade" in the "elementstable" "table"
And I click on ".edit-icon" "css_element" in the "Grade" "table_row" And I click on ".edit-icon" "css_element" in the "Grade" "table_row"
And the following fields match these values: And the following fields match these values:
| Grade item | Topic 0 : Assignment 1 | | Grade item | Activity : Assignment 1 |
| Grade format | Percentage | | Grade format | Percentage |
| Font | Helvetica | | Font | Helvetica |
| Size | 20 | | Size | 20 |
| Colour | #045ECD | | Colour | #045ECD |
| Width | 20 | | Width | 20 |
| Reference point location | Top left | | Reference point location | Top left |
And I press "Save changes" And I press "Save changes"
# Grade item name. # Grade item name.
And I add the element "Grade item name" to page "1" of the "Custom certificate 1" certificate template And I add the element "Grade item name" to page "1" of the "Custom certificate 1" certificate template
And I set the following fields to these values: And I set the following fields to these values:
| Grade item | Topic 0 : Assignment 2 | | Grade item | Activity : Assignment 2 |
| Font | Helvetica | | Font | Helvetica |
| Size | 20 | | Size | 20 |
| Colour | #045ECD | | Colour | #045ECD |
| Width | 20 | | Width | 20 |
| Reference point location | Top left | | Reference point location | Top left |
And I press "Save changes" And I press "Save changes"
And I should see "Grade item name" in the "elementstable" "table" And I should see "Grade item name" in the "elementstable" "table"
And I click on ".edit-icon" "css_element" in the "Grade item name" "table_row" And I click on ".edit-icon" "css_element" in the "Grade item name" "table_row"
And the following fields match these values: And the following fields match these values:
| Grade item | Topic 0 : Assignment 2 | | Grade item | Activity : Assignment 2 |
| Font | Helvetica | | Font | Helvetica |
| Size | 20 | | Size | 20 |
| Colour | #045ECD | | Colour | #045ECD |
| Width | 20 | | Width | 20 |
| Reference point location | Top left | | Reference point location | Top left |
And I press "Save changes" And I press "Save changes"
# Image. # Image.
And I add the element "Image" to page "1" of the "Custom certificate 1" certificate template And I add the element "Image" to page "1" of the "Custom certificate 1" certificate template
@ -287,6 +323,18 @@ Feature: Being able to manage elements in a certificate template
| Width | 10 | | Width | 10 |
| Height | 10 | | Height | 10 |
And I press "Save changes" And I press "Save changes"
# QR Code.
And I add the element "QR code" to page "1" of the "Custom certificate 1" certificate template
And I set the following fields to these values:
| Width | 25 |
| Height | 15 |
And I press "Save changes"
And I should see "QR code" in the "elementstable" "table"
And I click on ".edit-icon" "css_element" in the "QR code" "table_row"
And the following fields match these values:
| Width | 25 |
| Height | 15 |
And I press "Save changes"
# Just to test there are no exceptions being thrown. # Just to test there are no exceptions being thrown.
And I follow "Reposition elements" And I follow "Reposition elements"
And I press "Save and close" And I press "Save and close"

View file

@ -180,14 +180,24 @@ class mod_customcert_element_helper_testcase extends advanced_testcase {
$gc = $this->getDataGenerator()->create_grade_category(['courseid' => $course->id]); $gc = $this->getDataGenerator()->create_grade_category(['courseid' => $course->id]);
$gc = $DB->get_record('grade_items', ['itemtype' => 'category', 'iteminstance' => $gc->id]); $gc = $DB->get_record('grade_items', ['itemtype' => 'category', 'iteminstance' => $gc->id]);
// Create an item attached to an outcome.
$outcome = $this->getDataGenerator()->create_grade_outcome(['courseid' => $course->id, 'shortname' => 'outcome']);
$go = $this->getDataGenerator()->create_grade_item(
[
'courseid' => $course->id,
'outcomeid' => $outcome->id
]
);
// Confirm the function returns the correct number of grade items. // Confirm the function returns the correct number of grade items.
$gradeitems = \mod_customcert\element_helper::get_grade_items($course); $gradeitems = \mod_customcert\element_helper::get_grade_items($course);
$this->assertCount(5, $gradeitems); $this->assertCount(6, $gradeitems);
$this->assertArrayHasKey($assign1->cmid, $gradeitems); $this->assertArrayHasKey($assign1->cmid, $gradeitems);
$this->assertArrayHasKey($assign2->cmid, $gradeitems); $this->assertArrayHasKey($assign2->cmid, $gradeitems);
$this->assertArrayHasKey($assign3->cmid, $gradeitems); $this->assertArrayHasKey($assign3->cmid, $gradeitems);
$this->assertArrayHasKey('gradeitem:' . $gi->id, $gradeitems); $this->assertArrayHasKey('gradeitem:' . $gi->id, $gradeitems);
$this->assertArrayHasKey('gradeitem:' . $gc->id, $gradeitems); $this->assertArrayHasKey('gradeitem:' . $gc->id, $gradeitems);
$this->assertArrayHasKey('gradeitem:' . $go->id, $gradeitems);
} }
/** /**

View file

@ -25,8 +25,6 @@
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
global $CFG;
/** /**
* Unit tests for the email certificate task. * Unit tests for the email certificate task.
* *
@ -44,6 +42,32 @@ class mod_customcert_task_email_certificate_task_testcase extends advanced_testc
$this->resetAfterTest(); $this->resetAfterTest();
} }
/**
* Tests the email certificate task when there are no elements.
*/
public function test_email_certificates_no_elements() {
// Create a course.
$course = $this->getDataGenerator()->create_course();
// Create a user.
$user1 = $this->getDataGenerator()->create_user();
// Create a custom certificate with no elements.
$this->getDataGenerator()->create_module('customcert', ['course' => $course->id, 'emailstudents' => 1]);
// Enrol the user as a student
$this->getDataGenerator()->enrol_user($user1->id, $course->id);
// Run the task.
$sink = $this->redirectEmails();
$task = new \mod_customcert\task\email_certificate_task();
$task->execute();
$emails = $sink->get_messages();
// Confirm that we did not send any emails because the certificate has no elements.
$this->assertCount(0, $emails);
}
/** /**
* Tests the email certificate task for users without a capability to receive a certificate. * Tests the email certificate task for users without a capability to receive a certificate.
*/ */
@ -65,7 +89,23 @@ class mod_customcert_task_email_certificate_task_testcase extends advanced_testc
unassign_capability('mod/customcert:receiveissue', $roleids['student']); unassign_capability('mod/customcert:receiveissue', $roleids['student']);
// Create a custom certificate. // Create a custom certificate.
$this->getDataGenerator()->create_module('customcert', ['course' => $course->id, 'emailstudents' => 1]); $customcert = $this->getDataGenerator()->create_module('customcert', ['course' => $course->id, 'emailstudents' => 1]);
// Create template object.
$template = new stdClass();
$template->id = $customcert->templateid;
$template->name = 'A template';
$template->contextid = context_course::instance($course->id)->id;
$template = new \mod_customcert\template($template);
// Add a page to this template.
$pageid = $template->add_page();
// Add an element to the page.
$element = new stdClass();
$element->pageid = $pageid;
$element->name = 'Image';
$DB->insert_record('customcert_elements', $element);
// Run the task. // Run the task.
$sink = $this->redirectEmails(); $sink = $this->redirectEmails();
@ -103,6 +143,22 @@ class mod_customcert_task_email_certificate_task_testcase extends advanced_testc
$customcert = $this->getDataGenerator()->create_module('customcert', array('course' => $course->id, $customcert = $this->getDataGenerator()->create_module('customcert', array('course' => $course->id,
'emailstudents' => 1)); 'emailstudents' => 1));
// Create template object.
$template = new stdClass();
$template->id = $customcert->templateid;
$template->name = 'A template';
$template->contextid = context_course::instance($course->id)->id;
$template = new \mod_customcert\template($template);
// Add a page to this template.
$pageid = $template->add_page();
// Add an element to the page.
$element = new stdClass();
$element->pageid = $pageid;
$element->name = 'Image';
$DB->insert_record('customcert_elements', $element);
// Ok, now issue this to one user. // Ok, now issue this to one user.
\mod_customcert\certificate::issue_certificate($customcert->id, $user1->id); \mod_customcert\certificate::issue_certificate($customcert->id, $user1->id);
@ -169,9 +225,25 @@ class mod_customcert_task_email_certificate_task_testcase extends advanced_testc
$this->getDataGenerator()->enrol_user($user3->id, $course->id, $roleids['editingteacher']); $this->getDataGenerator()->enrol_user($user3->id, $course->id, $roleids['editingteacher']);
// Create a custom certificate. // Create a custom certificate.
$this->getDataGenerator()->create_module('customcert', array('course' => $course->id, $customcert = $this->getDataGenerator()->create_module('customcert', array('course' => $course->id,
'emailteachers' => 1)); 'emailteachers' => 1));
// Create template object.
$template = new stdClass();
$template->id = $customcert->templateid;
$template->name = 'A template';
$template->contextid = context_course::instance($course->id)->id;
$template = new \mod_customcert\template($template);
// Add a page to this template.
$pageid = $template->add_page();
// Add an element to the page.
$element = new stdClass();
$element->pageid = $pageid;
$element->name = 'Image';
$DB->insert_record('customcert_elements', $element);
// Run the task. // Run the task.
$sink = $this->redirectEmails(); $sink = $this->redirectEmails();
$task = new \mod_customcert\task\email_certificate_task(); $task = new \mod_customcert\task\email_certificate_task();
@ -192,7 +264,7 @@ class mod_customcert_task_email_certificate_task_testcase extends advanced_testc
* Tests the email certificate task for others. * Tests the email certificate task for others.
*/ */
public function test_email_certificates_others() { public function test_email_certificates_others() {
global $CFG; global $CFG, $DB;
// Create a course. // Create a course.
$course = $this->getDataGenerator()->create_course(); $course = $this->getDataGenerator()->create_course();
@ -206,9 +278,25 @@ class mod_customcert_task_email_certificate_task_testcase extends advanced_testc
$this->getDataGenerator()->enrol_user($user2->id, $course->id); $this->getDataGenerator()->enrol_user($user2->id, $course->id);
// Create a custom certificate. // Create a custom certificate.
$this->getDataGenerator()->create_module('customcert', array('course' => $course->id, $customcert = $this->getDataGenerator()->create_module('customcert', array('course' => $course->id,
'emailothers' => 'testcustomcert@example.com, doo@dah')); 'emailothers' => 'testcustomcert@example.com, doo@dah'));
// Create template object.
$template = new stdClass();
$template->id = $customcert->templateid;
$template->name = 'A template';
$template->contextid = context_course::instance($course->id)->id;
$template = new \mod_customcert\template($template);
// Add a page to this template.
$pageid = $template->add_page();
// Add an element to the page.
$element = new stdClass();
$element->pageid = $pageid;
$element->name = 'Image';
$DB->insert_record('customcert_elements', $element);
// Run the task. // Run the task.
$sink = $this->redirectEmails(); $sink = $this->redirectEmails();
$task = new \mod_customcert\task\email_certificate_task(); $task = new \mod_customcert\task\email_certificate_task();
@ -242,7 +330,23 @@ class mod_customcert_task_email_certificate_task_testcase extends advanced_testc
$this->getDataGenerator()->enrol_user($user1->id, $course->id); $this->getDataGenerator()->enrol_user($user1->id, $course->id);
// Create a custom certificate. // Create a custom certificate.
$this->getDataGenerator()->create_module('customcert', array('course' => $course->id, 'emailstudents' => 1)); $customcert = $this->getDataGenerator()->create_module('customcert', ['course' => $course->id, 'emailstudents' => 1]);
// Create template object.
$template = new stdClass();
$template->id = $customcert->templateid;
$template->name = 'A template';
$template->contextid = context_course::instance($course->id)->id;
$template = new \mod_customcert\template($template);
// Add a page to this template.
$pageid = $template->add_page();
// Add an element to the page.
$element = new stdClass();
$element->pageid = $pageid;
$element->name = 'Image';
$DB->insert_record('customcert_elements', $element);
// Remove the permission for the user to view the certificate. // Remove the permission for the user to view the certificate.
assign_capability('mod/customcert:view', CAP_PROHIBIT, $roleids['student'], \context_course::instance($course->id)); assign_capability('mod/customcert:view', CAP_PROHIBIT, $roleids['student'], \context_course::instance($course->id));
@ -280,9 +384,25 @@ class mod_customcert_task_email_certificate_task_testcase extends advanced_testc
$this->getDataGenerator()->enrol_user($user1->id, $course->id); $this->getDataGenerator()->enrol_user($user1->id, $course->id);
// Create a custom certificate. // Create a custom certificate.
$this->getDataGenerator()->create_module('customcert', array('course' => $course->id, 'emailstudents' => 1, $customcert = $this->getDataGenerator()->create_module('customcert', array('course' => $course->id, 'emailstudents' => 1,
'requiredtime' => '60')); 'requiredtime' => '60'));
// Create template object.
$template = new stdClass();
$template->id = $customcert->templateid;
$template->name = 'A template';
$template->contextid = context_course::instance($course->id)->id;
$template = new \mod_customcert\template($template);
// Add a page to this template.
$pageid = $template->add_page();
// Add an element to the page.
$element = new stdClass();
$element->pageid = $pageid;
$element->name = 'Image';
$DB->insert_record('customcert_elements', $element);
// Run the task. // Run the task.
$sink = $this->redirectEmails(); $sink = $this->redirectEmails();
$task = new \mod_customcert\task\email_certificate_task(); $task = new \mod_customcert\task\email_certificate_task();

View file

@ -24,10 +24,10 @@
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
$plugin->version = 2018120302; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2018120307; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2018120300; // Requires this Moodle version (3.6). $plugin->requires = 2018120300; // Requires this Moodle version (3.6).
$plugin->cron = 0; // Period for cron to check this module (secs). $plugin->cron = 0; // Period for cron to check this module (secs).
$plugin->component = 'mod_customcert'; $plugin->component = 'mod_customcert';
$plugin->maturity = MATURITY_STABLE; $plugin->maturity = MATURITY_STABLE;
$plugin->release = "3.6.2"; // User-friendly version number. $plugin->release = "3.6.7"; // User-friendly version number.

View file

@ -139,7 +139,7 @@ if (!$downloadown && !$downloadissue) {
if ($canreceive) { if ($canreceive) {
$linkname = get_string('getcustomcert', 'customcert'); $linkname = get_string('getcustomcert', 'customcert');
$link = new moodle_url('/mod/customcert/view.php', array('id' => $cm->id, 'downloadown' => true)); $link = new moodle_url('/mod/customcert/view.php', array('id' => $cm->id, 'downloadown' => true));
$downloadbutton = new single_button($link, $linkname, 'post', true); $downloadbutton = new single_button($link, $linkname, 'get', true);
$downloadbutton->class .= ' m-b-1'; // Seems a bit hackish, ahem. $downloadbutton->class .= ' m-b-1'; // Seems a bit hackish, ahem.
$downloadbutton = $OUTPUT->render($downloadbutton); $downloadbutton = $OUTPUT->render($downloadbutton);
} }
@ -179,6 +179,8 @@ if (!$downloadown && !$downloadissue) {
redirect(new moodle_url('/mod/customcert/view.php', array('id' => $cm->id))); redirect(new moodle_url('/mod/customcert/view.php', array('id' => $cm->id)));
} }
\core\session\manager::write_close();
// Now we want to generate the PDF. // Now we want to generate the PDF.
$template = new \mod_customcert\template($template); $template = new \mod_customcert\template($template);
$template->generate_pdf(false, $userid); $template->generate_pdf(false, $userid);

View file

@ -80,24 +80,14 @@ Y.extend(Rearrange, Y.Base, {
this.elements = params[2]; this.elements = params[2];
// Set the PDF dimensions. // Set the PDF dimensions.
this.pdfx = Y.one('#pdf').getX(); this.setPdfDimensions();
this.pdfy = Y.one('#pdf').getY();
this.pdfwidth = parseFloat(Y.one('#pdf').getComputedStyle('width'));
this.pdfheight = parseFloat(Y.one('#pdf').getComputedStyle('height'));
// Set the boundaries. // Set the boundaries.
this.pdfleftboundary = this.pdfx; this.setBoundaries();
if (this.page.leftmargin) {
this.pdfleftboundary += parseInt(this.page.leftmargin * this.pixelsinmm, 10);
}
this.pdfrightboundary = this.pdfx + this.pdfwidth;
if (this.page.rightmargin) {
this.pdfrightboundary -= parseInt(this.page.rightmargin * this.pixelsinmm, 10);
}
this.setpositions(); this.setpositions();
this.createevents(); this.createevents();
window.addEventListener("resize", this.checkWindownResize.bind(this));
}, },
/** /**
@ -130,6 +120,40 @@ Y.extend(Rearrange, Y.Base, {
} }
}, },
/**
* Sets the PDF dimensions.
*/
setPdfDimensions: function() {
this.pdfx = Y.one('#pdf').getX();
this.pdfy = Y.one('#pdf').getY();
this.pdfwidth = parseFloat(Y.one('#pdf').getComputedStyle('width'));
this.pdfheight = parseFloat(Y.one('#pdf').getComputedStyle('height'));
},
/**
* Sets the boundaries.
*/
setBoundaries: function() {
this.pdfleftboundary = this.pdfx;
if (this.page.leftmargin) {
this.pdfleftboundary += parseInt(this.page.leftmargin * this.pixelsinmm, 10);
}
this.pdfrightboundary = this.pdfx + this.pdfwidth;
if (this.page.rightmargin) {
this.pdfrightboundary -= parseInt(this.page.rightmargin * this.pixelsinmm, 10);
}
},
/**
* Check browser resize and reset position.
*/
checkWindownResize: function() {
this.setPdfDimensions();
this.setBoundaries();
this.setpositions();
},
/** /**
* Creates the JS events for changing element positions. * Creates the JS events for changing element positions.
*/ */

View file

@ -1 +1 @@
YUI.add("moodle-mod_customcert-rearrange",function(e,t){var n=function(){n.superclass.constructor.apply(this,[arguments])};e.extend(n,e.Base,{templateid:0,page:[],elements:[],pdfx:0,pdfy:0,pdfwidth:0,pdfheight:0,elementxy:0,pdfleftboundary:0,pdfrightboundary:0,pixelsinmm:3.779527559055,initializer:function(t){this.templateid=t[0],this.page=t[1],this.elements=t[2],this.pdfx=e.one("#pdf").getX(),this.pdfy=e.one("#pdf").getY(),this.pdfwidth=parseFloat(e.one("#pdf").getComputedStyle("width")),this.pdfheight=parseFloat(e.one("#pdf").getComputedStyle("height")),this.pdfleftboundary=this.pdfx,this.page.leftmargin&&(this.pdfleftboundary+=parseInt(this.page.leftmargin*this.pixelsinmm,10)),this.pdfrightboundary=this.pdfx+this.pdfwidth,this.page.rightmargin&&(this.pdfrightboundary-=parseInt(this.page.rightmargin*this.pixelsinmm,10)),this.setpositions(),this.createevents()},setpositions:function(){for(var t in this.elements){var n=this.elements[t],r=this.pdfx+n.posx*this.pixelsinmm,i=this.pdfy+n.posy*this.pixelsinmm,s=parseFloat(e.one("#element-"+n.id).getComputedStyle("width")),o=n.width*this.pixelsinmm;o&&s>o&&(s=o);switch(n.refpoint){case"1":r-=s/2;break;case"2":r=r-s+2}e.one("#element-"+n.id).setX(r),e.one("#element-"+n.id).setY(i)}},createevents:function(){e.one(".savepositionsbtn [type=submit]").on("click",function(e){this.savepositions(e)},this),e.one(".applypositionsbtn [type=submit]").on("click",function(e){this.savepositions(e),e.preventDefault()},this);var t=new e.DD.Delegate({container:"#pdf",nodes:".element"});t.on("drag:start",function(){var e=t.get("currentNode");this.elementxy=e.getXY()},this),t.on("drag:end",function(){var e=t.get("currentNode");this.isoutofbounds(e)&&e.setXY(this.elementxy)},this)},isoutofbounds:function(e){var t=parseFloat(e.getComputedStyle("width")),n=parseFloat(e.getComputedStyle("height")),r=e.getX(),i=r+t,s=e.getY(),o=s+n;return r<this.pdfleftboundary||i>this.pdfrightboundary?!0:s<this.pdfy||o>this.pdfy+this.pdfheight?!0:!1},savepositions:function(t){var n={tid:this.templateid,values:[]};for(var r in this.elements){var i=this.elements[r],s=e.one("#element-"+i.id),o=s.getX()-this.pdfx,u=s.getY()-this.pdfy,a=s.getData("refpoint"),f=parseFloat(s.getComputedStyle("width"));switch(a){case"1":o+=f/2;break;case"2":o+=f}n.values.push({id:i.id,posx:Math.round(parseFloat(o/this.pixelsinmm)),posy:Math.round(parseFloat(u/this.pixelsinmm))})}n.values=JSON.stringify(n.values),e.io(M.cfg.wwwroot+"/mod/customcert/ajax.php",{method:"POST",data:n,on:{failure:function(e,t){this.ajaxfailure(t)},success:function(){var e=t.currentTarget.ancestor("form",!0),n=e.getAttribute("action"),r=e.one("[name=pid]");if(r){var i=r.get("value");window.location=n+"?pid="+i}else{var s=e.one("[name=tid]").get("value");window.location=n+"?tid="+s}}},context:this}),t.preventDefault()},ajaxfailure:function(e){var t={name:e.status+" "+e.statusText,message:e.responseText};return new M.core.exception(t)}}),e.namespace("M.mod_customcert.rearrange").init=function(e,t,r){new n(e,t,r)}},"@VERSION@",{requires:["dd-delegate","dd-drag"]}); YUI.add("moodle-mod_customcert-rearrange",function(e,t){var n=function(){n.superclass.constructor.apply(this,[arguments])};e.extend(n,e.Base,{templateid:0,page:[],elements:[],pdfx:0,pdfy:0,pdfwidth:0,pdfheight:0,elementxy:0,pdfleftboundary:0,pdfrightboundary:0,pixelsinmm:3.779527559055,initializer:function(e){this.templateid=e[0],this.page=e[1],this.elements=e[2],this.setPdfDimensions(),this.setBoundaries(),this.setpositions(),this.createevents(),window.addEventListener("resize",this.checkWindownResize.bind(this))},setpositions:function(){for(var t in this.elements){var n=this.elements[t],r=this.pdfx+n.posx*this.pixelsinmm,i=this.pdfy+n.posy*this.pixelsinmm,s=parseFloat(e.one("#element-"+n.id).getComputedStyle("width")),o=n.width*this.pixelsinmm;o&&s>o&&(s=o);switch(n.refpoint){case"1":r-=s/2;break;case"2":r=r-s+2}e.one("#element-"+n.id).setX(r),e.one("#element-"+n.id).setY(i)}},setPdfDimensions:function(){this.pdfx=e.one("#pdf").getX(),this.pdfy=e.one("#pdf").getY(),this.pdfwidth=parseFloat(e.one("#pdf").getComputedStyle("width")),this.pdfheight=parseFloat(e.one("#pdf").getComputedStyle("height"))},setBoundaries:function(){this.pdfleftboundary=this.pdfx,this.page.leftmargin&&(this.pdfleftboundary+=parseInt(this.page.leftmargin*this.pixelsinmm,10)),this.pdfrightboundary=this.pdfx+this.pdfwidth,this.page.rightmargin&&(this.pdfrightboundary-=parseInt(this.page.rightmargin*this.pixelsinmm,10))},checkWindownResize:function(){this.setPdfDimensions(),this.setBoundaries(),this.setpositions()},createevents:function(){e.one(".savepositionsbtn [type=submit]").on("click",function(e){this.savepositions(e)},this),e.one(".applypositionsbtn [type=submit]").on("click",function(e){this.savepositions(e),e.preventDefault()},this);var t=new e.DD.Delegate({container:"#pdf",nodes:".element"});t.on("drag:start",function(){var e=t.get("currentNode");this.elementxy=e.getXY()},this),t.on("drag:end",function(){var e=t.get("currentNode");this.isoutofbounds(e)&&e.setXY(this.elementxy)},this)},isoutofbounds:function(e){var t=parseFloat(e.getComputedStyle("width")),n=parseFloat(e.getComputedStyle("height")),r=e.getX(),i=r+t,s=e.getY(),o=s+n;return r<this.pdfleftboundary||i>this.pdfrightboundary?!0:s<this.pdfy||o>this.pdfy+this.pdfheight?!0:!1},savepositions:function(t){var n={tid:this.templateid,values:[]};for(var r in this.elements){var i=this.elements[r],s=e.one("#element-"+i.id),o=s.getX()-this.pdfx,u=s.getY()-this.pdfy,a=s.getData("refpoint"),f=parseFloat(s.getComputedStyle("width"));switch(a){case"1":o+=f/2;break;case"2":o+=f}n.values.push({id:i.id,posx:Math.round(parseFloat(o/this.pixelsinmm)),posy:Math.round(parseFloat(u/this.pixelsinmm))})}n.values=JSON.stringify(n.values),e.io(M.cfg.wwwroot+"/mod/customcert/ajax.php",{method:"POST",data:n,on:{failure:function(e,t){this.ajaxfailure(t)},success:function(){var e=t.currentTarget.ancestor("form",!0),n=e.getAttribute("action"),r=e.one("[name=pid]");if(r){var i=r.get("value");window.location=n+"?pid="+i}else{var s=e.one("[name=tid]").get("value");window.location=n+"?tid="+s}}},context:this}),t.preventDefault()},ajaxfailure:function(e){var t={name:e.status+" "+e.statusText,message:e.responseText};return new M.core.exception(t)}}),e.namespace("M.mod_customcert.rearrange").init=function(e,t,r){new n(e,t,r)}},"@VERSION@",{requires:["dd-delegate","dd-drag"]});

View file

@ -80,24 +80,14 @@ Y.extend(Rearrange, Y.Base, {
this.elements = params[2]; this.elements = params[2];
// Set the PDF dimensions. // Set the PDF dimensions.
this.pdfx = Y.one('#pdf').getX(); this.setPdfDimensions();
this.pdfy = Y.one('#pdf').getY();
this.pdfwidth = parseFloat(Y.one('#pdf').getComputedStyle('width'));
this.pdfheight = parseFloat(Y.one('#pdf').getComputedStyle('height'));
// Set the boundaries. // Set the boundaries.
this.pdfleftboundary = this.pdfx; this.setBoundaries();
if (this.page.leftmargin) {
this.pdfleftboundary += parseInt(this.page.leftmargin * this.pixelsinmm, 10);
}
this.pdfrightboundary = this.pdfx + this.pdfwidth;
if (this.page.rightmargin) {
this.pdfrightboundary -= parseInt(this.page.rightmargin * this.pixelsinmm, 10);
}
this.setpositions(); this.setpositions();
this.createevents(); this.createevents();
window.addEventListener("resize", this.checkWindownResize.bind(this));
}, },
/** /**
@ -130,6 +120,40 @@ Y.extend(Rearrange, Y.Base, {
} }
}, },
/**
* Sets the PDF dimensions.
*/
setPdfDimensions: function() {
this.pdfx = Y.one('#pdf').getX();
this.pdfy = Y.one('#pdf').getY();
this.pdfwidth = parseFloat(Y.one('#pdf').getComputedStyle('width'));
this.pdfheight = parseFloat(Y.one('#pdf').getComputedStyle('height'));
},
/**
* Sets the boundaries.
*/
setBoundaries: function() {
this.pdfleftboundary = this.pdfx;
if (this.page.leftmargin) {
this.pdfleftboundary += parseInt(this.page.leftmargin * this.pixelsinmm, 10);
}
this.pdfrightboundary = this.pdfx + this.pdfwidth;
if (this.page.rightmargin) {
this.pdfrightboundary -= parseInt(this.page.rightmargin * this.pixelsinmm, 10);
}
},
/**
* Check browser resize and reset position.
*/
checkWindownResize: function() {
this.setPdfDimensions();
this.setBoundaries();
this.setpositions();
},
/** /**
* Creates the JS events for changing element positions. * Creates the JS events for changing element positions.
*/ */

View file

@ -78,24 +78,14 @@ Y.extend(Rearrange, Y.Base, {
this.elements = params[2]; this.elements = params[2];
// Set the PDF dimensions. // Set the PDF dimensions.
this.pdfx = Y.one('#pdf').getX(); this.setPdfDimensions();
this.pdfy = Y.one('#pdf').getY();
this.pdfwidth = parseFloat(Y.one('#pdf').getComputedStyle('width'));
this.pdfheight = parseFloat(Y.one('#pdf').getComputedStyle('height'));
// Set the boundaries. // Set the boundaries.
this.pdfleftboundary = this.pdfx; this.setBoundaries();
if (this.page.leftmargin) {
this.pdfleftboundary += parseInt(this.page.leftmargin * this.pixelsinmm, 10);
}
this.pdfrightboundary = this.pdfx + this.pdfwidth;
if (this.page.rightmargin) {
this.pdfrightboundary -= parseInt(this.page.rightmargin * this.pixelsinmm, 10);
}
this.setpositions(); this.setpositions();
this.createevents(); this.createevents();
window.addEventListener("resize", this.checkWindownResize.bind(this));
}, },
/** /**
@ -128,6 +118,40 @@ Y.extend(Rearrange, Y.Base, {
} }
}, },
/**
* Sets the PDF dimensions.
*/
setPdfDimensions: function() {
this.pdfx = Y.one('#pdf').getX();
this.pdfy = Y.one('#pdf').getY();
this.pdfwidth = parseFloat(Y.one('#pdf').getComputedStyle('width'));
this.pdfheight = parseFloat(Y.one('#pdf').getComputedStyle('height'));
},
/**
* Sets the boundaries.
*/
setBoundaries: function() {
this.pdfleftboundary = this.pdfx;
if (this.page.leftmargin) {
this.pdfleftboundary += parseInt(this.page.leftmargin * this.pixelsinmm, 10);
}
this.pdfrightboundary = this.pdfx + this.pdfwidth;
if (this.page.rightmargin) {
this.pdfrightboundary -= parseInt(this.page.rightmargin * this.pixelsinmm, 10);
}
},
/**
* Check browser resize and reset position.
*/
checkWindownResize: function() {
this.setPdfDimensions();
this.setBoundaries();
this.setpositions();
},
/** /**
* Creates the JS events for changing element positions. * Creates the JS events for changing element positions.
*/ */