Compare commits

..

262 commits

Author SHA1 Message Date
6ac6ba53c1
Current status 2022-09-07 13:54:03 +00:00
bd2a78b94b Add CLI script to batch-apply templates 2021-11-09 14:27:32 +00:00
87b79503c8 Implemented PIN replacement 2021-11-09 14:23:04 +00:00
7c33e659b4 Current changes 2021-11-09 13:43:08 +00:00
8cc6693b24 Current changes 2021-11-09 13:42:54 +00:00
6bb72f655f Current changes 2021-11-09 13:42:47 +00:00
b9c5e95f7a Add custom HTML field which takes precedence over template pages 2021-11-05 08:02:24 +00:00
Mark Nelson
311e243ca5 Update CHANGES.md 2021-10-27 16:46:50 +08:00
Mark Nelson
5ba1b2b40f Respect multiple languages in manage template page title (#467) 2021-10-27 16:44:49 +08:00
Mark Nelson
4ca9f05fc1 Updated CHANGES.md 2021-10-27 15:31:53 +08:00
hieuvu
2a9640b5a3 Fix error message when we have custom profile fields (#465) 2021-10-27 15:30:13 +08:00
Mark Nelson
18eab896bd Change JS to please GHA 2021-10-27 13:30:04 +08:00
Mark Nelson
9e38411434 Changes to make GHA happy and added usages of the coalescing operator (#121) 2021-10-27 13:15:32 +08:00
Mark Nelson
8464ba6259 Add issue number to CHANGES.md 2021-10-27 12:23:50 +08:00
Mark Nelson
92a3bcb0c3 Updated CHANGES.md 2021-10-25 16:14:18 +08:00
Jesús Alonso Abad
3b1cd64703 Added alignment to activity backup/cloning (#121) 2021-10-17 20:54:42 +08:00
Jesús Alonso Abad
383054be39 Added elements alignment support (#121) 2021-10-17 20:44:55 +08:00
Mark Nelson
80dc7395c3 Do not encode html entities in emails (#457) 2021-09-28 22:17:53 +08:00
Mark Nelson
b684fee9f4 Update CHANGES.md 2021-08-06 10:56:43 +08:00
Andrew Madden
95274bfad6 Closes #449 The user id should be mapped to the equivalent user id in new sites during activity restore
As the userid in the customcert_issues table were using the userid from the site where the activity was backed up, emails were being sent to those that had already received them. Theoretically there wwere also users who should have received an email who didn't get one.
2021-08-06 10:48:21 +08:00
Mark Nelson
75fe6b4cdd Update CHANGES.md 2021-08-04 11:37:11 +08:00
Mark Nelson
8b8a358790 Minor code changes (#415) 2021-08-04 11:28:03 +08:00
Sameer Ahmed
a1f0e6dec7 Display the course short name (#415)
- Added a new select box to choose from course name or short description to display.
2021-08-03 18:56:25 +08:00
Mark Nelson
f1cf599f31 GHA: ROW_FORMAT=COMPRESSED deprecated in MariaDB 10.6
See MDL-72131.
2021-08-03 17:56:54 +08:00
Mark Nelson
afcea89c82 Add new lines at end of files 2021-08-03 17:39:34 +08:00
Mark Nelson
5e3135324c Remove unnecessary new line 2021-08-03 16:53:24 +08:00
Mark Nelson
c6c4e6f6a7 Minor changes and CHANGES.md note (#433) 2021-08-03 16:30:45 +08:00
Michael Milette
25d398ce22 Fix for multi-language issues (#433).
- Filename of PDF when viewing/previewing PDF.
- Page title tag when viewing/previewing PDF.
- List of available templates.
- Template Load dropdown list.
2021-07-27 10:54:18 +08:00
Mark Nelson
cf11f765b0 Fix GHA failing 2021-07-06 17:18:35 +08:00
Dani Palou
6d966df56c Adapt mobile app code to Ionic 5 (#431) 2021-07-06 17:03:49 +08:00
Mark Nelson
e4458872fa Fix release date 2021-06-13 14:26:57 +08:00
Mark Nelson
26802e9262 Bump version 2021-06-13 14:04:59 +08:00
Mark Nelson
0de67035a3 Use 'cron_setup_user' when sending emails (#414) 2021-06-13 14:04:18 +08:00
Mark Nelson
11225ff1fa Set proper context when sending emails (#402) 2021-06-13 13:36:02 +08:00
hieuvu
bd361d2434 Add actions title to elements table (#425) 2021-06-08 17:41:38 +08:00
Mark Nelson
659e065a2f Fix failing PHPUnit test 2021-05-28 00:50:25 +08:00
Mark Nelson
991ef9b0e3 Fix failing Behat tests 2021-05-28 00:33:52 +08:00
Mark Nelson
a68f1b9487 Remove usage of deprecated user fields 2021-05-27 23:14:10 +08:00
Mark Nelson
8145e020e4 Add type hint and return type 2021-05-27 22:49:57 +08:00
Mark Nelson
e65cf7e9c2 Add new line at end of file 2021-05-27 22:48:30 +08:00
Mark Nelson
345c6e438b Removed usaged of deprecated functions (#423) 2021-05-27 22:46:53 +08:00
Mark Nelson
1343d64310 Fix Moodle Code Checker complaints 2021-05-27 22:13:31 +08:00
Mark Nelson
0208efa585 Removed usaged of deprecated functions (#423) 2021-05-27 21:37:50 +08:00
Mark Nelson
1a6d370d3f Removed .travis.yml file 2021-05-27 19:21:11 +08:00
Mark Nelson
69a3436faa Fix Moodle Code Checker complaints 2021-05-27 18:24:01 +08:00
Mark Nelson
9f320984c6 Update github/workflows/moodle-ci.yml 2021-05-27 14:28:16 +08:00
Mark Nelson
0122cc1138 Update travis.yml 2021-05-27 14:11:54 +08:00
Mark Nelson
4ca04d204d Bump for version 3.11 2021-05-27 14:05:04 +08:00
Mark Nelson
ff16abfd7c Update CHANGES.md 2021-04-16 14:42:31 +08:00
Mikhail Golenkov
1859ffa651 Fix the issue with displaying PDF when debugging is ON (#420) 2021-04-16 14:36:15 +08:00
Mark Nelson
47061f8737 Add type hinting to get_course_time() 2021-04-05 20:11:02 +08:00
Mark Nelson
6e89c1d921 Use instance of moodle_url() in the function notice() 2021-04-05 20:08:25 +08:00
Mark Nelson
22081dffb0 Fix get_course_time() allowing users to view certificate early (#403) 2021-04-05 19:55:27 +08:00
Mark Nelson
2e58b24daa Updated CHANGES.md 2021-04-05 16:51:26 +08:00
Mark Nelson
8446a2e10a Add ability to choose how to deliver the certificate (#401) 2021-04-05 16:49:10 +08:00
Mark Nelson
ae2df6c9ed Updated CHANGES.md 2021-04-03 11:27:32 +08:00
Mark Nelson
2e881e32c9 Add ability to show description on course page (#406) 2021-04-03 10:37:15 +08:00
Marina Glancy
dcbb07e2f3 Add github actions (#407) 2021-04-02 20:48:49 +08:00
Toni Förster
350b18dda6 Allow managers to download certificates (#412)
Currently, it is not possible for editing teachers
and managers to download certificates for users because
we only check for $canreceive.
2021-04-02 20:45:42 +08:00
Mark Nelson
a9f83a2579 Fix PHPDocs for the method get_course_field_value() 2020-11-27 20:13:33 +08:00
Mark Nelson
a81b6e4fd5 Update .travis.yml 2020-11-27 20:13:28 +08:00
Mark Nelson
8515c5bd9e Add missing full-stop to comment 2020-11-27 16:02:07 +08:00
Mark Nelson
b70224c0e4 Make tests compatible with new version of PHPUnit 2020-11-27 16:01:04 +08:00
Mark Nelson
a38678c691 3.10 release 2020-11-27 14:40:56 +08:00
Mark Nelson
35350fe454 3.9 release 2020-11-27 14:25:02 +08:00
Mark Nelson
fd4d992602 Rebuilt moodle-mod_customcert-rearrange-min.js 2020-11-27 14:25:02 +08:00
Mark Nelson
361b156f4a Fixed size of 'Changed' in CHANGES.md 2020-11-26 23:30:30 +08:00
Mark Nelson
e9b30dac79 Bumped version 2020-11-26 22:48:49 +08:00
Mark Nelson
be202676c9 Update CHANGES.md (#390) 2020-11-26 22:08:06 +08:00
uvigo-atic
da49799857 Added username to userfield form element (#390) 2020-11-26 22:06:31 +08:00
Mark Nelson
842b0910e2 Updated CHANGES.md (#276) 2020-11-26 21:41:24 +08:00
Mark Nelson
8c0e00419c Update 'emailteachers_help' and 'emailothers_help' language strings (#276) 2020-11-26 21:34:05 +08:00
Mark Nelson
c1615cbd16 Do not email out certificates with no elements (#276) 2020-11-26 19:54:58 +08:00
Mark Nelson
438dfd868f Use null coalescing operator in element factory 2020-11-26 17:52:01 +08:00
Mark Nelson
a262adfc6e Update 'emailstudents_help' language string (#276) 2020-11-25 22:38:28 +08:00
Mark Nelson
2b4f385333 Bump .travis.yml to use v3 of moodle-plugin-ci 2020-11-09 15:32:31 +01:00
Mark Nelson
62dcd9576d Update repo used by Travis 2020-11-05 20:05:56 +01:00
Mark Nelson
9a2edca684 Update CHANGES.md 2020-11-04 20:21:02 +01:00
Brendan Heywood
0c89ff9547 PDFs should be inline not attachments #153 2020-11-04 19:03:57 +01:00
Mark Nelson
27f5a74cb3 Add CSS class for mobile app (#378) 2020-10-12 13:34:04 +02:00
Mark Nelson
a8eca647fe Removed multiple empty lines 2020-09-20 16:00:34 +02:00
Mark Nelson
695798ca6f Updated CHANGES.md 2020-09-20 15:51:04 +02:00
Mark Nelson
632dba8fb2 Fixed issue with PDF being generated without a name (#333) 2020-09-20 15:39:53 +02:00
Mark Nelson
0a390df7ff Fixed exception loading template image that has no image selected (#369) 2020-09-20 14:55:48 +02:00
Mark Nelson
46ca744d32 Do not email those who can manage the certificate (#376) 2020-09-20 14:24:50 +02:00
Guillermo Gomez
60b247f441 Implement get_objectid_mapping (#374) 2020-09-20 14:13:27 +02:00
Mark Nelson
6a19ec83e9 Updated CHANGES.md 2020-08-08 19:16:54 +02:00
Mark Nelson
e52422b922 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:08:02 +02:00
Sergey Evstegneiev
2dc05b8b81 Adding enrollment start & end date options (#328) 2020-08-08 18:57:05 +02:00
Mark Nelson
c95b058049 Fix stale file warning 2020-08-08 18:10:29 +02:00
Mark Nelson
c69d4822cc Ignore multiple certificate codes (#363) 2020-08-08 15:52:10 +02:00
Mark Nelson
a884f948d3 Remove trailing spaces 2020-08-08 15:46:08 +02:00
Thong Bui
6af1757084 Fix get position when browser shrinking (#343) 2020-08-08 15:23:15 +02:00
Steven Tsvetkov
0f62eb30b5 Fix custom fields not displaying properly #359 2020-07-20 16:02:39 +02:00
Mark Nelson
66148c70f6 Updated CHANGES.md 2020-06-28 18:36:51 +02:00
Mark Nelson
98307310cf Update node version used in .travis.yml 2020-05-30 13:13:10 +02:00
Mark Nelson
05f12a3dde Mark certificates as viewed in mobile app (#342) 2020-05-30 13:03:02 +02:00
Mark Nelson
7107b67856 Refactor element_helper::get_grade_items() to be nicer to read 2020-05-08 21:23:36 +02:00
Mark Nelson
9e386f19c3 Fix docs in Grade element name 2020-05-08 17:40:26 +02:00
Mark Nelson
eec256cd01 The Grade item name element works with all grade items (#346) 2020-05-08 17:22:50 +02:00
Mark Nelson
4ab2c04b29 Extend unit test (#329) 2020-05-08 17:22:50 +02:00
Mark Nelson
784ca08fb1 Add the ability to select Outcomes in the Grade element (#329) 2020-05-08 17:22:44 +02:00
Mark Nelson
31d1f9a36d Removed 'exampledata' string 2020-05-08 15:56:41 +02:00
Mark Nelson
05c1c00cc0 Revert "Add new Outcome element (#329)"
This reverts commit 3e26a6b7c1.

This logic should be in the Grade element.
2020-05-06 14:57:27 +02:00
Mark Nelson
3e26a6b7c1 Add new Outcome element (#329) 2020-05-06 13:44:20 +02:00
Mark Nelson
4679c79e37 Bumped version 2020-03-17 00:20:30 +01:00
Mark Nelson
15b8061261 Fixed broken logic (#298) 2020-03-16 17:04:08 +01:00
Mark Nelson
2a16dbe1c0 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:42:36 +01:00
Mark Nelson
f2524610f0 Updated CHANGES.md 2020-03-12 14:42:54 +01:00
Mark Nelson
db10a589f1 Added extra Behat steps for new elements (#309) 2020-03-11 14:10:22 +01:00
Mark Nelson
a0bc5ffa04 Avoid use of old array syntax (#331) 2020-03-10 17:29:37 +01:00
Sergey
d5ba284e64 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:55:50 +01:00
Mark Nelson
f68658d675 Do not allow '0' as a value for width or height in QR code (#321) 2020-03-09 14:50:33 +01:00
Mark Nelson
28975d4625 Fix foreign key violation (#331) 2020-03-09 14:05:21 +01:00
Mark Nelson
2cdae3f45c Added missing full-stop 2019-12-16 20:56:24 +01:00
Mark Nelson
9cf95f5104 Bumped version 2019-12-16 20:53:06 +01:00
Mark Nelson
f0a25193ba Updated CHANGES.md 2019-12-16 20:53:00 +01:00
Mark Nelson
bd135ef579 Define subplugins.json (#312) 2019-12-16 20:39:08 +01:00
Mark Nelson
6a1f55c98a Fixed typo in Behat file name 2019-12-16 17:03:47 +01:00
Mark Nelson
9df6c9d71e Re-add code column (#264) 2019-12-16 16:55:33 +01:00
mwithheld
8ba89c18ce Do not fail if multiple certificate issues (#304)
Fix another location of this problem.
2019-12-16 15:59:57 +01:00
mwithheld
a5eea57644 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 15:55:42 +01:00
Mark Nelson
f172ad029b Changed references to old github name 2019-12-16 15:00:11 +01:00
Mark Nelson
59b87d19ac Bump with JS fix 2019-12-14 14:51:30 +01:00
Mark Nelson
849d6645fd Recompile JS for 3.8 2019-12-14 14:50:53 +01:00
Mark Nelson
ac8a49bcca Bumped version 2019-12-14 14:23:31 +01:00
Mark Nelson
f6b9e48691 Bumped version in travis 2019-12-14 14:19:51 +01:00
Arnaud Trouvé
080e53d13a Add userfullname variable for email subject (#316) 2019-12-14 14:17:49 +01:00
Mark Nelson
b89010ffd1 Bumped version for 3.8 2019-12-14 14:14:50 +01:00
Mark Nelson
8296796519 Bumped version 2019-06-17 17:02:25 +08:00
Mark Nelson
9f953fc928 Updated CHANGES.md 2019-06-17 14:38:13 +08:00
Mark Nelson
36d178bda3 Add ability to specify the current date for date related elements (#289) 2019-06-17 13:33:16 +08:00
Mark Nelson
7756d928ac String improvements for date range element
Also added a help button to reduce the wall
of text.
2019-06-17 12:41:02 +08:00
Mark Nelson
7611196fa3 Fixed PHPDoc reference to edit_element_form 2019-06-17 12:14:26 +08:00
Mark Nelson
68838944fa 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:29:06 +08:00
Dan Marsden
7dd18f75e2 Add custom course field element to custom cert (#274) 2019-05-29 16:07:30 +08:00
Mark Nelson
7e60325e17 Bumped PHP minimum version used by travis 2019-05-29 00:35:37 +08:00
Mark Nelson
688d243eee Specify OS in travis 2019-05-29 00:18:51 +08:00
Mark Nelson
0d67b87908 Update travis to use Moodle 3.7 2019-05-29 00:12:10 +08:00
Mark Nelson
38def6bfb4 Fix complaints found by local_moodlecheck 2019-05-29 00:12:10 +08:00
Mark Nelson
cbf9997f3e Bump version for 3.7 2019-05-28 18:29:44 +08:00
Mark Nelson
8358280c6c Added capability to determine who can receive a certificate (#270) 2019-05-28 17:52:52 +08:00
Mark Nelson
557a2aee18 Updated CHANGES.md 2019-05-28 17:52:50 +08:00
Mark Nelson
88dcb9142a Minor changes (#185) 2019-05-28 14:20:30 +08:00
Dmitrii Metelkin
1fbf233111 Use repeat form element for date ranges (#185) 2019-05-25 15:01:26 +08:00
Dmitrii Metelkin
3450388a36 Remove empty line (#185) 2019-05-25 15:01:13 +08:00
Dmitrii Metelkin
5b44544e74 Add new placeholders and update help description (#185) 2019-05-25 15:01:02 +08:00
Dmitrii Metelkin
597659e23e Rename first_year and last_year placeholders (#185) 2019-05-25 15:00:49 +08:00
Dmitrii Metelkin
c31f31a63d Update description string (#185) 2019-05-25 15:00:41 +08:00
Dmitrii Metelkin
fafb0159d8 Add tests to support functionality (#185) 2019-05-25 15:00:33 +08:00
Dmitrii Metelkin
6e091e9fd6 Improve the logic of recurring ranges (#185) 2019-05-25 15:00:19 +08:00
Dmitrii Metelkin
4ac9615514 Recursively check recurring date ranges (#185) 2019-05-25 15:00:12 +08:00
Dmitrii Metelkin
d0bca80d35 Add an empty line to the end of styles.css (#185) 2019-05-25 15:00:03 +08:00
Dmitrii Metelkin
386592d27b Update lang strings (#185) 2019-05-25 14:59:56 +08:00
Dmitrii Metelkin
a7441e8b1b Be more strict about 12 months limit (#185) 2019-05-25 14:59:48 +08:00
Dmitrii Metelkin
fbe8024438 Modify recurring dateranges (#185) 2019-05-25 14:59:23 +08:00
Dmitrii Metelkin
4e30f1e851 Add range related placeholders (#185) 2019-05-25 14:59:15 +08:00
Dmitrii Metelkin
5a9d9792df Get rid of useless css rule (#185) 2019-05-25 14:59:03 +08:00
Dmitrii Metelkin
19d0bf5dc8 Change a way of displaying date ranges on the edit element page (#185) 2019-05-25 14:58:52 +08:00
Dmitrii Metelkin
b26e8c92b9 Add recurring option to dateranges (#185) 2019-05-25 14:58:41 +08:00
Dmitrii Metelkin
b6ce73637c Use element name in the preview mode (#185) 2019-05-25 14:58:31 +08:00
Mark Nelson
1a172b9065 Bump date range element version numbers (#185) 2019-05-25 14:58:27 +08:00
Dmitrii Metelkin
7436054c17 Add date range element (#185) 2019-05-25 14:58:13 +08:00
Mark Nelson
0b0b37ed35 Check that a certificate issue exists (#269) 2019-05-23 12:04:19 +08:00
Mark Nelson
1ac494df93 Make travis happy (#282) 2019-05-23 12:04:12 +08:00
Sagar Ghimire
f0733f4e7d Fix failing test_get_grades (#282) 2019-05-23 12:04:01 +08:00
Mark Nelson
8467b847b2 Use openjdk-8-jre-headless instead of oracle-java8 in .travis.yml 2019-05-23 10:45:41 +08:00
Mark Nelson
4f4a999439 Readded number of certificates issued (#266) 2019-04-16 20:54:17 +08:00
Mark Nelson
b808ff4ead Removed unnecessary inclusion 2019-04-16 20:54:17 +08:00
Mark Nelson
14e369b775 Specify grades' decimal places in unit test (#277) 2019-04-16 20:54:09 +08:00
Mark Nelson
1746ec47dc Always send emails from 'noreplyuser' (#165) 2019-04-16 19:37:07 +08:00
Mark Nelson
a6b180454f Incorrect PHPDocs for render_form_elements and co (#271) 2019-04-16 19:22:57 +08:00
Mark Nelson
813fae127c Added QR code element (#146) 2019-04-16 19:04:53 +08:00
Nicolas Roeser
a05996d656 Add vertical spacing after certificate download button
Increase the vertical spacing after the single button for downloading
user’s own certificate. This makes the page look better.
2019-02-04 12:06:56 +08:00
Mark Nelson
2ea80b1084 Fixed misleading PHP comment referring to 3.5 2019-01-03 06:22:31 +08:00
Mark Nelson
544c6ed67b Bumped version 2018-12-31 10:35:17 +08:00
Mark Nelson
85e84348fc Updated CHANGES.md 2018-12-31 10:35:17 +08:00
Mark Nelson
7c333442ee Make it clear what element values are just an example (#144) 2018-12-31 09:54:26 +08:00
Mark Nelson
61d0bd433f Use course module context when calling format_string/text (#200) 2018-12-28 14:11:58 +08:00
Mark Nelson
f8a55869b5 Prevent exception when adding 'teachername' element to site template (#261) 2018-12-27 09:05:45 +08:00
Mark Nelson
5807d5e2dd Added missing details to CHANGES.md 2018-12-27 07:40:44 +08:00
Mark Nelson
7002818fca Added missing implementation for privacy provider (#260) 2018-12-26 11:34:51 +08:00
Mark Nelson
7b40489019 Update travis (again) 2018-12-24 16:31:58 +08:00
Mark Nelson
2cee556f9f Update travis 2018-12-24 16:17:39 +08:00
Mark Nelson
b211a06bd5 Bumped version for 3.6 2018-12-24 15:50:40 +08:00
Mark Nelson
ed2eccd430 Bumped version 2018-12-24 15:19:57 +08:00
Mark Nelson
7ebb5b0ab5 Updated CHANGES.md 2018-12-20 12:32:51 +08:00
Mark Nelson
13ddab941f Add ability to set transparency on images (#186) 2018-12-20 09:16:31 +08:00
Mark Nelson
9e9192c7fc Fixed issue when scales are used (#242)
The grade item's type was being hard-coded to
GRADE_TYPE_VALUE when this isn't always the
case.
2018-12-19 14:37:06 +08:00
Mark Nelson
32338c9aa7 Make use of the decimal value setting
We should use this instead of our own logic.
2018-12-19 14:19:27 +08:00
Mark Nelson
2bfb6b53e0 Allow verification of deleted users (#159) 2018-12-18 11:25:25 +08:00
Mark Nelson
5182ab60e2 Report now respects the setting 'Show user identity' (#224) 2018-12-18 09:43:43 +08:00
Mark Nelson
a55b253cff Fixed logic so site templates work as expected (#160) 2018-12-17 16:41:21 +08:00
Mark Nelson
cf93b540d6 Hide completion date option if not applicable (#160) 2018-12-17 16:28:16 +08:00
mwithheld
97e44d3d1d Move creating the temp directory out of the emailing for loop (#238) 2018-12-17 15:23:50 +08:00
Mark Nelson
9834d7f4b8 Removed boolean return and added a comment (#237) 2018-12-17 14:13:15 +08:00
mwithheld
255743f9e0 Reduce code nesting level in task (#237)
Make the code easier to read and understand
2018-12-17 14:10:30 +08:00
Mark Nelson
988af1c457 Change field 'element' to character rather than text (#241) 2018-12-17 12:36:37 +08:00
Aaron Parker
76a20a80e0 Added plaintext lang variants for email bodies and updated template (#230) 2018-12-17 11:00:51 +08:00
james-cnz
ff02ba71aa Matched up prevent x description strings (#254)
The description strings for the settings prevent copy, prevent print, and prevent modify were mixed up when added with:
#180 Add possibility to set default values of activity instance
2018-12-17 10:51:58 +08:00
Sara Arjona
7ae3101ea1 #252 Privacy API update
Add support to get_users_in_context and delete_data_for_users methods
added since 3.4.6 and 3.5.3 and 3.6.
2018-12-17 10:13:15 +08:00
Mark Nelson
2ebb734145 Changed reference to moodlerooms (#251) 2018-12-17 09:48:33 +08:00
Marina Glancy
e6d74ec047 Updated travis (#251)
Bring the travis.yml up to date with https://github.com/blackboard-open-source/moodle-plugin-ci/blob/master/.travis.dist.yml
use grunt to validate js and css
add validation of mustache, savepoints, allow javascript behat tests
2018-12-17 09:47:50 +08:00
Mark Nelson
c8939ae526 #225 Renamed static function 2018-08-23 20:37:32 +08:00
Alexander Bias
fcaf1fefd5 #225 Allow element plugins to control if they can be added 2018-08-23 20:15:52 +08:00
Mark Nelson
706912c52f Revert version bump until release 2018-08-07 11:47:36 +08:00
Mark Nelson
7de488d638 #223 Removed incorrect course reset logic 2018-08-06 20:32:15 +08:00
Dmitrii Metelkin
804b0ec359 Add tests to support new permissions and default activity options 2018-08-06 18:44:58 +08:00
Dmitrii Metelkin
d169b6958c #179 Don't display form elements if a user doesn't have capabilities 2018-08-06 18:44:58 +08:00
Dmitrii Metelkin
b797b659f5 #180 Add possibility to set default values of activity instance 2018-08-06 18:44:44 +08:00
Dmitrii Metelkin
13ab862856 #179 Add possibility to selectively disable activity instance settings 2018-08-04 12:52:15 +08:00
Mark Nelson
64865aff4e Added LICENSE.md 2018-08-04 12:44:21 +08:00
Mark Nelson
210e61e227 Fixed misleading version number in requires comment 2018-07-28 12:43:03 +08:00
Dmitrii Metelkin
ee301654d3 #213 Allow element plugins to have admin settings 2018-07-27 16:42:47 +08:00
Mark Nelson
13c1998c49 Bumped version 2018-07-13 22:40:55 +08:00
Mark Nelson
b254116e4d Updated CHANGES.md 2018-07-13 22:03:59 +08:00
Mark Nelson
26ea75479f #171 Only get teachers who are enrolled within the course 2018-07-13 20:46:17 +08:00
Tristan Mackay
9f2a36d983 #176 Only get users details who are enrolled within the course 2018-07-13 14:46:38 +08:00
Mark Nelson
9440dea72c #210 Allow short dates with leading zeros 2018-07-13 12:17:35 +08:00
Mark Nelson
ef285e6075 #210 Improve code for adding date strings to select 2018-07-12 18:09:47 +08:00
Mark Nelson
39ccef311e #154 Don't display 'Add page' link for unsaved templates 2018-07-12 16:13:34 +08:00
Mark Nelson
91da92f10d #187 Fix broken SQL on Oracle 2018-07-12 15:16:24 +08:00
Mark Nelson
54212da3d2 #211 Use custom fonts if present 2018-07-12 12:39:35 +08:00
Mark Nelson
2500fada80 #207 Changed download button between managers and non-managers on mobile
Also minor changes to 'notissued' string and fixed reference to
non-existent string 'notissuedyet'.
2018-07-10 14:47:02 +08:00
Mark Nelson
f3cff401b5 #207 No need to use a table/list when displaying only one issue 2018-07-10 11:13:42 +08:00
Mark Nelson
47f1102545 #207 Changed font-size for awarded time in the report in mobile view
Also made minor alignment changes.
2018-07-09 22:50:44 +08:00
Mark Nelson
c395c7dadc #207 Follow-up UX improvements
The majority of this change involved moving
the report page logic into the view page for
both browser and mobile.
2018-07-01 20:37:20 +08:00
Mark Nelson
6926280807 Removed unnecessary new lines 2018-06-26 21:34:55 +08:00
Mark Nelson
973d81b697 Bumped version 2018-06-26 21:19:44 +08:00
Mark Nelson
002cebd47c #203 Added missing consistency changes for headings/titles 2018-06-26 20:11:01 +08:00
Mark Nelson
5d6e1e4250 Updated CHANGES.md 2018-06-26 19:35:25 +08:00
Mark Nelson
86551f18c7 #203 Consistency among headings/titles on all pages 2018-06-26 19:25:16 +08:00
Mark Nelson
51c0741ecb #203 Removed 'code' column
Not all certificates contain a 'code' element,
making this column unnecessary. It may also
confuse users when it is not applicable. For
those who are interested they can always use
the 'verify' functionality to confirm
certificates, rather than using this page.
2018-06-26 19:25:02 +08:00
Mark Nelson
ef67beadf6 #203 Improved 'listofissues' string 2018-06-26 14:54:40 +08:00
Mark Nelson
9d68fcead2 #203 Make report heading/title consistent with core activities 2018-06-25 14:49:26 +08:00
Mark Nelson
bc527a0669 #203 Changed 'listofissues' string to avoid confusion 2018-06-20 17:44:03 +08:00
Mark Nelson
8fbc6a0f13 #203 Added heading to activity view page in browser 2018-06-20 17:03:51 +08:00
Mark Nelson
9a21f67089 #203 Added new string for list of awarded certificates 2018-06-19 21:37:33 +08:00
Mark Nelson
e7a7f0aef5 #203 Don't display group select on activity view page 2018-06-19 21:37:33 +08:00
Mark Nelson
de2709ae54 #203 Don't show 'Awarded on' date to managers 2018-06-19 21:37:33 +08:00
Mark Nelson
c18c84b0df Fixed reference to 'mod/certificate' capability 2018-06-19 21:37:33 +08:00
Mark Nelson
3727ff4fbd #203 Make mobile layout friendly on small screens 2018-06-19 21:37:33 +08:00
Mark Nelson
9a32a99f65 #203 Removed 'issued' string and replaced with 'receiveddate'
Also renamed 'Received date' to 'Awarded on'.
2018-06-19 21:02:01 +08:00
Mark Nelson
74d0fb363f #203 Removed confusing 'summaryofissue' string 2018-06-19 19:27:02 +08:00
Mark Nelson
2868082e9e #197 Respect filters in 'My certificates' and 'Verify certificate' pages 2018-06-12 16:43:51 +08:00
Mark Nelson
7bff2c5367 #196 Use FORMAT_HTML when calling format_text() (again)
This commit is not as vital as the previous one as it
does not affect the layout of the PDF.
2018-06-07 20:10:38 +08:00
Mark Nelson
033fdc7ee2 Bumped version 2018-06-07 19:37:34 +08:00
Mark Nelson
05df874d2a Updated CHANGES.md 2018-06-07 19:37:34 +08:00
Mark Nelson
32cbb26a21 Add example JSON context to templates 2018-06-07 19:29:34 +08:00
Mark Nelson
308b8d1e19 #196 Use FORMAT_HTML when calling format_text() 2018-06-07 18:55:54 +08:00
Mark Nelson
2687ee737f Updated CHANGES.md 2018-06-06 23:01:19 +08:00
Mark Nelson
08a385fc85 #70 Added mobile app support
This allows students to view the activity and download
their certificate. It also allows teachers to view the
list of issued certificates, with the ability to revoke
any.

This is for Moodle Mobile v3.5.0 (not to be confused with
your Moodle site version) and will not work on Mobile
versions earlier than this.

If you are running a Moodle site on version 3.4 or below
you will need to install the local_mobile plugin in order
for this to work.

If you are running a Moodle site on version 3.0 or below
then you will need to upgrade.
2018-06-06 22:58:47 +08:00
Mark Nelson
3b2a45d755 #170 Elements now respect filters 2018-06-06 22:58:47 +08:00
Mark Nelson
be9c466f4a Fixed typo in PHPDoc 2018-06-06 21:31:47 +08:00
Roberto Pinna
7a20d0778b #192 Fixed report fields for download 2018-05-26 18:23:54 +08:00
Mark Nelson
c61e687080 Fixed misleading comment in version.php 2018-05-26 18:22:42 +08:00
Mark Nelson
ab32f72644 Fixed PHPDoc 2018-05-26 18:22:35 +08:00
Mark Nelson
3c6a8b97f3 Added helper function to issue certificates 2018-05-26 18:22:27 +08:00
Mark Nelson
f0b8292779 Changed icon for downloading certificate from reports
This was done because the core 'import' icon was mapped
to the Font Awesome icon 'fa-level-up' which did not look
appropriate.
2018-05-22 11:35:48 +08:00
Mark Nelson
3620b5543d #148 Increase size limit on fonts 2018-05-17 19:42:36 +08:00
Mark Nelson
b301b44748 Update travis.yml to use Moodle 3.5 2018-05-17 17:29:15 +08:00
Mark Nelson
13eb4509de Bump version for 3.5 2018-05-17 17:19:43 +08:00
111 changed files with 3871 additions and 513 deletions

120
.github/workflows/moodle-ci.yml vendored Normal file
View file

@ -0,0 +1,120 @@
name: Moodle Plugin CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-18.04
services:
postgres:
image: postgres:9.6
env:
POSTGRES_USER: 'postgres'
POSTGRES_HOST_AUTH_METHOD: 'trust'
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3
mariadb:
image: mariadb:10.5
env:
MYSQL_USER: 'root'
MYSQL_ALLOW_EMPTY_PASSWORD: "true"
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 3
strategy:
fail-fast: false
matrix:
include:
- php: '7.4'
moodle-branch: 'MOODLE_311_STABLE'
database: pgsql
- php: '7.4'
moodle-branch: 'MOODLE_311_STABLE'
database: mariadb
- php: '7.3'
moodle-branch: 'MOODLE_311_STABLE'
database: pgsql
- php: '7.3'
moodle-branch: 'MOODLE_311_STABLE'
database: mariadb
steps:
- name: Check out repository code
uses: actions/checkout@v2
with:
path: plugin
- name: Setup PHP ${{ matrix.php }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
- name: Initialise moodle-plugin-ci
run: |
composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3
echo $(cd ci/bin; pwd) >> $GITHUB_PATH
echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH
sudo locale-gen en_AU.UTF-8
echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV
- name: Install moodle-plugin-ci
run: moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1
env:
DB: ${{ matrix.database }}
MOODLE_BRANCH: ${{ matrix.moodle-branch }}
MUSTACHE_IGNORE_NAMES: 'mobile_*.mustache'
- name: PHP Lint
if: ${{ always() }}
run: moodle-plugin-ci phplint
- name: PHP Copy/Paste Detector
continue-on-error: true # This step will show errors but will not fail
if: ${{ always() }}
run: moodle-plugin-ci phpcpd
- name: PHP Mess Detector
continue-on-error: true # This step will show errors but will not fail
if: ${{ always() }}
run: moodle-plugin-ci phpmd
- name: Moodle Code Checker
if: ${{ always() }}
run: moodle-plugin-ci codechecker --max-warnings 0
- name: Moodle PHPDoc Checker
if: ${{ always() }}
run: moodle-plugin-ci phpdoc
- name: Validating
if: ${{ always() }}
run: moodle-plugin-ci validate
- name: Check upgrade savepoints
if: ${{ always() }}
run: moodle-plugin-ci savepoints
- name: Mustache Lint
if: ${{ always() }}
run: moodle-plugin-ci mustache
- name: Grunt
if: ${{ always() }}
run: moodle-plugin-ci grunt --max-lint-warnings 0
- name: PHPUnit tests
if: ${{ always() }}
run: |
moodle-plugin-ci phpunit
cd moodle
vendor/bin/phpunit --fail-on-risky --disallow-test-output --filter tool_dataprivacy_metadata_registry_testcase
vendor/bin/phpunit --fail-on-risky --disallow-test-output --filter core_externallib_testcase
vendor/bin/phpunit --fail-on-risky --disallow-test-output --testsuite core_privacy_testsuite --filter provider_testcase
- name: Behat features
if: ${{ always() }}
run: moodle-plugin-ci behat --profile chrome

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
vendor/
*.swp

View file

@ -1,52 +0,0 @@
language: php
# For javascript behat tests we need sudo
sudo: true
cache:
directories:
- $HOME/.composer/cache
- $HOME/.npm
php:
- 7.0
- 7.2
addons:
firefox: 47.0.1
postgresql: 9.3
apt:
packages:
- oracle-java8-installer
- oracle-java8-set-default
env:
global:
- MOODLE_BRANCH=MOODLE_34_STABLE
- IGNORE_NAMES=mobile_*.mustache # Mobile mustache has specific syntax, ignore their templates
matrix:
- DB=pgsql
- DB=mysqli
before_install:
- phpenv config-rm xdebug.ini
- nvm install 8.9
- nvm use 8.9
- cd ../..
- composer create-project -n --no-dev --prefer-dist blackboard-open-source/moodle-plugin-ci ci ^2
- export PATH="$(cd ci/bin; pwd):$(cd ci/vendor/bin; pwd):$PATH"
install:
- moodle-plugin-ci install
script:
- moodle-plugin-ci phplint
# - moodle-plugin-ci phpcpd # subplugins often have similar code and cause "duplicated code" errors
# - moodle-plugin-ci phpmd # too much noise from this check, maybe, some day...
- moodle-plugin-ci codechecker
- moodle-plugin-ci validate
- moodle-plugin-ci savepoints
- moodle-plugin-ci mustache
- moodle-plugin-ci grunt -t stylelint:css -t js
- moodle-plugin-ci phpunit
- moodle-plugin-ci behat

View file

@ -2,9 +2,140 @@
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.4.7] - 2018-12-31 ## [3.11.2] - 2021-??-??
### Fixed
- Fix places not using the multi-language filter (#433).
- Fix user IDs in the issue table not being mapped during restore (#449).
- Fix emails displaying HTML entities encoded (#457).
- Fix error message when we have custom profile fields (#465).
- Respect multiple languages in manage template page title (#467).
### Added
- You can now choose the course short or full name to display (#415).
- You can now select the alignment for all text elements (#121).
## [3.11.1] - 2021-06-13
### Fixed
- Usage of deprecated functions (#423)
## [3.10.1] - 2021-06-13
### Added
- Usage of github actions (#407).
- The ability to show the description on the course page (#406).
- The ability to choose how to deliver the certificate (#401).
### Fixed
- Managers are now able to download their students' certificates (#412).
- Users being able to view the certificate before the required time set (#403).
- Fixed the issue with displaying PDF when debugging is ON (#420).
- Using incorrect context when sending emails (#402).
- Use `cron_setup_user` when sending emails (#414).
## [3.8.5] - 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).
- Custom fields not displaying properly (#359).
- 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.8.4] - 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.8.3] - 2020-03-09
### Fixed
- Fixed foreign key violation (#331).
## [3.8.2] - 2019-12-16
### Added
- Added subplugins.json file (#312).
- 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.7.1] - 2019-06-17
### Added
- Added new custom course field element (#274).
- 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
### Changed
- Always send emails from the 'noreplyuser' (#165).
### Added
- Added QR code element (#146).
- Added Date range element (#185).
- Added the number of certificates issued above the report (#266).
- Added new capability to control who can be issued a certificate (#270).
### Fixed
- Failures when running unit tests for multiple activities (#282).
- Check that a certificate is valid before downloading on 'My certificates' page (#269).
## [3.6.1] - 2018-12-31
### Changed ### Changed
@ -16,7 +147,7 @@ Note - All hash comments refer to the issue number. Eg. #169 refers to https://g
- Use course module context when calling format_string/text (#200). - Use course module context when calling format_string/text (#200).
- Exception being thrown when adding the 'teachername' element to site template (#261). - Exception being thrown when adding the 'teachername' element to site template (#261).
## [3.4.6] - 2018-12-20 ## [3.5.5] - 2018-12-20
### Added ### Added
- GDPR: Add support for removal of users from a context (see MDL-62560) (#252). - GDPR: Add support for removal of users from a context (see MDL-62560) (#252).
@ -42,7 +173,7 @@ Note - All hash comments refer to the issue number. Eg. #169 refers to https://g
- Removed incorrect course reset logic (#223). - Removed incorrect course reset logic (#223).
- Description strings referring to the wrong setting (#254). - Description strings referring to the wrong setting (#254).
## [3.4.5] - 2018-07-13 ## [3.5.4] - 2018-07-13
### Fixed ### Fixed
- Use custom fonts if present (#211). - Use custom fonts if present (#211).
@ -57,23 +188,22 @@ Note - All hash comments refer to the issue number. Eg. #169 refers to https://g
- One big change here is combining the report and activity view page into one. - One big change here is combining the report and activity view page into one.
- Allow short dates with leading zeros (#210). - Allow short dates with leading zeros (#210).
## [3.4.4] - 2018-06-26 ## [3.5.3] - 2018-06-26
### Fixed ### Fixed
- Respect filters in the 'My certificates' and 'Verify certificate' pages (#197). - Respect filters in the 'My certificates' and 'Verify certificate' pages (#197).
- Fixed reference to 'mod/certificate' capability. - Fixed reference to 'mod/certificate' capability.
- Provided access to necessary web services for mobile functionality to the local_mobile plugin (#202).
### Changed ### Changed
- Multiple UX improvements to both the browser and mobile views (#203). - Multiple UX improvements to both the browser and mobile views (#203).
## [3.4.3] - 2018-06-07 ## [3.5.2] - 2018-06-07
### Fixed ### Fixed
- Hotfix to prevent misalignment of 'text' elements after last release (#196). - Hotfix to prevent misalignment of 'text' elements after last release (#196).
## [3.4.2] - 2018-06-06 ## [3.5.1] - 2018-06-06
### Added ### Added
- Mobile app support (#70). - Mobile app support (#70).
``` ```

View file

@ -6,6 +6,8 @@ This activity allows the dynamic generation of PDF certificates with complete cu
There are two installation methods that are available. There are two installation methods that are available.
First, make sure that wkhtmltopdf is installed on your server and available on $PATH.
Follow one of these, then log into your Moodle site as an administrator and visit the notifications page to complete the install. Follow one of these, then log into your Moodle site as an administrator and visit the notifications page to complete the install.
### Git ### Git
@ -15,7 +17,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

@ -1 +1,2 @@
define(["core/yui"],function(a){var b=function(b,c,d,e,f){this.yuiDialogue=null;var g=this;"undefined"==typeof f&&(f=!1),a.use("moodle-core-notification","timers",function(){var h="480px";f&&(h="800px"),g.yuiDialogue=new M.core.dialogue({headerContent:b,bodyContent:c,draggable:!0,visible:!1,center:!0,modal:!0,width:h}),g.yuiDialogue.after("visibleChange",function(b){b.newVal?"undefined"!=typeof d&&a.soon(function(){d(g),g.yuiDialogue.centerDialogue()}):"undefined"!=typeof e&&a.soon(function(){e(g)})}),g.yuiDialogue.show()})};return b.prototype.close=function(){this.yuiDialogue.hide(),this.yuiDialogue.destroy()},b.prototype.getContent=function(){return this.yuiDialogue.bodyNode.getDOMNode()},b}); define ("mod_customcert/dialogue",["core/yui"],function(a){var b=function(b,c,d,e,f){this.yuiDialogue=null;var g=this;if("undefined"==typeof f){f=!1}a.use("moodle-core-notification","timers",function(){var h="480px";if(f){h="800px"}g.yuiDialogue=new M.core.dialogue({headerContent:b,bodyContent:c,draggable:!0,visible:!1,center:!0,modal:!0,width:h});g.yuiDialogue.after("visibleChange",function(b){if(b.newVal){if("undefined"!=typeof d){a.soon(function(){d(g);g.yuiDialogue.centerDialogue()})}}else{if("undefined"!=typeof e){a.soon(function(){e(g)})}}});g.yuiDialogue.show()})};b.prototype.close=function(){this.yuiDialogue.hide();this.yuiDialogue.destroy()};b.prototype.getContent=function(){return this.yuiDialogue.bodyNode.getDOMNode()};return b});
//# sourceMappingURL=dialogue.min.js.map

File diff suppressed because one or more lines are too long

View file

@ -1 +1,2 @@
define(["jquery","core/yui","core/fragment","mod_customcert/dialogue","core/notification","core/str","core/templates","core/ajax"],function(a,b,c,d,e,f,g,h){var i=function(b){this._node=a(b),this._setEvents()};return i.prototype.CUSTOMCERT_REF_POINT_TOPLEFT=0,i.prototype.CUSTOMCERT_REF_POINT_TOPCENTER=1,i.prototype.CUSTOMCERT_REF_POINT_TOPRIGHT=2,i.prototype.PIXELSINMM=3.779527559055,i.prototype._setEvents=function(){this._node.on("click",".element",this._editElement.bind(this))},i.prototype._editElement=function(a){var g=a.currentTarget.id.substr(8),h=this._node.attr("data-contextid"),i={elementid:g};c.loadFragment("mod_customcert","editelement",h,i).done(function(a,c){f.get_string("editelement","mod_customcert").done(function(e){b.use("moodle-core-formchangechecker",function(){new d(e,"<div id='elementcontent'></div>",this._editElementDialogueConfig.bind(this,g,a,c),(void 0),(!0))}.bind(this))}.bind(this))}.bind(this)).fail(e.exception)},i.prototype._editElementDialogueConfig=function(b,c,d,f){g.replaceNode("#elementcontent",c,d),this._setPositionInForm(b);var h=a(f.getContent());h.on("click","#id_submitbutton",function(c){M.core_formchangechecker.reset_form_dirty_state(),this._saveElement(b).then(function(){this._getElementHTML(b).done(function(c){var d=this._node.find("#element-"+b),e=parseInt(a("#id_refpoint").val()),g="";e==this.CUSTOMCERT_REF_POINT_TOPLEFT?g="refpoint-left":e==this.CUSTOMCERT_REF_POINT_TOPCENTER?g="refpoint-center":e==this.CUSTOMCERT_REF_POINT_TOPRIGHT&&(g="refpoint-right"),d.empty().append(c),d.removeClass(),d.addClass("element "+g),d.attr("data-refpoint",e);var h=a("#editelementform #id_posx").val(),i=a("#editelementform #id_posy").val();this._setPosition(b,e,h,i),f.close()}.bind(this))}.bind(this)).fail(e.exception),c.preventDefault()}.bind(this)),h.on("click","#id_cancel",function(a){f.close(),a.preventDefault()})},i.prototype._setPosition=function(a,c,d,e){var f=b.one("#element-"+a);d=b.one("#pdf").getX()+d*this.PIXELSINMM,e=b.one("#pdf").getY()+e*this.PIXELSINMM;var g=parseFloat(f.getComputedStyle("width")),h=f.width*this.PIXELSINMM;switch(h&&g>h&&(g=h),c){case this.CUSTOMCERT_REF_POINT_TOPCENTER:d-=g/2;break;case this.CUSTOMCERT_REF_POINT_TOPRIGHT:d=d-g+2}f.setX(d),f.setY(e)},i.prototype._setPositionInForm=function(c){var d=a("#editelementform #id_posx"),e=a("#editelementform #id_posy");if(d.length&&e.length){var f=b.one("#element-"+c),g=f.getX()-b.one("#pdf").getX(),h=f.getY()-b.one("#pdf").getY(),i=parseInt(f.getData("refpoint")),j=parseFloat(f.getComputedStyle("width"));switch(i){case this.CUSTOMCERT_REF_POINT_TOPCENTER:g+=j/2;break;case this.CUSTOMCERT_REF_POINT_TOPRIGHT:g+=j}g=Math.round(parseFloat(g/this.PIXELSINMM)),h=Math.round(parseFloat(h/this.PIXELSINMM)),d.val(g),e.val(h)}},i.prototype._getElementHTML=function(a){var b=this._node.attr("data-templateid"),c=h.call([{methodname:"mod_customcert_get_element_html",args:{templateid:b,elementid:a}}]);return c[0]},i.prototype._saveElement=function(b){var c=this._node.attr("data-templateid"),d=a("#editelementform").serializeArray(),e=h.call([{methodname:"mod_customcert_save_element",args:{templateid:c,elementid:b,values:d}}]);return e[0]},{init:function(a){new i(a)}}}); define ("mod_customcert/rearrange-area",["jquery","core/yui","core/fragment","mod_customcert/dialogue","core/notification","core/str","core/templates","core/ajax"],function(a,b,c,d,f,g,h,i){var j=function(b){this._node=a(b);this._setEvents()};j.prototype.CUSTOMCERT_REF_POINT_TOPLEFT=0;j.prototype.CUSTOMCERT_REF_POINT_TOPCENTER=1;j.prototype.CUSTOMCERT_REF_POINT_TOPRIGHT=2;j.prototype.PIXELSINMM=3.779527559055;j.prototype._setEvents=function(){this._node.on("click",".element",this._editElement.bind(this))};j.prototype._editElement=function(a){var e=a.currentTarget.id.substr(8),h=this._node.attr("data-contextid");c.loadFragment("mod_customcert","editelement",h,{elementid:e}).done(function(a,c){g.get_string("editelement","mod_customcert").done(function(f){b.use("moodle-core-formchangechecker",function(){new d(f,"<div id='elementcontent'></div>",this._editElementDialogueConfig.bind(this,e,a,c),void 0,!0)}.bind(this))}.bind(this))}.bind(this)).fail(f.exception)};j.prototype._editElementDialogueConfig=function(b,c,d,g){h.replaceNode("#elementcontent",c,d);this._setPositionInForm(b);var i=a(g.getContent());i.on("click","#id_submitbutton",function(c){M.core_formchangechecker.reset_form_dirty_state();this._saveElement(b).then(function(){this._getElementHTML(b).done(function(c){var d=this._node.find("#element-"+b),e=parseInt(a("#id_refpoint").val()),f="";if(e==this.CUSTOMCERT_REF_POINT_TOPLEFT){f="refpoint-left"}else if(e==this.CUSTOMCERT_REF_POINT_TOPCENTER){f="refpoint-center"}else if(e==this.CUSTOMCERT_REF_POINT_TOPRIGHT){f="refpoint-right"}d.empty().append(c);d.removeClass();d.addClass("element "+f);d.attr("data-refpoint",e);var h=a("#editelementform #id_posx").val(),i=a("#editelementform #id_posy").val();this._setPosition(b,e,h,i);g.close()}.bind(this))}.bind(this)).fail(f.exception);c.preventDefault()}.bind(this));i.on("click","#id_cancel",function(a){g.close();a.preventDefault()})};j.prototype._setPosition=function(a,c,d,e){var f=b.one("#element-"+a);d=b.one("#pdf").getX()+d*this.PIXELSINMM;e=b.one("#pdf").getY()+e*this.PIXELSINMM;var g=parseFloat(f.getComputedStyle("width")),h=f.width*this.PIXELSINMM;if(h&&g>h){g=h}switch(c){case this.CUSTOMCERT_REF_POINT_TOPCENTER:d-=g/2;break;case this.CUSTOMCERT_REF_POINT_TOPRIGHT:d=d-g+2;break;}f.setX(d);f.setY(e)};j.prototype._setPositionInForm=function(c){var d=a("#editelementform #id_posx"),e=a("#editelementform #id_posy");if(d.length&&e.length){var f=b.one("#element-"+c),g=f.getX()-b.one("#pdf").getX(),h=f.getY()-b.one("#pdf").getY(),i=parseInt(f.getData("refpoint")),j=parseFloat(f.getComputedStyle("width"));switch(i){case this.CUSTOMCERT_REF_POINT_TOPCENTER:g+=j/2;break;case this.CUSTOMCERT_REF_POINT_TOPRIGHT:g+=j;break;}g=Math.round(parseFloat(g/this.PIXELSINMM));h=Math.round(parseFloat(h/this.PIXELSINMM));d.val(g);e.val(h)}};j.prototype._getElementHTML=function(a){var b=this._node.attr("data-templateid"),c=i.call([{methodname:"mod_customcert_get_element_html",args:{templateid:b,elementid:a}}]);return c[0]};j.prototype._saveElement=function(b){var c=this._node.attr("data-templateid"),d=a("#editelementform").serializeArray(),e=i.call([{methodname:"mod_customcert_save_element",args:{templateid:c,elementid:b,values:d}}]);return e[0]};return{init:function init(a){new j(a)}}});
//# sourceMappingURL=rearrange-area.min.js.map

File diff suppressed because one or more lines are too long

View file

@ -18,7 +18,6 @@
* use the YUI version in AMD code until it is replaced. * use the YUI version in AMD code until it is replaced.
* *
* @module mod_customcert/dialogue * @module mod_customcert/dialogue
* @package mod_customcert
* @copyright 2016 Mark Nelson <markn@moodle.com> * @copyright 2016 Mark Nelson <markn@moodle.com>
* @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
*/ */

View file

@ -17,7 +17,6 @@
* AMD module used when rearranging a custom certificate. * AMD module used when rearranging a custom certificate.
* *
* @module mod_customcert/rearrange-area * @module mod_customcert/rearrange-area
* @package mod_customcert
* @copyright 2016 Mark Nelson <markn@moodle.com> * @copyright 2016 Mark Nelson <markn@moodle.com>
* @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
*/ */

View file

@ -56,7 +56,7 @@ class backup_customcert_activity_task extends backup_activity_task {
* @param string $content * @param string $content
* @return mixed|string * @return mixed|string
*/ */
static public function encode_content_links($content) { public static function encode_content_links($content) {
global $CFG; global $CFG;
$base = preg_quote($CFG->wwwroot, "/"); $base = preg_quote($CFG->wwwroot, "/");

View file

@ -59,7 +59,7 @@ class backup_customcert_activity_structure_step extends backup_activity_structur
$element = new backup_nested_element('element', array('id'), array( $element = new backup_nested_element('element', array('id'), array(
'pageid', 'name', 'element', 'data', 'font', 'fontsize', 'pageid', 'name', 'element', 'data', 'font', 'fontsize',
'colour', 'posx', 'posy', 'width', 'refpoint', 'sequence', 'colour', 'posx', 'posy', 'width', 'refpoint', 'sequence',
'timecreated', 'timemodified')); 'alignment', 'timecreated', 'timemodified'));
// The issues. // The issues.
$issues = new backup_nested_element('issues'); $issues = new backup_nested_element('issues');

View file

@ -53,7 +53,7 @@ class restore_customcert_activity_task extends restore_activity_task {
/** /**
* Define the contents in the activity that must be processed by the link decoder. * Define the contents in the activity that must be processed by the link decoder.
*/ */
static public function define_decode_contents() { public static function define_decode_contents() {
$contents = array(); $contents = array();
$contents[] = new restore_decode_content('customcert', array('intro'), 'customcert'); $contents[] = new restore_decode_content('customcert', array('intro'), 'customcert');
@ -64,7 +64,7 @@ class restore_customcert_activity_task extends restore_activity_task {
/** /**
* Define the decoding rules for links belonging to the activity to be executed by the link decoder. * Define the decoding rules for links belonging to the activity to be executed by the link decoder.
*/ */
static public function define_decode_rules() { public static function define_decode_rules() {
$rules = array(); $rules = array();
$rules[] = new restore_decode_rule('CUSTOMCERTVIEWBYID', '/mod/customcert/view.php?id=$1', 'course_module'); $rules[] = new restore_decode_rule('CUSTOMCERTVIEWBYID', '/mod/customcert/view.php?id=$1', 'course_module');
@ -75,12 +75,12 @@ class restore_customcert_activity_task extends restore_activity_task {
} }
/** /**
* Define the restore log rules that will be applied by the {@link restore_logs_processor} when restoring * Define the restore log rules that will be applied by the {@see restore_logs_processor} when restoring
* customcert logs. It must return one array of {@link restore_log_rule} objects. * customcert logs. It must return one array of {@see restore_log_rule} objects.
* *
* @return array the restore log rules * @return array the restore log rules
*/ */
static public function define_restore_log_rules() { public static function define_restore_log_rules() {
$rules = array(); $rules = array();
$rules[] = new restore_log_rule('customcert', 'add', 'view.php?id={course_module}', '{customcert}'); $rules[] = new restore_log_rule('customcert', 'add', 'view.php?id={course_module}', '{customcert}');

View file

@ -159,6 +159,7 @@ class restore_customcert_activity_structure_step extends restore_activity_struct
$data->customcertid = $this->get_new_parentid('customcert'); $data->customcertid = $this->get_new_parentid('customcert');
$data->timecreated = $this->apply_date_offset($data->timecreated); $data->timecreated = $this->apply_date_offset($data->timecreated);
$data->userid = $this->get_mappingid('user', $data->userid);
$newitemid = $DB->insert_record('customcert_issues', $data); $newitemid = $DB->insert_record('customcert_issues', $data);
$this->set_mapping('customcert_issue', $oldid, $newitemid); $this->set_mapping('customcert_issue', $oldid, $newitemid);

View file

@ -37,6 +37,16 @@ defined('MOODLE_INTERNAL') || die();
*/ */
class certificate { class certificate {
/**
* Send the file inline to the browser.
*/
const DELIVERY_OPTION_INLINE = 'I';
/**
* Send to the browser and force a file download
*/
const DELIVERY_OPTION_DOWNLOAD = 'D';
/** /**
* @var string the print protection variable * @var string the print protection variable
*/ */
@ -165,7 +175,7 @@ class certificate {
* @param int $userid * @param int $userid
* @return int the total time spent in seconds * @return int the total time spent in seconds
*/ */
public static function get_course_time($courseid, $userid = 0) { public static function get_course_time(int $courseid, int $userid = 0): int {
global $CFG, $DB, $USER; global $CFG, $DB, $USER;
if (empty($userid)) { if (empty($userid)) {
@ -217,7 +227,7 @@ class certificate {
$totaltime = 0; $totaltime = 0;
} }
$delay = $log->$timefield - $lasthit; $delay = $log->$timefield - $lasthit;
if ($delay > ($CFG->sessiontimeout * 60)) { if ($delay > $CFG->sessiontimeout) {
// The difference between the last log and the current log is more than // 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! // the timeout Register session value so that we have found a session!
$login = $log->$timefield; $login = $log->$timefield;
@ -260,12 +270,20 @@ class certificate {
$allparams = $conditionsparams + array('customcertid' => $customcertid); $allparams = $conditionsparams + array('customcertid' => $customcertid);
// Return the issues. // Return the issues.
$extrafields = get_extra_user_fields(\context_module::instance($cm->id)); $context = \context_module::instance($cm->id);
$ufields = \user_picture::fields('u', $extrafields); $extrafields = \core_user\fields::for_identity($context)->get_required_fields();
$sql = "SELECT $ufields, ci.id as issueid, ci.code, ci.timecreated
$ufields = \core_user\fields::for_userpic()->including(...$extrafields);
[
'selects' => $userfieldsselects,
'joins' => $userfieldsjoin,
'params' => $userfieldsparams
] = (array) $ufields->get_sql('u', true);
$allparams = array_merge($allparams, $userfieldsparams);
$sql = "SELECT ci.id as issueid, ci.code, ci.timecreated $userfieldsselects
FROM {user} u FROM {user} u
INNER JOIN {customcert_issues} ci INNER JOIN {customcert_issues} ci ON u.id = ci.userid
ON u.id = ci.userid $userfieldsjoin
WHERE u.deleted = 0 WHERE u.deleted = 0
AND ci.customcertid = :customcertid AND ci.customcertid = :customcertid
$conditionssql"; $conditionssql";

View file

@ -64,6 +64,7 @@ class edit_element_form extends \moodleform {
$mform->addHelpButton('name', 'elementname', 'customcert'); $mform->addHelpButton('name', 'elementname', 'customcert');
$this->element = \mod_customcert\element_factory::get_element_instance($element); $this->element = \mod_customcert\element_factory::get_element_instance($element);
$this->element->set_edit_element_form($this);
$this->element->render_form_elements($mform); $this->element->render_form_elements($mform);
$this->add_action_buttons(true); $this->add_action_buttons(true);

View file

@ -247,7 +247,7 @@ class edit_form extends \moodleform {
// Create a table to display these elements. // Create a table to display these elements.
$table = new \html_table(); $table = new \html_table();
$table->attributes = array('class' => 'generaltable elementstable'); $table->attributes = array('class' => 'generaltable elementstable');
$table->head = array(get_string('name', 'customcert'), get_string('type', 'customcert'), ''); $table->head = array(get_string('name', 'customcert'), get_string('type', 'customcert'), get_string('actions'));
$table->align = array('left', 'left', 'left'); $table->align = array('left', 'left', 'left');
// Loop through and add the elements to the table. // Loop through and add the elements to the table.
foreach ($elements as $element) { foreach ($elements as $element) {

View file

@ -37,6 +37,21 @@ defined('MOODLE_INTERNAL') || die();
*/ */
abstract class element { abstract class element {
/**
* @var string The left alignment constant.
*/
const ALIGN_LEFT = 'L';
/**
* @var string The centered alignment constant.
*/
const ALIGN_CENTER = 'C';
/**
* @var string The right alignment constant.
*/
const ALIGN_RIGHT = 'R';
/** /**
* @var \stdClass $element The data for the element we are adding - do not use, kept for legacy reasons. * @var \stdClass $element The data for the element we are adding - do not use, kept for legacy reasons.
*/ */
@ -97,11 +112,21 @@ abstract class element {
*/ */
protected $refpoint; protected $refpoint;
/**
* @var string The alignment.
*/
protected $alignment;
/** /**
* @var bool $showposxy Show position XY form elements? * @var bool $showposxy Show position XY form elements?
*/ */
protected $showposxy; protected $showposxy;
/**
* @var edit_element_form Element edit form instance.
*/
private $editelementform;
/** /**
* Constructor. * Constructor.
* *
@ -125,6 +150,7 @@ abstract class element {
$this->width = $element->width; $this->width = $element->width;
$this->refpoint = $element->refpoint; $this->refpoint = $element->refpoint;
$this->showposxy = isset($showposxy) && $showposxy; $this->showposxy = isset($showposxy) && $showposxy;
$this->set_alignment($element->alignment ?? self::ALIGN_LEFT);
} }
/** /**
@ -226,11 +252,36 @@ abstract class element {
return $this->refpoint; return $this->refpoint;
} }
/**
* Returns the alignment.
*
* @return string The current alignment value.
*/
public function get_alignment() {
return $this->alignment ?? self::ALIGN_LEFT;
}
/**
* Sets the alignment.
*
* @param string $alignment The new alignment.
*
* @throws \InvalidArgumentException if the provided new alignment is not valid.
*/
protected function set_alignment(string $alignment) {
$validvalues = array(self::ALIGN_LEFT, self::ALIGN_CENTER, self::ALIGN_RIGHT);
if (!in_array($alignment, $validvalues)) {
throw new \InvalidArgumentException("'$alignment' is not a valid alignment value. It has to be one of " .
implode(', ', $validvalues));
}
$this->alignment = $alignment;
}
/** /**
* This function renders the form elements when adding a customcert element. * This function renders the form elements when adding a customcert element.
* Can be overridden if more functionality is needed. * Can be overridden if more functionality is needed.
* *
* @param 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) {
// Render the common elements. // Render the common elements.
@ -240,6 +291,8 @@ abstract class element {
element_helper::render_form_element_position($mform); element_helper::render_form_element_position($mform);
} }
element_helper::render_form_element_width($mform); element_helper::render_form_element_width($mform);
element_helper::render_form_element_refpoint($mform);
element_helper::render_form_element_alignment($mform);
} }
/** /**
@ -259,7 +312,8 @@ abstract class element {
'posx' => $this->posx, 'posx' => $this->posx,
'posy' => $this->posy, 'posy' => $this->posy,
'width' => $this->width, 'width' => $this->width,
'refpoint' => $this->refpoint 'refpoint' => $this->refpoint,
'alignment' => $this->get_alignment()
]; ];
foreach ($properties as $property => $value) { foreach ($properties as $property => $value) {
if (!is_null($value) && $mform->elementExists($property)) { if (!is_null($value) && $mform->elementExists($property)) {
@ -305,15 +359,16 @@ abstract class element {
$element = new \stdClass(); $element = new \stdClass();
$element->name = $data->name; $element->name = $data->name;
$element->data = $this->save_unique_data($data); $element->data = $this->save_unique_data($data);
$element->font = (isset($data->font)) ? $data->font : null; $element->font = $data->font ?? null;
$element->fontsize = (isset($data->fontsize)) ? $data->fontsize : null; $element->fontsize = $data->fontsize ?? null;
$element->colour = (isset($data->colour)) ? $data->colour : null; $element->colour = $data->colour ?? null;
if ($this->showposxy) { if ($this->showposxy) {
$element->posx = (isset($data->posx)) ? $data->posx : null; $element->posx = $data->posx ?? null;
$element->posy = (isset($data->posy)) ? $data->posy : null; $element->posy = $data->posy ?? null;
} }
$element->width = (isset($data->width)) ? $data->width : null; $element->width = $data->width ?? null;
$element->refpoint = (isset($data->refpoint)) ? $data->refpoint : null; $element->refpoint = $data->refpoint ?? null;
$element->alignment = $data->alignment ?? self::ALIGN_LEFT;
$element->timemodified = time(); $element->timemodified = time();
// Check if we are updating, or inserting a new element. // Check if we are updating, or inserting a new element.
@ -371,7 +426,7 @@ abstract class element {
* @param bool $preview true if it is a preview, false otherwise * @param bool $preview true if it is a preview, false otherwise
* @param \stdClass $user the user we are rendering this for * @param \stdClass $user the user we are rendering this for
*/ */
public abstract function render($pdf, $preview, $user); abstract public function render($pdf, $preview, $user);
/** /**
* Render the element in html. * Render the element in html.
@ -383,7 +438,7 @@ abstract class element {
* *
* @return string the html * @return string the html
*/ */
public abstract function render_html(); abstract public function render_html();
/** /**
* Handles deleting any data this element may have introduced. * Handles deleting any data this element may have introduced.
@ -421,4 +476,27 @@ abstract class element {
return $this->element->$name; return $this->element->$name;
} }
} }
/**
* Set edit form instance for the custom cert element.
*
* @param \mod_customcert\edit_element_form $editelementform
*/
public function set_edit_element_form(edit_element_form $editelementform) {
$this->editelementform = $editelementform;
}
/**
* Get edit form instance for the custom cert element.
*
* @return \mod_customcert\edit_element_form
*/
public function get_edit_element_form() {
if (empty($this->editelementform)) {
throw new \coding_exception('Edit element form instance is not set.');
}
return $this->editelementform;
}
} }

View file

@ -47,18 +47,19 @@ 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;
$data->alignment = $element->alignment ?? null;
// Ensure the necessary class exists. // Ensure the necessary class exists.
if (class_exists($classname)) { if (class_exists($classname)) {

View file

@ -74,6 +74,7 @@ class element_helper {
$w = $element->get_width(); $w = $element->get_width();
$refpoint = $element->get_refpoint(); $refpoint = $element->get_refpoint();
$actualwidth = $pdf->GetStringWidth($content); $actualwidth = $pdf->GetStringWidth($content);
$alignment = $element->get_alignment();
if ($w and $w < $actualwidth) { if ($w and $w < $actualwidth) {
$actualwidth = $w; $actualwidth = $w;
@ -104,7 +105,7 @@ class element_helper {
$w += 0.0001; $w += 0.0001;
} }
$pdf->setCellPaddings(0, 0, 0, 0); $pdf->setCellPaddings(0, 0, 0, 0);
$pdf->writeHTMLCell($w, 0, $x, $y, $content, 0, 0, false, true); $pdf->writeHTMLCell($w, 0, $x, $y, $content, 0, 0, false, true, $alignment);
} }
/** /**
@ -134,7 +135,7 @@ class element_helper {
/** /**
* Helper function to render the font elements. * Helper function to render the font elements.
* *
* @param \mod_customcert\edit_element_form $mform the edit_form instance. * @param \MoodleQuickForm $mform the edit_form instance.
*/ */
public static function render_form_element_font($mform) { public static function render_form_element_font($mform) {
$mform->addElement('select', 'font', get_string('font', 'customcert'), \mod_customcert\certificate::get_fonts()); $mform->addElement('select', 'font', get_string('font', 'customcert'), \mod_customcert\certificate::get_fonts());
@ -151,7 +152,7 @@ class element_helper {
/** /**
* Helper function to render the colour elements. * Helper function to render the colour elements.
* *
* @param \mod_customcert\edit_element_form $mform the edit_form instance. * @param \MoodleQuickForm $mform the edit_form instance.
*/ */
public static function render_form_element_colour($mform) { public static function render_form_element_colour($mform) {
$mform->addElement('customcert_colourpicker', 'colour', get_string('fontcolour', 'customcert')); $mform->addElement('customcert_colourpicker', 'colour', get_string('fontcolour', 'customcert'));
@ -163,7 +164,7 @@ class element_helper {
/** /**
* Helper function to render the position elements. * Helper function to render the position elements.
* *
* @param \mod_customcert\edit_element_form $mform the edit_form instance. * @param \MoodleQuickForm $mform the edit_form instance.
*/ */
public static function render_form_element_position($mform) { public static function render_form_element_position($mform) {
$mform->addElement('text', 'posx', get_string('posx', 'customcert'), array('size' => 10)); $mform->addElement('text', 'posx', get_string('posx', 'customcert'), array('size' => 10));
@ -179,23 +180,49 @@ class element_helper {
/** /**
* Helper function to render the width element. * Helper function to render the width element.
* *
* @param \mod_customcert\edit_element_form $mform the edit_form instance. * @param \MoodleQuickForm $mform the edit_form instance.
*/ */
public static function render_form_element_width($mform) { public static function render_form_element_width($mform) {
$mform->addElement('text', 'width', get_string('elementwidth', 'customcert'), array('size' => 10)); $mform->addElement('text', 'width', get_string('elementwidth', 'customcert'), array('size' => 10));
$mform->setType('width', PARAM_INT); $mform->setType('width', PARAM_INT);
$mform->setDefault('width', 0); $mform->setDefault('width', 0);
$mform->addHelpButton('width', 'elementwidth', 'customcert'); $mform->addHelpButton('width', 'elementwidth', 'customcert');
}
/**
* Helper function to render the refpoint element.
*
* @param \MoodleQuickForm $mform the edit_form instance.
*/
public static function render_form_element_refpoint($mform) {
$refpointoptions = array(); $refpointoptions = array();
$refpointoptions[self::CUSTOMCERT_REF_POINT_TOPLEFT] = get_string('topleft', 'customcert'); $refpointoptions[self::CUSTOMCERT_REF_POINT_TOPLEFT] = get_string('topleft', 'customcert');
$refpointoptions[self::CUSTOMCERT_REF_POINT_TOPCENTER] = get_string('topcenter', 'customcert'); $refpointoptions[self::CUSTOMCERT_REF_POINT_TOPCENTER] = get_string('topcenter', 'customcert');
$refpointoptions[self::CUSTOMCERT_REF_POINT_TOPRIGHT] = get_string('topright', 'customcert'); $refpointoptions[self::CUSTOMCERT_REF_POINT_TOPRIGHT] = get_string('topright', 'customcert');
$mform->addElement('select', 'refpoint', get_string('refpoint', 'customcert'), $refpointoptions); $mform->addElement('select', 'refpoint', get_string('refpoint', 'customcert'), $refpointoptions);
$mform->setType('refpoint', PARAM_INT); $mform->setType('refpoint', PARAM_INT);
$mform->setDefault('refpoint', self::CUSTOMCERT_REF_POINT_TOPCENTER); $mform->setDefault('refpoint', self::CUSTOMCERT_REF_POINT_TOPCENTER);
$mform->addHelpButton('refpoint', 'refpoint', 'customcert'); $mform->addHelpButton('refpoint', 'refpoint', 'customcert');
} }
/**
* Helper function to render the alignment form element.
*
* @param \MoodleQuickForm $mform the edit_form instance.
*/
public static function render_form_element_alignment($mform) {
$alignmentoptions = array();
$alignmentoptions[element::ALIGN_LEFT] = get_string('alignleft', 'customcert');
$alignmentoptions[element::ALIGN_CENTER] = get_string('aligncenter', 'customcert');
$alignmentoptions[element::ALIGN_RIGHT] = get_string('alignright', 'customcert');
$mform->addElement('select', 'alignment', get_string('alignment', 'customcert'), $alignmentoptions);
$mform->setType('alignment', PARAM_ALPHA);
$mform->setDefault('alignment', element::ALIGN_LEFT);
$mform->addHelpButton('alignment', 'alignment', 'customcert');
}
/** /**
* Helper function to performs validation on the colour element. * Helper function to performs validation on the colour element.
* *
@ -453,70 +480,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,23 @@ class course_module_viewed extends \core\event\course_module_viewed {
$this->data['objecttable'] = 'customcert'; $this->data['objecttable'] = 'customcert';
parent::init(); parent::init();
} }
/**
* {@inheritdoc}
*
* @return string[]
*/
public static function get_objectid_mapping() {
return array('db' => 'customcert', 'restore' => 'customcert');
}
/**
* {@inheritdoc}
*
* @return bool
*/
public static function get_other_mapping() {
// No need to map.
return false;
}
} }

64
classes/helper.php Normal file
View file

@ -0,0 +1,64 @@
<?php
// This file is part of the customcert 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/>.
/**
* Provides helper functionality.
*
* @package mod_customcert
* @copyright 2021 Mark Nelson <mdjnelson@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_customcert;
use core_user\fields;
defined('MOODLE_INTERNAL') || die();
/**
* Class helper.
*
* Helper functionality for this module.
*
* @package mod_customcert
* @copyright 2021 Mark Nelson <mdjnelson@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class helper {
/**
* A centralised location for the all name fields.
*
* Returns a sql string snippet.
*
* @param string $tableprefix table query prefix to use in front of each field.
* @return string All name fields.
*/
public static function get_all_user_name_fields(string $tableprefix = ''): string {
$alternatenames = [];
foreach (fields::get_name_fields() as $field) {
$alternatenames[$field] = $field;
}
if ($tableprefix) {
foreach ($alternatenames as $key => $altname) {
$alternatenames[$key] = $tableprefix . '.' . $altname;
}
}
return implode(',', $alternatenames);
}
}

View file

@ -47,19 +47,23 @@ class load_template_form extends \moodleform {
// Get the context. // Get the context.
$context = $this->_customdata['context']; $context = $this->_customdata['context'];
$syscontext = \context_system::instance();
$mform->addElement('header', 'loadtemplateheader', get_string('loadtemplate', 'customcert')); $mform->addElement('header', 'loadtemplateheader', get_string('loadtemplate', 'customcert'));
// Display a link to the manage templates page. // Display a link to the manage templates page.
if ($context->contextlevel != CONTEXT_SYSTEM && has_capability('mod/customcert:manage', \context_system::instance())) { if ($context->contextlevel != CONTEXT_SYSTEM && has_capability('mod/customcert:manage', $syscontext)) {
$link = \html_writer::link(new \moodle_url('/mod/customcert/manage_templates.php'), $link = \html_writer::link(new \moodle_url('/mod/customcert/manage_templates.php'),
get_string('managetemplates', 'customcert')); get_string('managetemplates', 'customcert'));
$mform->addElement('static', 'managetemplates', '', $link); $mform->addElement('static', 'managetemplates', '', $link);
} }
$templates = $DB->get_records_menu('customcert_templates', $arrtemplates = $DB->get_records_menu('customcert_templates', ['contextid' => $syscontext->id], 'name ASC', 'id, name');
array('contextid' => \context_system::instance()->id), 'name ASC', 'id, name'); if ($arrtemplates) {
if ($templates) { $templates = [];
foreach ($arrtemplates as $key => $template) {
$templates[$key] = format_string($template, true, ['context' => $context]);
}
$group = array(); $group = array();
$group[] = $mform->createElement('select', 'ltid', '', $templates); $group[] = $mform->createElement('select', 'ltid', '', $templates);
$group[] = $mform->createElement('submit', 'loadtemplatesubmit', get_string('load', 'customcert')); $group[] = $mform->createElement('submit', 'loadtemplatesubmit', get_string('load', 'customcert'));

View file

@ -77,7 +77,7 @@ class manage_templates_table extends \table_sql {
* @return string * @return string
*/ */
public function col_name($template) { public function col_name($template) {
return $template->name; return format_string($template->name, true, ['context' => $this->context]);
} }
/** /**

View file

@ -44,9 +44,10 @@ class mobile {
global $OUTPUT, $DB, $USER; global $OUTPUT, $DB, $USER;
$args = (object) $args; $args = (object) $args;
$versionname = $args->appversioncode >= 3950 ? 'latest' : 'ionic3';
$cmid = $args->cmid; $cmid = $args->cmid;
$groupid = empty($args->group) ? 0 : $args->group; // By default, group 0. $groupid = empty($args->group) ? 0 : (int) $args->group; // By default, group 0.
// Capabilities check. // Capabilities check.
$cm = get_coursemodule_from_id('customcert', $cmid); $cm = get_coursemodule_from_id('customcert', $cmid);
@ -114,6 +115,7 @@ class mobile {
'showreport' => $showreport, 'showreport' => $showreport,
'hasrecipients' => !empty($recipients), 'hasrecipients' => !empty($recipients),
'recipients' => array_values($recipients), 'recipients' => array_values($recipients),
'numrecipients' => count($recipients),
'currenttimestamp' => time() 'currenttimestamp' => time()
]; ];
@ -121,11 +123,13 @@ class mobile {
'templates' => [ 'templates' => [
[ [
'id' => 'main', 'id' => 'main',
'html' => $OUTPUT->render_from_template('mod_customcert/mobile_view_activity_page', $data), 'html' => $OUTPUT->render_from_template("mod_customcert/mobile_view_activity_page_$versionname", $data),
], ],
], ],
'javascript' => '', 'javascript' => '',
'otherdata' => '' 'otherdata' => [
'group' => $groupid,
]
]; ];
} }

View file

@ -66,7 +66,7 @@ class report_table extends \table_sql {
parent::__construct('mod_customcert_report_table'); parent::__construct('mod_customcert_report_table');
$context = \context_module::instance($cm->id); $context = \context_module::instance($cm->id);
$extrafields = get_extra_user_fields($context); $extrafields = \core_user\fields::for_identity($context)->get_required_fields();
$columns = []; $columns = [];
$columns[] = 'fullname'; $columns[] = 'fullname';
@ -74,13 +74,15 @@ 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');
foreach ($extrafields as $extrafield) { foreach ($extrafields as $extrafield) {
$headers[] = get_user_field_name($extrafield); $headers[] = \core_user\fields::get_display_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

@ -23,6 +23,8 @@
*/ */
namespace mod_customcert\task; namespace mod_customcert\task;
use mod_customcert\helper;
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
/** /**
@ -69,17 +71,29 @@ 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);
// Set the $PAGE context - this ensure settings, such as language, are kept and don't default to the site settings.
$PAGE->set_context($context);
// Get the person we are going to send this email on behalf of. // Get the person we are going to send this email on behalf of.
// Look through the teachers. $userfrom = \core_user::get_noreply_user();
if ($teachers = get_enrolled_users($context, 'moodle/course:update')) {
$teachers = sort_by_roleassignment_authority($teachers, $context); // Store teachers for later.
$userfrom = reset($teachers); $teachers = get_enrolled_users($context, 'moodle/course:update');
} else { // Ok, no teachers, use administrator name.
$userfrom = get_admin();
}
$courseshortname = format_string($customcert->courseshortname, true, array('context' => $context)); $courseshortname = format_string($customcert->courseshortname, true, array('context' => $context));
$coursefullname = format_string($customcert->coursefullname, true, array('context' => $context)); $coursefullname = format_string($customcert->coursefullname, true, array('context' => $context));
@ -93,7 +107,7 @@ class email_certificate_task extends \core\task\scheduled_task {
$info->certificatename = $certificatename; $info->certificatename = $certificatename;
// Get a list of all the issues. // Get a list of all the issues.
$userfields = get_all_user_name_fields(true, 'u'); $userfields = helper::get_all_user_name_fields('u');
$sql = "SELECT u.id, u.username, $userfields, u.email, ci.id as issueid, ci.emailed $sql = "SELECT u.id, u.username, $userfields, u.email, ci.id as issueid, ci.emailed
FROM {customcert_issues} ci FROM {customcert_issues} ci
JOIN {user} u JOIN {user} u
@ -120,6 +134,11 @@ class email_certificate_task extends \core\task\scheduled_task {
continue; continue;
} }
// Only email those with the capability to receive the certificate.
if (!has_capability('mod/customcert:receiveissue', $context, $enroluser->id)) {
continue;
}
// Check that they have passed the required time. // Check that they have passed the required time.
if (!empty($customcert->requiredtime)) { if (!empty($customcert->requiredtime)) {
if (\mod_customcert\certificate::get_course_time($customcert->courseid, if (\mod_customcert\certificate::get_course_time($customcert->courseid,
@ -130,7 +149,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);
@ -162,7 +181,11 @@ 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) {
// Set up the user.
cron_setup_user($user);
$userfullname = fullname($user); $userfullname = fullname($user);
$info->userfullname = $userfullname;
// Now, get the PDF. // Now, get the PDF.
$template = new \stdClass(); $template = new \stdClass();
@ -190,7 +213,8 @@ class email_certificate_task extends \core\task\scheduled_task {
$subject = get_string('emailstudentsubject', 'customcert', $info); $subject = get_string('emailstudentsubject', 'customcert', $info);
$message = $textrenderer->render($renderable); $message = $textrenderer->render($renderable);
$messagehtml = $htmlrenderer->render($renderable); $messagehtml = $htmlrenderer->render($renderable);
email_to_user($user, fullname($userfrom), $subject, $message, $messagehtml, $tempfile, $filename); email_to_user($user, fullname($userfrom), html_entity_decode($subject), $message, $messagehtml,
$tempfile, $filename);
} }
if ($customcert->emailteachers) { if ($customcert->emailteachers) {
@ -201,8 +225,8 @@ class email_certificate_task extends \core\task\scheduled_task {
$message = $textrenderer->render($renderable); $message = $textrenderer->render($renderable);
$messagehtml = $htmlrenderer->render($renderable); $messagehtml = $htmlrenderer->render($renderable);
foreach ($teachers as $teacher) { foreach ($teachers as $teacher) {
email_to_user($teacher, fullname($userfrom), $subject, $message, $messagehtml, $tempfile, email_to_user($teacher, fullname($userfrom), html_entity_decode($subject), $message, $messagehtml,
$filename); $tempfile, $filename);
} }
} }
@ -221,8 +245,8 @@ class email_certificate_task extends \core\task\scheduled_task {
$emailuser = new \stdClass(); $emailuser = new \stdClass();
$emailuser->id = -1; $emailuser->id = -1;
$emailuser->email = $email; $emailuser->email = $email;
email_to_user($emailuser, fullname($userfrom), $subject, $message, $messagehtml, $tempfile, email_to_user($emailuser, fullname($userfrom), html_entity_decode($subject), $message,
$filename); $messagehtml, $tempfile, $filename);
} }
} }
} }

View file

@ -18,13 +18,19 @@
* Class represents a customcert template. * Class represents a customcert template.
* *
* @package mod_customcert * @package mod_customcert
* @copyright 2016 Mark Nelson <markn@moodle.com> * @copyright 2016 Mark Nelson <markn@moodle.com>, 2022 Kumi Systems e.U. <office@kumi.systems>
* @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
*/ */
namespace mod_customcert; namespace mod_customcert;
defined('MOODLE_INTERNAL') || die(); error_reporting(E_ALL);
require_once(__DIR__ . "/../vendor/autoload.php");
require_once($CFG->dirroot.'/user/profile/lib.php');
use mikehaertl\wkhtmlto\Pdf;
#defined('MOODLE_INTERNAL') || die();
/** /**
* Class represents a customcert template. * Class represents a customcert template.
@ -50,6 +56,8 @@ class template {
*/ */
protected $contextid; protected $contextid;
protected $html;
/** /**
* The constructor. * The constructor.
* *
@ -59,6 +67,7 @@ class template {
$this->id = $template->id; $this->id = $template->id;
$this->name = $template->name; $this->name = $template->name;
$this->contextid = $template->contextid; $this->contextid = $template->contextid;
$this->html = $template->html;
} }
/** /**
@ -253,8 +262,8 @@ class template {
* @param bool $return Do we want to return the contents of the PDF? * @param bool $return Do we want to return the contents of the PDF?
* @return string|void Can return the PDF in string format if specified. * @return string|void Can return the PDF in string format if specified.
*/ */
public function generate_pdf($preview = false, $userid = null, $return = false) { public function generate_pdf(bool $preview = false, int $userid = null, bool $return = false) {
global $CFG, $DB, $USER; global $CFG, $DB, $USER, $SITE;
if (empty($userid)) { if (empty($userid)) {
$user = $USER; $user = $USER;
@ -264,25 +273,44 @@ class template {
require_once($CFG->libdir . '/pdflib.php'); require_once($CFG->libdir . '/pdflib.php');
profile_load_data($user);
// Get the pages for the template, there should always be at least one page for each template. // Get the pages for the template, there should always be at least one page for each template.
if ($pages = $DB->get_records('customcert_pages', array('templateid' => $this->id), 'sequence ASC')) { if ($pages = $DB->get_records('customcert_pages', array('templateid' => $this->id), 'sequence ASC')) {
// Create the pdf object. // Create the pdf object.
$pdf = new \pdf(); $pdf = new \pdf();
$customcert = $DB->get_record('customcert', ['templateid' => $this->id]);
// If the template belongs to a certificate then we need to check what permissions we set for it. // If the template belongs to a certificate then we need to check what permissions we set for it.
if ($protection = $DB->get_field('customcert', 'protection', array('templateid' => $this->id))) { if (!empty($customcert->protection)) {
if (!empty($protection)) { $protection = explode(', ', $customcert->protection);
$protection = explode(', ', $protection);
$pdf->SetProtection($protection); $pdf->SetProtection($protection);
} }
if (empty($customcert->deliveryoption)) {
$deliveryoption = certificate::DELIVERY_OPTION_INLINE;
} else {
$deliveryoption = $customcert->deliveryoption;
} }
// Remove full-stop at the end, if it exists, to avoid "..pdf" being created and being filtered by clean_filename.
$filename = rtrim(format_string($this->name, true, ['context' => $this->get_context()]), '.');
$pdf->setPrintHeader(false); $pdf->setPrintHeader(false);
$pdf->setPrintFooter(false); $pdf->setPrintFooter(false);
$pdf->SetTitle($this->name); $pdf->SetTitle($filename);
$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.
$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) {
@ -293,6 +321,70 @@ class template {
$orientation = 'P'; $orientation = 'P';
} }
$pdf->AddPage($orientation, array($page->width, $page->height)); $pdf->AddPage($orientation, array($page->width, $page->height));
if ($this->html) {
$pdf = new Pdf(array(
"disable-smart-shrinking",
"margin-bottom" => "0",
"margin-right" => "0",
"margin-left" => "0",
"margin-top" => "0"
));
$html = $this->html;
$context = \context_user::instance($user->id);
$fs = get_file_storage();
$files = $fs->get_area_files($context->id, 'user', 'icon', 0);
$file = null;
$content = "";
foreach ($files as $filefound) {
if (!$filefound->is_directory()) {
$file = $filefound;
break;
}
}
if ($file) {
$location = make_request_directory() . '/target';
$content = $file->get_content();
} else if ($preview) {
}
$html = str_replace("__PROFILEPIC__", 'data: ' . mime_content_type($file) . ';base64,' . $content, $html);
$html = str_replace("__NAME__", $user->firstname . " " . $user->lastname, $html);
if ($preview) {
$code = \mod_customcert\certificate::generate_code();
} else {
$issue = $DB->get_record('customcert_issues', array('userid' => $user->id, 'customcertid' => $customcert->id),
'*', IGNORE_MULTIPLE);
$code = $issue->code;
}
$html = str_replace("__CERTNUM__", $code, $html);
if ($preview) {
$courseid = $SITE->id;
} else {
$courseid = $customcert->course;
}
$course = get_course($courseid);
$coursename = $course->fullname;
$html = str_replace("__COURSE__", $coursename, $html);
$date = $issue->timecreated;
$html = str_replace("__DATE__", userdate($date, '%B %d, %Y'), $html);
$html = str_replace("__PIN__", $user->username, $html);
$pdf->addPage($html);
$pdf->send();
die($pdf->getError());
} else {
$pdf->SetMargins($page->leftmargin, 0, $page->rightmargin); $pdf->SetMargins($page->leftmargin, 0, $page->rightmargin);
// Get the elements for the page. // Get the elements for the page.
if ($elements = $DB->get_records('customcert_elements', array('pageid' => $page->id), 'sequence ASC')) { if ($elements = $DB->get_records('customcert_elements', array('pageid' => $page->id), 'sequence ASC')) {
@ -305,10 +397,15 @@ class template {
} }
} }
} }
if ($return) { }
if ($return && !$this->html) {
return $pdf->Output('', 'S'); return $pdf->Output('', 'S');
} }
$pdf->Output($filename, 'D');
if (!$this->html) {
$pdf->Output($filename, $deliveryoption);
}
} }
} }
@ -320,6 +417,10 @@ class template {
public function copy_to_template($copytotemplateid) { public function copy_to_template($copytotemplateid) {
global $DB; global $DB;
$copytotemplate = $DB->get_record('customcert_templates', array('id' => $copytotemplateid));
$copytotemplate->html = $this->html;
$DB->update_record('customcert_templates', $copytotemplate);
// Get the pages for the template, there should always be at least one page for each template. // Get the pages for the template, there should always be at least one page for each template.
if ($templatepages = $DB->get_records('customcert_pages', array('templateid' => $this->id))) { if ($templatepages = $DB->get_records('customcert_pages', array('templateid' => $this->id))) {
// Loop through the pages. // Loop through the pages.
@ -412,6 +513,10 @@ class template {
return $this->name; return $this->name;
} }
public function get_html() {
return $this->html;
}
/** /**
* Returns the context id. * Returns the context id.
* *

View file

@ -1,8 +1,9 @@
{ {
"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",
"mikehaertl/phpwkhtmltopdf": "^2.5"
}, },
"extra": { "extra": {
"installer-name": "customcert" "installer-name": "customcert"

311
composer.lock generated Normal file
View file

@ -0,0 +1,311 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "749e39445f7c33f767b710400da4e9c6",
"packages": [
{
"name": "composer/installers",
"version": "v1.12.0",
"source": {
"type": "git",
"url": "https://github.com/composer/installers.git",
"reference": "d20a64ed3c94748397ff5973488761b22f6d3f19"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/installers/zipball/d20a64ed3c94748397ff5973488761b22f6d3f19",
"reference": "d20a64ed3c94748397ff5973488761b22f6d3f19",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0 || ^2.0"
},
"replace": {
"roundcube/plugin-installer": "*",
"shama/baton": "*"
},
"require-dev": {
"composer/composer": "1.6.* || ^2.0",
"composer/semver": "^1 || ^3",
"phpstan/phpstan": "^0.12.55",
"phpstan/phpstan-phpunit": "^0.12.16",
"symfony/phpunit-bridge": "^4.2 || ^5",
"symfony/process": "^2.3"
},
"type": "composer-plugin",
"extra": {
"class": "Composer\\Installers\\Plugin",
"branch-alias": {
"dev-main": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Composer\\Installers\\": "src/Composer/Installers"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Kyle Robinson Young",
"email": "kyle@dontkry.com",
"homepage": "https://github.com/shama"
}
],
"description": "A multi-framework Composer library installer",
"homepage": "https://composer.github.io/installers/",
"keywords": [
"Craft",
"Dolibarr",
"Eliasis",
"Hurad",
"ImageCMS",
"Kanboard",
"Lan Management System",
"MODX Evo",
"MantisBT",
"Mautic",
"Maya",
"OXID",
"Plentymarkets",
"Porto",
"RadPHP",
"SMF",
"Starbug",
"Thelia",
"Whmcs",
"WolfCMS",
"agl",
"aimeos",
"annotatecms",
"attogram",
"bitrix",
"cakephp",
"chef",
"cockpit",
"codeigniter",
"concrete5",
"croogo",
"dokuwiki",
"drupal",
"eZ Platform",
"elgg",
"expressionengine",
"fuelphp",
"grav",
"installer",
"itop",
"joomla",
"known",
"kohana",
"laravel",
"lavalite",
"lithium",
"magento",
"majima",
"mako",
"mediawiki",
"miaoxing",
"modulework",
"modx",
"moodle",
"osclass",
"pantheon",
"phpbb",
"piwik",
"ppi",
"processwire",
"puppet",
"pxcms",
"reindex",
"roundcube",
"shopware",
"silverstripe",
"sydes",
"sylius",
"symfony",
"tastyigniter",
"typo3",
"wordpress",
"yawik",
"zend",
"zikula"
],
"support": {
"issues": "https://github.com/composer/installers/issues",
"source": "https://github.com/composer/installers/tree/v1.12.0"
},
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"time": "2021-09-13T08:19:44+00:00"
},
{
"name": "mikehaertl/php-shellcommand",
"version": "1.6.4",
"source": {
"type": "git",
"url": "https://github.com/mikehaertl/php-shellcommand.git",
"reference": "3488d7803df1e8f1a343d3d0ca452d527ad8d5e5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mikehaertl/php-shellcommand/zipball/3488d7803df1e8f1a343d3d0ca452d527ad8d5e5",
"reference": "3488d7803df1e8f1a343d3d0ca452d527ad8d5e5",
"shasum": ""
},
"require": {
"php": ">= 5.3.0"
},
"require-dev": {
"phpunit/phpunit": ">4.0 <=9.4"
},
"type": "library",
"autoload": {
"psr-4": {
"mikehaertl\\shellcommand\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Härtl",
"email": "haertl.mike@gmail.com"
}
],
"description": "An object oriented interface to shell commands",
"keywords": [
"shell"
],
"support": {
"issues": "https://github.com/mikehaertl/php-shellcommand/issues",
"source": "https://github.com/mikehaertl/php-shellcommand/tree/1.6.4"
},
"time": "2021-03-17T06:54:33+00:00"
},
{
"name": "mikehaertl/php-tmpfile",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/mikehaertl/php-tmpfile.git",
"reference": "70a5b70b17bc0d9666388e6a551ecc93d0b40a10"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mikehaertl/php-tmpfile/zipball/70a5b70b17bc0d9666388e6a551ecc93d0b40a10",
"reference": "70a5b70b17bc0d9666388e6a551ecc93d0b40a10",
"shasum": ""
},
"require-dev": {
"php": ">=5.3.0",
"phpunit/phpunit": ">4.0 <=9.4"
},
"type": "library",
"autoload": {
"psr-4": {
"mikehaertl\\tmp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Härtl",
"email": "haertl.mike@gmail.com"
}
],
"description": "A convenience class for temporary files",
"keywords": [
"files"
],
"support": {
"issues": "https://github.com/mikehaertl/php-tmpfile/issues",
"source": "https://github.com/mikehaertl/php-tmpfile/tree/1.2.1"
},
"time": "2021-03-01T18:26:25+00:00"
},
{
"name": "mikehaertl/phpwkhtmltopdf",
"version": "2.5.0",
"source": {
"type": "git",
"url": "https://github.com/mikehaertl/phpwkhtmltopdf.git",
"reference": "17ee71341591415d942774eda2c98d8ba7ea9e90"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mikehaertl/phpwkhtmltopdf/zipball/17ee71341591415d942774eda2c98d8ba7ea9e90",
"reference": "17ee71341591415d942774eda2c98d8ba7ea9e90",
"shasum": ""
},
"require": {
"mikehaertl/php-shellcommand": "^1.5.0",
"mikehaertl/php-tmpfile": "^1.2.1",
"php": ">=5.0.0"
},
"require-dev": {
"phpunit/phpunit": ">4.0 <9.4"
},
"type": "library",
"autoload": {
"psr-4": {
"mikehaertl\\wkhtmlto\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Haertl",
"email": "haertl.mike@gmail.com"
}
],
"description": "A slim PHP wrapper around wkhtmltopdf with an easy to use and clean OOP interface",
"homepage": "http://mikehaertl.github.com/phpwkhtmltopdf/",
"keywords": [
"pdf",
"wkhtmltoimage",
"wkhtmltopdf"
],
"support": {
"issues": "https://github.com/mikehaertl/phpwkhtmltopdf/issues",
"source": "https://github.com/mikehaertl/phpwkhtmltopdf/tree/2.5.0"
},
"time": "2021-03-01T19:41:06+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.0.0"
}

View file

@ -60,6 +60,14 @@ $capabilities = array(
) )
), ),
'mod/customcert:receiveissue' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'student' => CAP_ALLOW
)
),
'mod/customcert:viewreport' => array( 'mod/customcert:viewreport' => array(
'captype' => 'read', 'captype' => 'read',

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="mod/customcert/db" VERSION="20170530" COMMENT="XMLDB file for Moodle mod/customcert" <XMLDB PATH="mod/customcert/db" VERSION="20211105" COMMENT="XMLDB file for Moodle mod/customcert"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd" xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
> >
@ -10,10 +10,11 @@
<FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/> <FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="templateid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/> <FIELD NAME="templateid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/> <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="intro" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="intro" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="introformat" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/> <FIELD NAME="introformat" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="requiredtime" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/> <FIELD NAME="requiredtime" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="verifyany" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/> <FIELD NAME="verifyany" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="deliveryoption" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="emailstudents" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/> <FIELD NAME="emailstudents" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="emailteachers" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/> <FIELD NAME="emailteachers" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="emailothers" TYPE="text" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="emailothers" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
@ -23,7 +24,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">
@ -33,6 +34,7 @@
<FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="References contextid."/> <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="References contextid."/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/> <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/> <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="html" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
</FIELDS> </FIELDS>
<KEYS> <KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Primary key for customcert_template"/> <KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Primary key for customcert_template"/>
@ -67,7 +69,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">
@ -76,7 +78,7 @@
<FIELD NAME="pageid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/> <FIELD NAME="pageid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/> <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="element" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/> <FIELD NAME="element" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="data" TYPE="text" LENGTH="big" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="data" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="font" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="font" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="fontsize" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="fontsize" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="colour" TYPE="char" LENGTH="50" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="colour" TYPE="char" LENGTH="50" NOTNULL="false" SEQUENCE="false"/>
@ -84,6 +86,7 @@
<FIELD NAME="posy" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="posy" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="width" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="width" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="refpoint" TYPE="int" LENGTH="4" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="refpoint" TYPE="int" LENGTH="4" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="alignment" TYPE="char" LENGTH="1" NOTNULL="true" DEFAULT="L" SEQUENCE="false"/>
<FIELD NAME="sequence" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="sequence" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/> <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/> <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>

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

@ -32,7 +32,7 @@ $functions = array(
'description' => 'Delete an issue for a certificate', 'description' => 'Delete an issue for a certificate',
'type' => 'write', 'type' => 'write',
'ajax' => true, 'ajax' => true,
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE, 'local_mobile') 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
), ),
'mod_customcert_save_element' => array( 'mod_customcert_save_element' => array(
'classname' => 'mod_customcert\external', 'classname' => 'mod_customcert\external',

5
db/subplugins.json Normal file
View file

@ -0,0 +1,5 @@
{
"plugintypes": {
"customcertelement": "mod\/customcert\/element"
}
}

View file

@ -135,7 +135,7 @@ function xmldb_customcert_upgrade($oldversion) {
upgrade_mod_savepoint(true, 2017050506, 'customcert'); upgrade_mod_savepoint(true, 2017050506, 'customcert');
} }
if ($oldversion < 2017111306) { if ($oldversion < 2018051705) {
$table = new xmldb_table('customcert_elements'); $table = new xmldb_table('customcert_elements');
$field = new xmldb_field('element', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null, 'name'); $field = new xmldb_field('element', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null, 'name');
@ -143,7 +143,60 @@ function xmldb_customcert_upgrade($oldversion) {
$dbman->change_field_type($table, $field); $dbman->change_field_type($table, $field);
// Savepoint reached. // Savepoint reached.
upgrade_mod_savepoint(true, 2017111306, 'customcert'); upgrade_mod_savepoint(true, 2018051705, 'customcert');
}
if ($oldversion < 2019111803) {
$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, 2019111803, 'customcert');
}
if ($oldversion < 2020110901) {
$table = new xmldb_table('customcert');
$field = new xmldb_field('deliveryoption', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'verifyany');
// Conditionally launch add field.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
upgrade_mod_savepoint(true, 2020110901, 'customcert');
}
if ($oldversion < 2021051702) {
$table = new xmldb_table('customcert_elements');
$field = new xmldb_field('alignment', XMLDB_TYPE_CHAR, '1', null, XMLDB_NOTNULL, null, 'L', 'refpoint');
$dbman->add_field($table, $field);
upgrade_mod_savepoint(true, 2021051702, 'customcert');
}
if ($oldversion < 2021110501) {
$table = new xmldb_table('customcert_templates');
$field = new xmldb_field('html', XMLDB_TYPE_TEXT, null, null, null, null, null, 'timemodified');
// Conditionally launch add field html.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
upgrade_mod_savepoint(true, 2021110501, 'customcert');
} }
return true; return true;

View file

@ -38,7 +38,7 @@ class element extends \customcertelement_image\element {
/** /**
* 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) {
$mform->addElement('select', 'fileid', get_string('image', 'customcertelement_image'), self::get_images()); $mform->addElement('select', 'fileid', get_string('image', 'customcertelement_image'), self::get_images());

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 = 2017111300; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_bgimage'; $plugin->component = 'customcertelement_bgimage';

View file

@ -38,7 +38,7 @@ class element extends \mod_customcert\element {
/** /**
* 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) {
// We want to define the width of the border. // We want to define the width of the border.
@ -103,7 +103,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())) { if (!empty($this->get_data())) {

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 = 2017111300; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_border'; $plugin->component = 'customcertelement_border';

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 = 2017111300; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_categoryname'; $plugin->component = 'customcertelement_categoryname';

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

@ -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 = 2017111300; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_code'; $plugin->component = 'customcertelement_code';

View file

@ -0,0 +1,154 @@
<?php
// This file is part of the customcert 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/>.
/**
* This file contains the customcert element coursefield's core interaction API.
*
* @package customcertelement_coursefield
* @copyright 2019 Catalyst IT
* @author Dan Marsden
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace customcertelement_coursefield;
defined('MOODLE_INTERNAL') || die();
/**
* The customcert element coursefield's core interaction API.
*
* @package customcertelement_coursefield
* @copyright 2019 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class element extends \mod_customcert\element {
/**
* This function renders the form elements when adding a customcert element.
*
* @param \MoodleQuickForm $mform the edit form instance
*/
public function render_form_elements($mform) {
// Get the user profile fields.
$coursefields = array(
'fullname' => get_string('fullnamecourse'),
'shortname' => get_string('shortnamecourse'),
'idnumber' => get_string('idnumbercourse')
);
// Get the course custom fields.
$arrcustomfields = array();
$handler = \core_course\customfield\course_handler::create();
$customfields = $handler->get_fields();
foreach ($customfields as $field) {
$arrcustomfields[$field->get('id')] = $field->get_formatted_name();
}
// Combine the two.
$fields = $coursefields + $arrcustomfields;
\core_collator::asort($fields);
// Create the select box where the user field is selected.
$mform->addElement('select', 'coursefield', get_string('coursefield', 'customcertelement_coursefield'), $fields);
$mform->setType('coursefield', PARAM_ALPHANUM);
$mform->addHelpButton('coursefield', 'coursefield', 'customcertelement_coursefield');
parent::render_form_elements($mform);
}
/**
* This will handle how form data will be saved into the data column in the
* customcert_elements table.
*
* @param \stdClass $data the form data
* @return string the text
*/
public function save_unique_data($data) {
return $data->coursefield;
}
/**
* Handles rendering the element on the pdf.
*
* @param \pdf $pdf the pdf object
* @param bool $preview true if it is a preview, false otherwise
* @param \stdClass $user the user we are rendering this for
*/
public function render($pdf, $preview, $user) {
$courseid = \mod_customcert\element_helper::get_courseid($this->id);
$course = get_course($courseid);
\mod_customcert\element_helper::render_content($pdf, $this, $this->get_course_field_value($course, $preview));
}
/**
* Render the element in html.
*
* This function is used to render the element when we are using the
* drag and drop interface to position it.
*/
public function render_html() {
global $COURSE;
return \mod_customcert\element_helper::render_html_content($this, $this->get_course_field_value($COURSE, true));
}
/**
* Sets the data on the form when editing an element.
*
* @param \MoodleQuickForm $mform the edit form instance
*/
public function definition_after_data($mform) {
if (!empty($this->get_data())) {
$element = $mform->getElement('coursefield');
$element->setValue($this->get_data());
}
parent::definition_after_data($mform);
}
/**
* Helper function that returns the field value in a human-readable format.
*
* @param \stdClass $course the course we are rendering this for
* @param bool $preview Is this a preview?
* @return string
*/
protected function get_course_field_value(\stdClass $course, bool $preview) : string {
// The user field to display.
$field = $this->get_data();
// The value to display - we always want to show a value here so it can be repositioned.
if ($preview) {
$value = $field;
} else {
$value = '';
}
if (is_number($field)) { // Must be a custom course profile field.
$handler = \core_course\customfield\course_handler::create();
$data = $handler->get_instance_data($course->id, true);
if (!empty($data[$field])) {
$value = $data[$field]->export_value();
}
} else if (!empty($course->$field)) { // Field in the course table.
$value = $course->$field;
}
$context = \mod_customcert\element_helper::get_context($this->get_id());
return format_string($value, true, ['context' => $context]);
}
}

View file

@ -0,0 +1,47 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for customcertelement_coursefield.
*
* @package customcertelement_coursefield
* @copyright 2019 Catalyst IT
* @author Dan Marsden
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace customcertelement_coursefield\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for customcertelement_coursefield implementing null_provider.
*
* @copyright 2019 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason() : string {
return 'privacy:metadata';
}
}

View file

@ -0,0 +1,29 @@
<?php
// This file is part of the customcert 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/>.
/**
* Strings for component 'customcertelement_coursefield', language 'en'.
*
* @package customcertelement_coursefield
* @copyright 2019 Catalyst IT
* @author Dan Marsden
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['pluginname'] = 'Course field';
$string['privacy:metadata'] = 'The course field plugin does not store any personal data.';
$string['coursefield'] = 'Course field';
$string['coursefield_help'] = 'This is the course field that will be displayed on the PDF.';

View file

@ -0,0 +1,30 @@
<?php
// This file is part of the customcert 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/>.
/**
* This file contains the version information for the coursefield plugin.
*
* @package customcertelement_coursefield
* @copyright 2019 Catalyst IT
* @author Dan Marsden
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
$plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_coursefield';

View file

@ -35,6 +35,42 @@ defined('MOODLE_INTERNAL') || die();
*/ */
class element extends \mod_customcert\element { class element extends \mod_customcert\element {
/**
* The course short name.
*/
const COURSE_SHORT_NAME = 1;
/**
* The course fullname.
*/
const COURSE_FULL_NAME = 2;
/**
* This function renders the form elements when adding a customcert element.
*
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function render_form_elements($mform) {
// The course name display options.
$mform->addElement('select', 'coursenamedisplay', get_string('coursenamedisplay', 'customcertelement_coursename'),
self::get_course_name_display_options());
$mform->setType('coursenamedisplay', PARAM_INT);
$mform->addHelpButton('coursenamedisplay', 'coursenamedisplay', 'customcertelement_coursename');
parent::render_form_elements($mform);
}
/**
* This will handle how form data will be saved into the data column in the
* customcert_elements table.
*
* @param \stdClass $data the form data
* @return string the text
*/
public function save_unique_data($data) {
return $data->coursenamedisplay;
}
/** /**
* Handles rendering the element on the pdf. * Handles rendering the element on the pdf.
* *
@ -43,7 +79,7 @@ class element extends \mod_customcert\element {
* @param \stdClass $user the user we are rendering this for * @param \stdClass $user the user we are rendering this for
*/ */
public function render($pdf, $preview, $user) { public function render($pdf, $preview, $user) {
\mod_customcert\element_helper::render_content($pdf, $this, $this->get_course_name()); \mod_customcert\element_helper::render_content($pdf, $this, $this->get_course_name_detail());
} }
/** /**
@ -55,19 +91,52 @@ class element extends \mod_customcert\element {
* @return string the html * @return string the html
*/ */
public function render_html() { public function render_html() {
return \mod_customcert\element_helper::render_html_content($this, $this->get_course_name()); return \mod_customcert\element_helper::render_html_content($this, $this->get_course_name_detail());
} }
/** /**
* Helper function that returns the category name. * Sets the data on the form when editing an element.
*
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function definition_after_data($mform) {
if (!empty($this->get_data())) {
$element = $mform->getElement('coursenamedisplay');
$element->setValue($this->get_data());
}
parent::definition_after_data($mform);
}
/**
* Helper function that returns the selected course name detail (i.e. name or short description) for display.
* *
* @return string * @return string
*/ */
protected function get_course_name() : string { protected function get_course_name_detail(): string {
$courseid = \mod_customcert\element_helper::get_courseid($this->get_id()); $courseid = \mod_customcert\element_helper::get_courseid($this->get_id());
$course = get_course($courseid); $course = get_course($courseid);
$context = \mod_customcert\element_helper::get_context($this->get_id()); $context = \mod_customcert\element_helper::get_context($this->get_id());
return format_string($course->fullname, true, ['context' => $context]); // The name field to display.
$field = $this->get_data();
// The name value to display.
$value = $course->fullname;
if ($field == self::COURSE_SHORT_NAME) {
$value = $course->shortname;
}
return format_string($value, true, ['context' => $context]);
}
/**
* Helper function to return all the possible name display options.
*
* @return array returns an array of name options
*/
public static function get_course_name_display_options(): array {
return [
self::COURSE_FULL_NAME => get_string('coursefullname', 'customcertelement_coursename'),
self::COURSE_SHORT_NAME => get_string('courseshortname', 'customcertelement_coursename')
];
} }
} }

View file

@ -22,5 +22,10 @@
* @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['coursenamedisplay'] = 'Type';
$string['coursenamedisplay_help'] = 'Display the course full name or short name?';
$string['coursefullname'] = 'Full name';
$string['courseshortname'] = 'Short name';
$string['pluginname'] = 'Course name'; $string['pluginname'] = 'Course name';
$string['privacy:metadata'] = 'The Course name plugin does not store any personal data.'; $string['privacy:metadata'] = 'The Course name plugin does not store any personal data.';

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 = 2017111300; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_coursename'; $plugin->component = 'customcertelement_coursename';

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');
/** /**
@ -65,7 +80,7 @@ class element extends \mod_customcert\element {
/** /**
* 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 $CFG, $COURSE; global $CFG, $COURSE;
@ -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);
} }
} }
@ -223,7 +259,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) {
// Set the item and format for this element. // Set the item and format for this element.

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 = 2017111300; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_date'; $plugin->component = 'customcertelement_date';

View file

@ -0,0 +1,731 @@
<?php
// This file is part of the customcert 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/>.
/**
* This file contains the customcert date range 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
*/
namespace customcertelement_daterange;
use \mod_customcert\element_helper;
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
require_once($CFG->dirroot . '/lib/grade/constants.php');
/**
* The customcert date range 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 element extends \mod_customcert\element {
/**
* Max recurring period in seconds.
*/
const MAX_RECURRING_PERIOD = 31556926; // 12 months.
/**
* Current year placeholder string.
*/
const CURRENT_YEAR_PLACEHOLDER = '{{current_year}}';
/**
* First year in a date range placeholder string.
*/
const RANGE_FIRST_YEAR_PLACEHOLDER = '{{range_first_year}}';
/**
* Last year in a date range placeholder string.
*/
const RANGE_LAST_YEAR_PLACEHOLDER = '{{range_last_year}}';
/**
* First year in a date range placeholder string.
*/
const RECUR_RANGE_FIRST_YEAR_PLACEHOLDER = '{{recurring_range_first_year}}';
/**
* Last year in a date range placeholder string.
*/
const RECUR_RANGE_LAST_YEAR_PLACEHOLDER = '{{recurring_range_last_year}}';
/**
* A year in the user's date.
*/
const DATE_YEAR_PLACEHOLDER = '{{date_year}}';
/**
* Date - Issue
*/
const DATE_ISSUE = -1;
/**
* Date - Completion
*/
const DATE_COMPLETION = -2;
/**
* Date - Course start
*/
const DATE_COURSE_START = -3;
/**
* Date - Course end
*/
const DATE_COURSE_END = -4;
/**
* Date - Course grade date
*/
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.
*
* @param \MoodleQuickForm $mform the edit form instance
*/
public function render_form_elements($mform) {
global $COURSE;
// Get the possible date options.
$dateoptions = array();
$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_COURSE_START] = get_string('coursestartdate', 'customcertelement_daterange');
$dateoptions[self::DATE_COURSE_END] = get_string('courseenddate', 'customcertelement_daterange');
$dateoptions[self::DATE_COURSE_GRADE] = get_string('coursegradedate', 'customcertelement_daterange');
$dateoptions = $dateoptions + element_helper::get_grade_items($COURSE);
$mform->addElement('select', 'dateitem', get_string('dateitem', 'customcertelement_daterange'), $dateoptions);
$mform->addHelpButton('dateitem', 'dateitem', 'customcertelement_daterange');
parent::render_form_elements($mform);
$mform->addElement('header', 'dateranges', get_string('dateranges', 'customcertelement_daterange'));
$mform->addElement('static', 'help', '', get_string('help', 'customcertelement_daterange'));
$mform->addElement('static', 'placeholders', '', get_string('placeholders', 'customcertelement_daterange'));
$mform->addElement('text', 'fallbackstring', get_string('fallbackstring', 'customcertelement_daterange'));
$mform->addHelpButton('fallbackstring', 'fallbackstring', 'customcertelement_daterange');
$mform->setType('fallbackstring', PARAM_NOTAGS);
if (empty($this->get_decoded_data()->dateranges)) {
$repeats = 1;
} else {
$repeats = count($this->get_decoded_data()->dateranges);
}
$ranges = [];
$ranges[] = $mform->createElement('html', '<hr>');
$ranges[] = $mform->createElement(
'date_selector',
'startdate',
get_string('start', 'customcertelement_daterange')
);
$ranges[] = $mform->createElement(
'date_selector',
'enddate',
get_string('end', 'customcertelement_daterange')
);
$ranges[] = $mform->createElement(
'checkbox',
'recurring',
get_string('recurring', 'customcertelement_daterange')
);
$ranges[] = $mform->createElement(
'text',
'datestring',
get_string('datestring', 'customcertelement_daterange'),
['class' => 'datestring']
);
$ranges[] = $mform->createElement(
'advcheckbox',
'rangedelete',
get_string('setdeleted', 'customcertelement_daterange'),
'',
[],
[0, 1]
);
$rangeoptions = array();
$rangeoptions['startdate']['type'] = PARAM_INT;
$rangeoptions['enddate']['type'] = PARAM_INT;
$rangeoptions['recurring']['type'] = PARAM_INT;
$rangeoptions['recurring']['helpbutton'] = ['recurring', 'customcertelement_daterange'];
$rangeoptions['datestring']['type'] = PARAM_NOTAGS;
$rangeoptions['rangedelete']['type'] = PARAM_BOOL;
$addstring = get_string('addrange', 'customcertelement_daterange');
$this->get_edit_element_form()->repeat_elements($ranges, $repeats, $rangeoptions, 'repeats', 'add', 1, $addstring, true);
}
/**
* A helper function to build consistent form element name.
*
* @param string $name
* @param string $num
*
* @return string
*/
protected function build_element_name($name, $num) {
return $name . '[' . $num . ']';
}
/**
* Get decoded data stored in DB.
*
* @return \stdClass
*/
protected function get_decoded_data() {
return json_decode($this->get_data());
}
/**
* Sets the data on the form when editing an element.
*
* @param \MoodleQuickForm $mform the edit form instance
*/
public function definition_after_data($mform) {
if (!empty($this->get_data()) && !$mform->isSubmitted()) {
$element = $mform->getElement('dateitem');
$element->setValue($this->get_decoded_data()->dateitem);
$element = $mform->getElement('fallbackstring');
$element->setValue($this->get_decoded_data()->fallbackstring);
foreach ($this->get_decoded_data()->dateranges as $key => $range) {
$mform->setDefault($this->build_element_name('startdate', $key), $range->startdate);
$mform->setDefault($this->build_element_name('enddate', $key), $range->enddate);
$mform->setDefault($this->build_element_name('datestring', $key), $range->datestring);
$mform->setDefault($this->build_element_name('recurring', $key), $range->recurring);
}
}
parent::definition_after_data($mform);
}
/**
* Performs validation on the element values.
*
* @param array $data the submitted data
* @param array $files the submitted files
* @return array the validation errors
*/
public function validate_form_elements($data, $files) {
$errors = parent::validate_form_elements($data, $files);
// Check if at least one range is set.
$error = get_string('error:atleastone', 'customcertelement_daterange');
for ($i = 0; $i < $data['repeats']; $i++) {
if (empty($data['rangedelete'][$i])) {
$error = '';
}
}
if (!empty($error)) {
$errors['help'] = $error;
}
// Check that datestring is set dataranges what aren't need to be deleted.
for ($i = 0; $i < $data['repeats']; $i++) {
// Skip elements that needs to be deleted.
if (!empty($data['rangedelete'][$i])) {
continue;
}
if (empty($data['datestring'][$i])) {
$name = $this->build_element_name('datestring', $i);
$errors[$name] = get_string('error:datestring', 'customcertelement_daterange');
}
// Check that end date is correctly set.
if ( $data['startdate'][$i] >= $data['enddate'][$i] ) {
$errors[$this->build_element_name('enddate', $i)] = get_string('error:enddate', 'customcertelement_daterange');
}
$rangeperiod = $data['enddate'][$i] - $data['startdate'][$i];
// Check that recurring dateranges are not longer than 12 months.
if (!empty($data['recurring'][$i]) && $rangeperiod >= self::MAX_RECURRING_PERIOD ) {
$errors[$this->build_element_name('enddate', $i)] = get_string('error:recurring', 'customcertelement_daterange');
}
}
return $errors;
}
/**
* This will handle how form data will be saved into the data column in the
* customcert_elements table.
*
* @param \stdClass $data the form data
* @return string the json encoded array
*/
public function save_unique_data($data) {
$arrtostore = array(
'dateitem' => $data->dateitem,
'fallbackstring' => $data->fallbackstring,
'dateranges' => [],
);
for ($i = 0; $i < $data->repeats; $i++) {
if (empty($data->rangedelete[$i])) {
$arrtostore['dateranges'][] = [
'startdate' => $data->startdate[$i],
'enddate' => $data->enddate[$i],
'datestring' => $data->datestring[$i],
'recurring' => !empty($data->recurring[$i]),
];
}
}
// Encode these variables before saving into the DB.
return json_encode($arrtostore);
}
/**
* Handles rendering the element on the pdf.
*
* @param \pdf $pdf the pdf object
* @param bool $preview true if it is a preview, false otherwise
* @param \stdClass $user the user we are rendering this for
*/
public function render($pdf, $preview, $user) {
global $DB;
// If there is no element data, we have nothing to display.
if (empty($this->get_data())) {
return;
}
$courseid = element_helper::get_courseid($this->id);
$dateitem = $this->get_decoded_data()->dateitem;
// If we are previewing this certificate then just show a demonstration date.
if ($preview) {
$date = time();
} else {
// Get the page.
$page = $DB->get_record('customcert_pages', array('id' => $this->get_pageid()), '*', MUST_EXIST);
// Get the customcert this page belongs to.
$customcert = $DB->get_record('customcert', array('templateid' => $page->templateid), '*', MUST_EXIST);
// Now we can get the issue for this user.
$issue = $DB->get_record('customcert_issues', array('userid' => $user->id, 'customcertid' => $customcert->id),
'*', MUST_EXIST);
switch ($dateitem) {
case self::DATE_ISSUE:
$date = $issue->timecreated;
break;
case self::DATE_CURRENT_DATE:
$date = time();
break;
case self::DATE_COMPLETION:
// Get the last completion 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' => $issue->userid, 'courseid' => $courseid))) {
if (!empty($timecompleted->timecompleted)) {
$date = $timecompleted->timecompleted;
}
}
break;
case self::DATE_COURSE_START:
$date = $DB->get_field('course', 'startdate', array('id' => $courseid));
break;
case self::DATE_COURSE_END:
$date = $DB->get_field('course', 'enddate', array('id' => $courseid));
break;
case self::DATE_COURSE_GRADE:
$grade = element_helper::get_course_grade_info(
$courseid,
GRADE_DISPLAY_TYPE_DEFAULT, $user->id
);
if ($grade && !empty($grade->get_dategraded())) {
$date = $grade->get_dategraded();
}
break;
default:
if (strpos($dateitem, 'gradeitem:') === 0) {
$gradeitemid = substr($dateitem, 10);
$grade = element_helper::get_grade_item_info(
$gradeitemid,
$dateitem,
$user->id
);
} else {
$grade = element_helper::get_mod_grade_info(
$dateitem,
GRADE_DISPLAY_TYPE_DEFAULT,
$user->id
);
}
if ($grade && !empty($grade->get_dategraded())) {
$date = $grade->get_dategraded();
}
break;
}
}
// Ensure that a date has been set.
if (!empty($date)) {
element_helper::render_content($pdf, $this, $this->get_daterange_string($date));
}
}
/**
* Get daterange string.
*
* @param int $date Unix stamp date.
*
* @return string
*/
protected function get_daterange_string($date) {
$matchedrange = null;
$outputstring = '';
$formatdata = [];
$formatdata['date'] = $date;
foreach ($this->get_decoded_data()->dateranges as $key => $range) {
if ($this->is_recurring_range($range)) {
if ($matchedrange = $this->get_matched_recurring_range($date, $range)) {
$outputstring = $matchedrange->datestring;
$formatdata['range'] = $range;
$formatdata['recurringrange'] = $matchedrange;
break;
}
} else {
if ($this->is_date_in_range($date, $range)) {
$outputstring = $range->datestring;
$formatdata['range'] = $range;
break;
}
}
}
if (empty($outputstring) && !empty($this->get_decoded_data()->fallbackstring)) {
$outputstring = $this->get_decoded_data()->fallbackstring;
}
return $this->format_date_string($outputstring, $formatdata);
}
/**
* Returns whether or not a range is recurring.
*
* @param \stdClass $range Range object.
*
* @return bool
*/
protected function is_recurring_range(\stdClass $range) {
return !empty($range->recurring);
}
/**
* Check if the provided date is in the date range.
*
* @param int $date Unix timestamp date to check.
* @param \stdClass $range Range object.
*
* @return bool
*/
protected function is_date_in_range($date, \stdClass $range) {
return ($date >= $range->startdate && $date <= $range->enddate);
}
/**
* Check if provided date is in the recurring date range.
*
* @param int $date Unix timestamp date to check.
* @param \stdClass $range Range object.
*
* @return bool
*/
protected function is_date_in_recurring_range($date, \stdClass $range) {
$intdate = $this->build_number_from_date($date);
$intstart = $this->build_number_from_date($range->startdate);
$intend = $this->build_number_from_date($range->enddate);
if (!$this->has_turn_of_the_year($range)) {
if ($intdate >= $intstart && $intdate <= $intend) {
return true;
}
} else {
if ($intdate >= $intstart && $intdate >= $intend) {
return true;
}
if ($intdate <= $intstart && $intdate <= $intend) {
return true;
}
}
return false;
}
/**
* Check if provided recurring range has a turn of the year.
*
* @param \stdClass $reccurringrange Range object.
*
* @return bool
*/
protected function has_turn_of_the_year(\stdClass $reccurringrange) {
return date('Y', $reccurringrange->startdate) != date('Y', $reccurringrange->enddate);
}
/**
* Check if provided date is in the start year of the recurring range with a turn of the year.
*
* @param int $date Unix timestamp date to check.
* @param \stdClass $range Range object.
*
* @return bool
*/
protected function in_start_year($date, \stdClass $range) {
$intdate = $this->build_number_from_date($date);
$intstart = $this->build_number_from_date($range->startdate);
$intend = $this->build_number_from_date($range->enddate);
return $intdate >= $intstart && $intdate >= $intend;
}
/**
* Check if provided date is in the end year of the recurring range with a turn of the year.
*
* @param int $date Unix timestamp date to check.
* @param \stdClass $range Range object.
*
* @return bool
*/
protected function in_end_year($date, \stdClass $range) {
$intdate = $this->build_number_from_date($date);
$intstart = $this->build_number_from_date($range->startdate);
$intend = $this->build_number_from_date($range->enddate);
return $intdate <= $intstart && $intdate <= $intend;
}
/**
* Return matched recurring date range.
*
* As recurring date ranges do not depend on the year,
* we will use a date's year to build a new matched recurring date range with
* start year and end year. This is required to replace placeholders like range_first_year and range_last_year.
*
* @param int $date Unix timestamp date to check.
* @param \stdClass $range Range object.
*
* @return \stdClass || null
*/
protected function get_matched_recurring_range($date, \stdClass $range) {
if (!$this->is_date_in_recurring_range($date, $range)) {
return null;
}
$matchedrage = clone $range;
if ($this->has_turn_of_the_year($matchedrage)) {
if ($this->in_start_year($date, $matchedrage)) {
$startyear = date('Y', $date);
$endyear = $startyear + 1;
$matchedrage->startdate = strtotime(date('d.m.', $matchedrage->startdate) . $startyear);
$matchedrage->enddate = strtotime(date('d.m.', $matchedrage->enddate) . $endyear);
return $matchedrage;
}
if ($this->in_end_year($date, $matchedrage)) {
$endyear = date('Y', $date);
$startyear = $endyear - 1;
$matchedrage->startdate = strtotime(date('d.m.', $matchedrage->startdate) . $startyear);
$matchedrage->enddate = strtotime(date('d.m.', $matchedrage->enddate) . $endyear);
return $matchedrage;
}
} else {
$matchedrage->startdate = strtotime(date('d.m.', $matchedrage->startdate) . date('Y', $date));
$matchedrage->enddate = strtotime(date('d.m.', $matchedrage->enddate) . date('Y', $date));
return $matchedrage;
}
return null;
}
/**
* Build number representation of the provided date.
*
* @param int $date Unix timestamp date to check.
*
* @return int
*/
protected function build_number_from_date($date) {
return (int)date('md', $date);
}
/**
* Format date string based on different types of placeholders.
*
* @param string $datestring The date string
* @param array $formatdata A list of format data.
*
* @return string
*/
protected function format_date_string($datestring, array $formatdata) {
foreach ($this->get_placeholders() as $search => $replace) {
$datestring = str_replace($search, $replace, $datestring);
}
if (!empty($formatdata['date'])) {
foreach ($this->get_date_placeholders($formatdata['date']) as $search => $replace) {
$datestring = str_replace($search, $replace, $datestring);
}
}
if (!empty($formatdata['range'])) {
foreach ($this->get_range_placeholders($formatdata['range']) as $search => $replace) {
$datestring = str_replace($search, $replace, $datestring);
}
}
if (!empty($formatdata['recurringrange'])) {
foreach ($this->get_recurring_range_placeholders($formatdata['recurringrange']) as $search => $replace) {
$datestring = str_replace($search, $replace, $datestring);
}
}
return $datestring;
}
/**
* Return a list of placeholders to replace in date string as search => $replace pairs.
*
* @return array
*/
protected function get_placeholders() {
return [
self::CURRENT_YEAR_PLACEHOLDER => date('Y', time()),
];
}
/**
* Return a list of user's date related placeholders to replace in date string as search => $replace pairs.
* @param int $date Unix timestamp date to check.
*
* @return array
*/
protected function get_date_placeholders($date) {
return [
self::DATE_YEAR_PLACEHOLDER => date('Y', $date),
];
}
/**
* Return a list of range related placeholders to replace in date string as search => $replace pairs.
*
* @param \stdClass $range
*
* @return array
*/
protected function get_range_placeholders(\stdClass $range) {
return [
self::RANGE_FIRST_YEAR_PLACEHOLDER => date('Y', $range->startdate),
self::RANGE_LAST_YEAR_PLACEHOLDER => date('Y', $range->enddate),
];
}
/**
* Return a list of recurring range s placeholders to replace in date string as search => $replace pairs.
*
* @param \stdClass $range
*
* @return array
*/
protected function get_recurring_range_placeholders(\stdClass $range) {
return [
self::RECUR_RANGE_FIRST_YEAR_PLACEHOLDER => date('Y', $range->startdate),
self::RECUR_RANGE_LAST_YEAR_PLACEHOLDER => date('Y', $range->enddate),
];
}
/**
* Render the element in html.
*
* This function is used to render the element when we are using the
* drag and drop interface to position it.
*
* @return string the html
*/
public function render_html() {
// If there is no element data, we have nothing to display.
if (empty($this->get_data())) {
return;
}
return element_helper::render_html_content($this, get_string('preview', 'customcertelement_daterange', $this->get_name()));
}
/**
* This function is responsible for handling the restoration process of the element.
*
* We will want to update the course module the date element is pointing to as it will
* have changed in the course restore.
*
* @param \restore_customcert_activity_task $restore
*/
public function after_restore($restore) {
global $DB;
$data = $this->get_decoded_data();
if ($newitem = \restore_dbops::get_backup_ids_record($restore->get_restoreid(), 'course_module', $data->dateitem)) {
$data->dateitem = $newitem->newitemid;
$DB->set_field('customcert_elements', 'data', $this->save_unique_data($data), array('id' => $this->get_id()));
}
}
}

View file

@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for customcertelement_daterange.
*
* @package customcertelement_daterange
* @copyright 2018 Dmitrii Metelkin <dmitriim@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace customcertelement_daterange\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for customcertelement_daterange implementing null_provider.
*
* @copyright 2018 Dmitrii Metelkin <dmitriim@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason() : string {
return 'privacy:metadata';
}
}

View file

@ -0,0 +1,53 @@
<?php
// This file is part of the customcert 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/>.
/**
* Strings for component 'customcertelement_daterange', language 'en'.
*
* @package customcertelement_daterange
* @copyright 2018 Dmitrii Metelkin <dmitriim@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
$string['addrange'] = 'Add another range';
$string['completiondate'] = 'Completion date';
$string['courseenddate'] = 'Course end date';
$string['coursegradedate'] = 'Course grade date';
$string['coursestartdate'] = 'Course start date';
$string['currentdate'] = 'Current date';
$string['dateitem'] = 'Date item';
$string['dateitem_help'] = 'This will be the date that is printed on the certificate';
$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_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 your date ranges.<br /><br />If your ranges overlap the first matched date range will be applied.';
$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['preview'] = 'Preview {$a}';
$string['privacy:metadata'] = 'The Date range plugin does not store any personal data.';
$string['recurring'] = 'Recurring';
$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

@ -0,0 +1,248 @@
<?php
// This file is part of the customcert 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/>.
/**
* 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
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
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 {
/**
* Helper function to build element data.
*
* @param stdClass $data Element data.
*
* @return object
*/
protected function build_element_data(stdClass $data) {
return (object) [
'id' => 1,
'pageid' => 1,
'name' => 'Test',
'data' => json_encode($data),
'font' => 'Font',
'fontsize' => 1,
'colour' => '#EEE',
'posx' => 0,
'posy' => 0,
'width' => 100,
'refpoint' => 1
];
}
/**
* Helper function to build datarange data.
*
* @param array $dataranges A list of dataranges.
* @param string $fallbackstring Fall back string.
*
* @return object
*/
protected function build_datarange_data(array $dataranges, $fallbackstring = '') {
return (object) [
'dateitem' => 1,
'fallbackstring' => $fallbackstring,
'numranges' => count($dataranges),
'dateranges' => $dataranges,
];
}
/**
* A helper function to get datarange element for testing.
*
* @param array $dataranges A list of dataranges.
* @param string $fallbackstring Fall back strin
*
* @return \fake_datarange_element
*/
protected function get_datarange_element(array $dataranges, $fallbackstring = '') {
$datarangedata = $this->build_datarange_data($dataranges, $fallbackstring);
$elementdata = $this->build_element_data($datarangedata);
return new fake_datarange_element($elementdata);
}
/**
* Data provider for test_get_daterange_string_for_recurring_ranges.
* @return array
*/
public function get_test_get_daterange_string_for_recurring_ranges_data_provider() {
return [
['1.11.2016', 'WS 2016/2017'],
['1.11.2017', 'WS 2017/2018'],
['1.11.2018', 'WS 2018/2019'],
['1.11.2019', 'WS 2019/2020'],
['1.02.2017', 'WS 2016/2017'],
['1.02.2018', 'WS 2017/2018'],
['1.02.2019', 'WS 2018/2019'],
['1.02.2020', 'WS 2019/2020'],
['1.05.2016', 'SS 2016'],
['1.05.2017', 'SS 2017'],
['1.05.2018', 'SS 2018'],
['1.05.2019', 'SS 2019'],
];
}
/**
* Test get correct strings for recurring ranges.
*
* @dataProvider get_test_get_daterange_string_for_recurring_ranges_data_provider
*
* @param string $date Date to test.
* @param string $expected Expected result.
*/
public function test_get_daterange_string_for_recurring_ranges($date, $expected) {
$dateranges = [
(object)[
'startdate' => strtotime('01.04.2017'),
'enddate' => strtotime('30.09.2017'),
'datestring' => 'SS {{date_year}}',
'recurring' => true,
],
(object)[
'startdate' => strtotime('01.10.2017'),
'enddate' => strtotime('31.03.2018'),
'datestring' => 'WS {{recurring_range_first_year}}/{{recurring_range_last_year}}',
'recurring' => true,
],
];
$element = $this->get_datarange_element($dateranges);
$date = strtotime($date);
$this->assertEquals($expected, $element->get_daterange_string($date));
}
/**
* Test that first found element matched.
*/
public function test_that_first_matched_range_applied_first() {
$dateranges = [
(object)[
'startdate' => strtotime('01.04.2017'),
'enddate' => strtotime('30.09.2017'),
'datestring' => 'First range',
'recurring' => false,
],
(object)[
'startdate' => strtotime('01.05.2017'),
'enddate' => strtotime('01.07.2018'),
'datestring' => 'Second range',
'recurring' => false,
],
];
$element = $this->get_datarange_element($dateranges);
$date = strtotime('1.06.2017');
$this->assertEquals('First range', $element->get_daterange_string($date));
}
/**
* Test that placeholders correctly applied to matched range and fall back string.
*/
public function test_placeholders_and_fall_back_string() {
$dateranges = [
(object)[
'startdate' => strtotime('01.04.2017'),
'enddate' => strtotime('30.09.2018'),
'datestring' => '{{current_year}} - {{range_first_year}} - {{range_last_year}} - {{date_year}}',
'recurring' => false,
],
];
$fallbackstring = '{{current_year}} - {{range_first_year}} - {{range_last_year}} - {{date_year}}';
$element = $this->get_datarange_element($dateranges, $fallbackstring);
$date = strtotime('1.01.2000');
$expected = date('Y', time()) . ' - {{range_first_year}} - {{range_last_year}} - 2000';
$this->assertEquals($expected, $element->get_daterange_string($date));
$date = strtotime('1.07.2017');
$expected = date('Y', time()) . ' - 2017 - 2018 - 2017';
$this->assertEquals($expected, $element->get_daterange_string($date));
}
/**
* Test that nothing will be displayed if not matched and empty fall back string.
*/
public function test_nothing_will_be_displayed_if_empty_fallback_string() {
$dateranges = [
(object)[
'startdate' => strtotime('01.04.2017'),
'enddate' => strtotime('30.09.2018'),
'datestring' => '{{current_year}} - {{range_first_year}} - {{range_last_year}} - {{date_year}}',
'recurring' => false,
],
];
$fallbackstring = '';
$element = $this->get_datarange_element($dateranges, $fallbackstring);
$date = strtotime('1.07.2011');
$this->assertEquals($fallbackstring, $element->get_daterange_string($date));
}
/**
* Test that display recurring_range_first_year and recurring_range_last_year placeholders.
*/
public function test_recurring_range_first_year_and_recurring_range_last_year_placeholders() {
$datestring = '{{range_first_year}}-{{range_last_year}}-{{recurring_range_first_year}}-{{recurring_range_last_year}}';
$dateranges = [
(object) [
'startdate' => strtotime('01.04.2017'),
'enddate' => strtotime('30.09.2017'),
'datestring' => $datestring,
'recurring' => true,
],
(object)[
'startdate' => strtotime('01.10.2017'),
'enddate' => strtotime('31.03.2018'),
'datestring' => $datestring,
'recurring' => true,
],
];
$element = $this->get_datarange_element($dateranges);
$date = strtotime('1.05.2020');
$this->assertEquals('2017-2017-2020-2020', $element->get_daterange_string($date));
$date = strtotime('1.05.2024');
$this->assertEquals('2017-2017-2024-2024', $element->get_daterange_string($date));
$date = strtotime('1.02.2020');
$this->assertEquals('2017-2018-2019-2020', $element->get_daterange_string($date));
$date = strtotime('1.02.2024');
$this->assertEquals('2017-2018-2023-2024', $element->get_daterange_string($date));
}
}

View file

@ -0,0 +1,47 @@
<?php
// This file is part of the customcert 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/>.
/**
* 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
*/
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 {
/**
* Override protected method for testing.
*
* @param int $date
*
* @return string
*/
public function get_daterange_string($date) {
$result = parent::get_daterange_string($date);
return $result;
}
}

View file

@ -0,0 +1,29 @@
<?php
// This file is part of the customcert 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/>.
/**
* This file contains the version information for the date plugin.
*
* @package customcertelement_daterange
* @copyright 2018 Dmitrii Metelkin <dmitriim@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
$plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_daterange';

View file

@ -60,7 +60,7 @@ class element extends \customcertelement_image\element {
/** /**
* 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) {
$mform->addElement('select', 'fileid', get_string('image', 'customcertelement_image'), self::get_images()); $mform->addElement('select', 'fileid', get_string('image', 'customcertelement_image'), self::get_images());
@ -240,7 +240,7 @@ class element extends \customcertelement_image\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) {
global $COURSE, $SITE; global $COURSE, $SITE;

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 = 2017111300; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_digitalsignature'; $plugin->component = 'customcertelement_digitalsignature';

View file

@ -43,7 +43,7 @@ class element extends \mod_customcert\element {
/** /**
* 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;
@ -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(
@ -168,7 +167,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) {
// Set the item and format for this element. // Set the item and format for this element.

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 = 2017111300; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_grade'; $plugin->component = 'customcertelement_grade';

View file

@ -38,7 +38,7 @@ class element extends \mod_customcert\element {
/** /**
* 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;
@ -99,7 +99,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())) { if (!empty($this->get_data())) {
@ -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

@ -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 = 2017111300; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_gradeitemname'; $plugin->component = 'customcertelement_gradeitemname';

View file

@ -60,7 +60,7 @@ class element extends \mod_customcert\element {
/** /**
* 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) {
$mform->addElement('select', 'fileid', get_string('image', 'customcertelement_image'), self::get_images()); $mform->addElement('select', 'fileid', get_string('image', 'customcertelement_image'), self::get_images());
@ -282,7 +282,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) {
global $COURSE, $SITE; global $COURSE, $SITE;
@ -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

@ -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 = 2017111300; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_image'; $plugin->component = 'customcertelement_image';

View file

@ -0,0 +1,208 @@
<?php
// This file is part of the customcert 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/>.
/**
* This file contains the customcert element QR code's core interaction API.
*
* @package customcertelement_qrcode
* @copyright 2019 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace customcertelement_qrcode;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/tcpdf/tcpdf_barcodes_2d.php');
/**
* The customcert element QR code's core interaction API.
*
* @package customcertelement_qrcode
* @copyright 2019 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class element extends \mod_customcert\element {
/**
* @var string The barcode type.
*/
const BARCODETYPE = 'QRCODE';
/**
* This function renders the form elements when adding a customcert element.
*
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function render_form_elements($mform) {
\mod_customcert\element_helper::render_form_element_width($mform);
$mform->addElement('text', 'height', get_string('height', 'customcertelement_qrcode'), array('size' => 10));
$mform->setType('height', PARAM_INT);
$mform->setDefault('height', 0);
$mform->addHelpButton('height', 'height', 'customcertelement_qrcode');
if ($this->showposxy) {
\mod_customcert\element_helper::render_form_element_position($mform);
}
}
/**
* Performs validation on the element values.
*
* @param array $data the submitted data
* @param array $files the submitted files
* @return array the validation errors
*/
public function validate_form_elements($data, $files) {
// Array to return the errors.
$errors = [];
// Check if height is not set, or not numeric or less than 0.
if ((!isset($data['height'])) || (!is_numeric($data['height'])) || ($data['height'] <= 0)) {
$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) {
$errors += \mod_customcert\element_helper::validate_form_element_position($data);
}
$errors += \mod_customcert\element_helper::validate_form_element_width($data);
return $errors;
}
/**
* This will handle how form data will be saved into the data column in the
* customcert_elements table.
*
* @param \stdClass $data the form data
* @return string the json encoded array
*/
public function save_unique_data($data) {
$arrtostore = [
'width' => !empty($data->width) ? (int)$data->width : 0,
'height' => !empty($data->height) ? (int)$data->height : 0
];
return json_encode($arrtostore);
}
/**
* Sets the data on the form when editing an element.
*
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function definition_after_data($mform) {
parent::definition_after_data($mform);
// Set the image, width, height and alpha channel for this element.
if (!empty($this->get_data())) {
$imageinfo = json_decode($this->get_data());
if (!empty($imageinfo->height)) {
$element = $mform->getElement('height');
$element->setValue($imageinfo->height);
}
}
}
/**
* Handles rendering the element on the pdf.
*
* @param \pdf $pdf the pdf object
* @param bool $preview true if it is a preview, false otherwise
* @param \stdClass $user the user we are rendering this for
*/
public function render($pdf, $preview, $user) {
global $DB;
// If there is no element data, we have nothing to display.
if (empty($this->get_data())) {
return;
}
$imageinfo = json_decode($this->get_data());
if ($preview) {
// Generate the URL to verify this.
$qrcodeurl = new \moodle_url('/');
$qrcodeurl = $qrcodeurl->out(false);
} else {
// Get the information we need.
$sql = "SELECT c.id, ct.contextid, ci.code
FROM {customcert_issues} ci
JOIN {customcert} c
ON ci.customcertid = c.id
JOIN {customcert_templates} ct
ON c.templateid = ct.id
JOIN {customcert_pages} cp
ON cp.templateid = ct.id
WHERE ci.userid = :userid
AND cp.id = :pageid";
// Now we can get the issue for this user.
$issue = $DB->get_record_sql($sql, array('userid' => $user->id, 'pageid' => $this->get_pageid()),
'*', MUST_EXIST);
$code = $issue->code;
// Generate the URL to verify this.
$qrcodeurl = new \moodle_url('/mod/customcert/verify_certificate.php',
[
'contextid' => $issue->contextid,
'code' => $code,
'qrcode' => 1
]
);
$qrcodeurl = $qrcodeurl->out(false);
}
$barcode = new \TCPDF2DBarcode($qrcodeurl, self::BARCODETYPE);
$image = $barcode->getBarcodePngData($imageinfo->width, $imageinfo->height);
$location = make_request_directory() . '/target';
file_put_contents($location, $image);
$pdf->Image($location, $this->get_posx(), $this->get_posy(), $imageinfo->width, $imageinfo->height);
}
/**
* Render the element in html.
*
* This function is used to render the element when we are using the
* drag and drop interface to position it.
*
* @return string the html
*/
public function render_html() {
// If there is no element data, we have nothing to display.
if (empty($this->get_data())) {
return;
}
$imageinfo = json_decode($this->get_data());
$qrcodeurl = new \moodle_url('/');
$qrcodeurl = $qrcodeurl->out(false);
$barcode = new \TCPDF2DBarcode($qrcodeurl, self::BARCODETYPE);
return $barcode->getBarcodeHTML($imageinfo->width / 10, $imageinfo->height / 10);
}
}

View file

@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for customcertelement_qrcode.
*
* @package customcertelement_qrcode
* @copyright 2019 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace customcertelement_qrcode\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for customcertelement_qrcode implementing null_provider.
*
* @copyright 2019 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason() : string {
return 'privacy:metadata';
}
}

View file

@ -15,13 +15,16 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/** /**
* Definition of sub-plugins. * Strings for component 'customcertelement_qrcode', language 'en'.
* *
* @package mod_customcert * @package customcertelement_qrcode
* @copyright 2013 Mark Nelson <markn@moodle.com> * @copyright 2019 Mark Nelson <markn@moodle.com>
* @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
*/ */
defined('MOODLE_INTERNAL') || die(); $string['height'] = 'Height';
$string['height_help'] = 'Height help';
$subplugins = array('customcertelement' => 'mod/customcert/element'); $string['pluginname'] = 'QR code';
$string['privacy:metadata'] = 'The QR code plugin does not store any personal data.';
$string['width'] = 'Width';
$string['width_help'] = 'width help';

View file

@ -0,0 +1,29 @@
<?php
// This file is part of the customcert 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/>.
/**
* This file contains the version information for the QR code plugin.
*
* @package customcertelement_qrcode
* @copyright 2019 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
$plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_qrcode';

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 = 2017111300; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_studentname'; $plugin->component = 'customcertelement_studentname';

View file

@ -38,7 +38,7 @@ class element extends \mod_customcert\element {
/** /**
* 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) {
$mform->addElement('select', 'teacher', get_string('teacher', 'customcertelement_teachername'), $mform->addElement('select', 'teacher', get_string('teacher', 'customcertelement_teachername'),
@ -123,7 +123,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())) { if (!empty($this->get_data())) {

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 = 2017111300; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_teachername'; $plugin->component = 'customcertelement_teachername';

View file

@ -38,7 +38,7 @@ class element extends \mod_customcert\element {
/** /**
* 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) {
$mform->addElement('textarea', 'text', get_string('text', 'customcertelement_text')); $mform->addElement('textarea', 'text', get_string('text', 'customcertelement_text'));
@ -85,7 +85,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())) { if (!empty($this->get_data())) {

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 = 2017111300; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_text'; $plugin->component = 'customcertelement_text';

View file

@ -24,6 +24,8 @@
namespace customcertelement_userfield; namespace customcertelement_userfield;
use core_user\fields;
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
/** /**
@ -38,34 +40,31 @@ class element extends \mod_customcert\element {
/** /**
* 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) {
// Get the user profile fields. // Get the user profile fields.
$userfields = array( $userfields = array(
'firstname' => get_user_field_name('firstname'), 'firstname' => fields::get_display_name('firstname'),
'lastname' => get_user_field_name('lastname'), 'lastname' => fields::get_display_name('lastname'),
'email' => get_user_field_name('email'), 'username' => fields::get_display_name('username'),
'city' => get_user_field_name('city'), 'email' => fields::get_display_name('email'),
'country' => get_user_field_name('country'), 'city' => fields::get_display_name('city'),
'url' => get_user_field_name('url'), 'country' => fields::get_display_name('country'),
'icq' => get_user_field_name('icq'), 'url' => fields::get_display_name('url'),
'skype' => get_user_field_name('skype'), 'skype' => fields::get_display_name('skype'),
'aim' => get_user_field_name('aim'), 'idnumber' => fields::get_display_name('idnumber'),
'yahoo' => get_user_field_name('yahoo'), 'institution' => fields::get_display_name('institution'),
'msn' => get_user_field_name('msn'), 'department' => fields::get_display_name('department'),
'idnumber' => get_user_field_name('idnumber'), 'phone1' => fields::get_display_name('phone1'),
'institution' => get_user_field_name('institution'), 'phone2' => fields::get_display_name('phone2'),
'department' => get_user_field_name('department'), 'address' => fields::get_display_name('address')
'phone1' => get_user_field_name('phone1'),
'phone2' => get_user_field_name('phone2'),
'address' => get_user_field_name('address')
); );
// Get the user custom fields. // Get the user custom fields.
$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;
@ -116,7 +115,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())) { if (!empty($this->get_data())) {

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 = 2017111300; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_userfield'; $plugin->component = 'customcertelement_userfield';

View file

@ -38,7 +38,7 @@ class element extends \mod_customcert\element {
/** /**
* 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) {
$mform->addElement('text', 'width', get_string('width', 'customcertelement_userpicture'), array('size' => 10)); $mform->addElement('text', 'width', get_string('width', 'customcertelement_userpicture'), array('size' => 10));
@ -190,7 +190,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) {
// Set the image, width and height for this element. // Set the image, width and height for this element.

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 = 2017111300; // The current module version (Date: YYYYMMDDXX). $plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2017111300; // Requires this Moodle version (3.4). $plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_userpicture'; $plugin->component = 'customcertelement_userpicture';

View file

@ -22,8 +22,14 @@
* @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['aligncenter'] = 'Centered';
$string['alignleft'] = 'Left alignment';
$string['alignment'] = 'Alignment';
$string['alignment_help'] = 'This property sets the horizontal alignment of the element. Some elements may not support this, while the behaviour of others may differ.';
$string['alignright'] = 'Right alignment';
$string['awardedto'] = 'Awarded to'; $string['awardedto'] = 'Awarded to';
$string['cannotverifyallcertificates'] = 'You do not have the permission to verify all certificates on the site.'; $string['cannotverifyallcertificates'] = 'You do not have the permission to verify all certificates on the site.';
$string['certificate'] = 'Certificate'; $string['certificate'] = 'Certificate';
@ -41,6 +47,7 @@ $string['customcert:manageemailothers'] = 'Manage email others setting';
$string['customcert:manageverifyany'] = 'Manage verification setting'; $string['customcert:manageverifyany'] = 'Manage verification setting';
$string['customcert:managerequiredtime'] = 'Manage time required setting'; $string['customcert:managerequiredtime'] = 'Manage time required setting';
$string['customcert:manageprotection'] = 'Manage protection setting'; $string['customcert:manageprotection'] = 'Manage protection setting';
$string['customcert:receiveissue'] = 'Receive a certificate';
$string['customcert:view'] = 'View a custom certificate'; $string['customcert:view'] = 'View a custom certificate';
$string['customcert:viewreport'] = 'View course report'; $string['customcert:viewreport'] = 'View course report';
$string['customcert:viewallcertificates'] = 'View all certificates'; $string['customcert:viewallcertificates'] = 'View all certificates';
@ -55,6 +62,9 @@ $string['deleteissueconfirm'] = 'Are you sure you want to delete this certificat
$string['deleteissuedcertificates'] = 'Delete issued certificates'; $string['deleteissuedcertificates'] = 'Delete issued certificates';
$string['deletepageconfirm'] = 'Are you sure you want to delete this certificate page?'; $string['deletepageconfirm'] = 'Are you sure you want to delete this certificate page?';
$string['deletetemplateconfirm'] = 'Are you sure you want to delete this certificate template?'; $string['deletetemplateconfirm'] = 'Are you sure you want to delete this certificate template?';
$string['deliveryoptiondownload'] = 'Send to the browser and force a file download';
$string['deliveryoptioninline'] = 'Send the file inline to the browser';
$string['deliveryoptions'] = 'Delivery options';
$string['description'] = 'Description'; $string['description'] = 'Description';
$string['duplicate'] = 'Duplicate'; $string['duplicate'] = 'Duplicate';
$string['duplicateconfirm'] = 'Duplicate confirmation'; $string['duplicateconfirm'] = 'Duplicate confirmation';
@ -82,12 +92,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.';
@ -96,6 +105,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.';
@ -108,7 +118,7 @@ $string['invalidwidth'] = 'The width has to be a valid number greater than 0.';
$string['landscape'] = 'Landscape'; $string['landscape'] = 'Landscape';
$string['leftmargin'] = 'Left margin'; $string['leftmargin'] = 'Left margin';
$string['leftmargin_help'] = 'This is the left margin of the certificate PDF in mm.'; $string['leftmargin_help'] = 'This is the left margin of the certificate PDF in mm.';
$string['listofissues'] = 'Recipients'; $string['listofissues'] = 'Recipients: {$a}';
$string['load'] = 'Load'; $string['load'] = 'Load';
$string['loadtemplate'] = 'Load template'; $string['loadtemplate'] = 'Load template';
$string['loadtemplatemsg'] = 'Are you sure you wish to load this template? This will remove any existing pages and elements for this certificate.'; $string['loadtemplatemsg'] = 'Are you sure you wish to load this template? This will remove any existing pages and elements for this certificate.';

View file

@ -264,6 +264,8 @@ function customcert_supports($feature) {
return true; return true;
case FEATURE_MOD_INTRO: case FEATURE_MOD_INTRO:
return true; return true;
case FEATURE_SHOW_DESCRIPTION:
return true;
case FEATURE_COMPLETION_TRACKS_VIEWS: case FEATURE_COMPLETION_TRACKS_VIEWS:
return true; return true;
case FEATURE_BACKUP_MOODLE2: case FEATURE_BACKUP_MOODLE2:

57
load_template_cli.php Normal file
View file

@ -0,0 +1,57 @@
<?php
// This file is part of the customcert 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/>.
define('CLI_SCRIPT', true);
require_once('../../config.php');
$tid = $argv[1];
$ltid = $argv[2];
$template = $DB->get_record('customcert_templates', array('id' => $tid), '*', MUST_EXIST);
$template = new \mod_customcert\template($template);
$loadtemplate = $DB->get_record('customcert_templates', array('id' => $ltid), '*', MUST_EXIST);
$loadtemplate = new \mod_customcert\template($loadtemplate);
$cm = $template->get_cm();
if ($template->get_context()->contextlevel == CONTEXT_MODULE) {
$customcert = $DB->get_record('customcert', ['id' => $cm->instance], '*', MUST_EXIST);
$title = $customcert->name;
$heading = format_string($title);
} else {
$title = $SITE->fullname;
$heading = $title;
}
$sql = "SELECT e.*
FROM {customcert_elements} e
INNER JOIN {customcert_pages} p
ON e.pageid = p.id
WHERE p.templateid = :templateid";
if ($elements = $DB->get_records_sql($sql, array('templateid' => $template->get_id()))) {
foreach ($elements as $element) {
if ($e = \mod_customcert\element_factory::get_element_instance($element)) {
$e->delete();
}
}
}
$DB->delete_records('customcert_pages', array('templateid' => $template->get_id()));
$loadtemplate->copy_to_template($template->get_id());

View file

@ -47,7 +47,6 @@ require_login();
require_capability('mod/customcert:manage', $context); require_capability('mod/customcert:manage', $context);
$title = $SITE->fullname; $title = $SITE->fullname;
$heading = $title;
// Set up the page. // Set up the page.
$pageurl = new moodle_url('/mod/customcert/manage_templates.php'); $pageurl = new moodle_url('/mod/customcert/manage_templates.php');
@ -61,6 +60,8 @@ if ($tid && $action && confirm_sesskey()) {
$PAGE->navbar->add(get_string('managetemplates', 'customcert')); $PAGE->navbar->add(get_string('managetemplates', 'customcert'));
} }
$heading = format_string($title, true, ['context' => $context]);
if ($tid) { if ($tid) {
if ($action && confirm_sesskey()) { if ($action && confirm_sesskey()) {
$nourl = new moodle_url('/mod/customcert/manage_templates.php'); $nourl = new moodle_url('/mod/customcert/manage_templates.php');

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

@ -22,6 +22,8 @@
* @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
*/ */
use mod_customcert\certificate;
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
require_once($CFG->dirroot.'/course/moodleform_mod.php'); require_once($CFG->dirroot.'/course/moodleform_mod.php');
@ -55,14 +57,20 @@ class mod_customcert_mod_form extends moodleform_mod {
$this->standard_intro_elements(get_string('description', 'customcert')); $this->standard_intro_elements(get_string('description', 'customcert'));
$optionsheader = $mform->createElement('header', 'options', get_string('options', 'customcert')); $mform->addElement('header', 'options', get_string('options', 'customcert'));
$deliveryoptions = [
certificate::DELIVERY_OPTION_INLINE => get_string('deliveryoptioninline', 'customcert'),
certificate::DELIVERY_OPTION_DOWNLOAD => get_string('deliveryoptiondownload', 'customcert')
];
$mform->addElement('select', 'deliveryoption', get_string('deliveryoptions', 'customcert'), $deliveryoptions);
$mform->setDefault('deliveryoption', certificate::DELIVERY_OPTION_INLINE);
if (has_capability('mod/customcert:manageemailstudents', $this->get_context())) { if (has_capability('mod/customcert:manageemailstudents', $this->get_context())) {
$mform->addElement('selectyesno', 'emailstudents', get_string('emailstudents', 'customcert')); $mform->addElement('selectyesno', 'emailstudents', get_string('emailstudents', 'customcert'));
$mform->setDefault('emailstudents', get_config('customcert', 'emailstudents')); $mform->setDefault('emailstudents', get_config('customcert', 'emailstudents'));
$mform->addHelpButton('emailstudents', 'emailstudents', 'customcert'); $mform->addHelpButton('emailstudents', 'emailstudents', 'customcert');
$mform->setType('emailstudents', PARAM_INT); $mform->setType('emailstudents', PARAM_INT);
$firstoption = 'emailstudents';
} }
if (has_capability('mod/customcert:manageemailteachers', $this->get_context())) { if (has_capability('mod/customcert:manageemailteachers', $this->get_context())) {
@ -70,7 +78,6 @@ class mod_customcert_mod_form extends moodleform_mod {
$mform->setDefault('emailteachers', get_config('customcert', 'emailteachers')); $mform->setDefault('emailteachers', get_config('customcert', 'emailteachers'));
$mform->addHelpButton('emailteachers', 'emailteachers', 'customcert'); $mform->addHelpButton('emailteachers', 'emailteachers', 'customcert');
$mform->setType('emailteachers', PARAM_INT); $mform->setType('emailteachers', PARAM_INT);
$firstoption = empty($firstoption) ? 'emailteachers' : $firstoption;
} }
if (has_capability('mod/customcert:manageemailothers', $this->get_context())) { if (has_capability('mod/customcert:manageemailothers', $this->get_context())) {
@ -78,7 +85,6 @@ class mod_customcert_mod_form extends moodleform_mod {
$mform->addHelpButton('emailothers', 'emailothers', 'customcert'); $mform->addHelpButton('emailothers', 'emailothers', 'customcert');
$mform->setDefault('emailothers', get_config('customcert', 'emailothers')); $mform->setDefault('emailothers', get_config('customcert', 'emailothers'));
$mform->setType('emailothers', PARAM_TEXT); $mform->setType('emailothers', PARAM_TEXT);
$firstoption = empty($firstoption) ? 'emailothers' : $firstoption;
} }
if (has_capability('mod/customcert:manageverifyany', $this->get_context())) { if (has_capability('mod/customcert:manageverifyany', $this->get_context())) {
@ -86,7 +92,6 @@ class mod_customcert_mod_form extends moodleform_mod {
$mform->addHelpButton('verifyany', 'verifycertificateanyone', 'customcert'); $mform->addHelpButton('verifyany', 'verifycertificateanyone', 'customcert');
$mform->setDefault('verifyany', get_config('customcert', 'verifyany')); $mform->setDefault('verifyany', get_config('customcert', 'verifyany'));
$mform->setType('verifyany', PARAM_INT); $mform->setType('verifyany', PARAM_INT);
$firstoption = empty($firstoption) ? 'verifyany' : $firstoption;
} }
if (has_capability('mod/customcert:managerequiredtime', $this->get_context())) { if (has_capability('mod/customcert:managerequiredtime', $this->get_context())) {
@ -94,7 +99,6 @@ class mod_customcert_mod_form extends moodleform_mod {
$mform->addHelpButton('requiredtime', 'coursetimereq', 'customcert'); $mform->addHelpButton('requiredtime', 'coursetimereq', 'customcert');
$mform->setDefault('requiredtime', get_config('customcert', 'requiredtime')); $mform->setDefault('requiredtime', get_config('customcert', 'requiredtime'));
$mform->setType('requiredtime', PARAM_INT); $mform->setType('requiredtime', PARAM_INT);
$firstoption = empty($firstoption) ? 'requiredtime' : $firstoption;
} }
if (has_capability('mod/customcert:manageprotection', $this->get_context())) { if (has_capability('mod/customcert:manageprotection', $this->get_context())) {
@ -106,11 +110,6 @@ class mod_customcert_mod_form extends moodleform_mod {
$mform->setType('protection_print', PARAM_BOOL); $mform->setType('protection_print', PARAM_BOOL);
$mform->setType('protection_modify', PARAM_BOOL); $mform->setType('protection_modify', PARAM_BOOL);
$mform->setType('protection_copy', PARAM_BOOL); $mform->setType('protection_copy', PARAM_BOOL);
$firstoption = empty($firstoption) ? 'protection_print' : $firstoption;
}
if (!empty($firstoption)) {
$mform->insertElementBefore($optionsheader, $firstoption);
} }
$this->standard_coursemodule_elements(); $this->standard_coursemodule_elements();
@ -227,13 +226,13 @@ class mod_customcert_mod_form extends moodleform_mod {
$protection = explode(', ', $protection); $protection = explode(', ', $protection);
if (in_array(\mod_customcert\certificate::PROTECTION_PRINT, $protection)) { if (in_array(certificate::PROTECTION_PRINT, $protection)) {
$data->protection_print = 1; $data->protection_print = 1;
} }
if (in_array(\mod_customcert\certificate::PROTECTION_MODIFY, $protection)) { if (in_array(certificate::PROTECTION_MODIFY, $protection)) {
$data->protection_modify = 1; $data->protection_modify = 1;
} }
if (in_array(\mod_customcert\certificate::PROTECTION_COPY, $protection)) { if (in_array(certificate::PROTECTION_COPY, $protection)) {
$data->protection_copy = 1; $data->protection_copy = 1;
} }

View file

@ -30,6 +30,11 @@ $downloadcert = optional_param('downloadcert', '', PARAM_BOOL);
if ($downloadcert) { if ($downloadcert) {
$certificateid = required_param('certificateid', PARAM_INT); $certificateid = required_param('certificateid', PARAM_INT);
$customcert = $DB->get_record('customcert', array('id' => $certificateid), '*', MUST_EXIST); $customcert = $DB->get_record('customcert', array('id' => $certificateid), '*', MUST_EXIST);
// Check there exists an issued certificate for this user.
if (!$issue = $DB->get_record('customcert_issues', ['userid' => $userid, 'customcertid' => $customcert->id])) {
throw new moodle_exception('You have not been issued a certificate');
}
} }
$page = optional_param('page', 0, PARAM_INT); $page = optional_param('page', 0, PARAM_INT);
$perpage = optional_param('perpage', \mod_customcert\certificate::CUSTOMCERT_PER_PAGE, PARAM_INT); $perpage = optional_param('perpage', \mod_customcert\certificate::CUSTOMCERT_PER_PAGE, PARAM_INT);
@ -44,9 +49,15 @@ $user = \core_user::get_user($userid, '*', MUST_EXIST);
// If we are viewing certificates that are not for the currently logged in user then do a capability check. // If we are viewing certificates that are not for the currently logged in user then do a capability check.
if (($userid != $USER->id) && !has_capability('mod/customcert:viewallcertificates', context_system::instance())) { if (($userid != $USER->id) && !has_capability('mod/customcert:viewallcertificates', context_system::instance())) {
print_error('You are not allowed to view these certificates'); throw new moodle_exception('You are not allowed to view these certificates');
} }
$PAGE->set_url($pageurl);
$PAGE->set_context(context_user::instance($userid));
$PAGE->set_title(get_string('mycertificates', 'customcert'));
$PAGE->set_pagelayout('standard');
$PAGE->navigation->extend_for_user($user);
// Check if we requested to download a certificate. // Check if we requested to download a certificate.
if ($downloadcert) { if ($downloadcert) {
$template = $DB->get_record('customcert_templates', array('id' => $customcert->templateid), '*', MUST_EXIST); $template = $DB->get_record('customcert_templates', array('id' => $customcert->templateid), '*', MUST_EXIST);
@ -63,12 +74,6 @@ if ($table->is_downloading()) {
exit(); exit();
} }
$PAGE->set_url($pageurl);
$PAGE->set_context(context_user::instance($userid));
$PAGE->set_title(get_string('mycertificates', 'customcert'));
$PAGE->set_pagelayout('standard');
$PAGE->navigation->extend_for_user($user);
// Additional page setup. // Additional page setup.
$PAGE->navbar->add(get_string('profile'), new moodle_url('/user/profile.php', array('id' => $userid))); $PAGE->navbar->add(get_string('profile'), new moodle_url('/user/profile.php', array('id' => $userid)));
$PAGE->navbar->add(get_string('mycertificates', 'customcert')); $PAGE->navbar->add(get_string('mycertificates', 'customcert'));

View file

@ -112,6 +112,18 @@ if ($elements) {
default: default:
$class = 'element refpoint-left'; $class = 'element refpoint-left';
} }
switch ($element->alignment) {
case \mod_customcert\element::ALIGN_CENTER:
$class .= ' align-center';
break;
case \mod_customcert\element::ALIGN_RIGHT:
$class .= ' align-right';
break;
case \mod_customcert\element::ALIGN_LEFT:
default:
$class .= ' align-left';
break;
}
$html .= html_writer::tag('div', $e->render_html(), array('class' => $class, $html .= html_writer::tag('div', $e->render_html(), array('class' => $class,
'data-refpoint' => $element->refpoint, 'id' => 'element-' . $element->id)); 'data-refpoint' => $element->refpoint, 'id' => 'element-' . $element->id));
} }

View file

@ -57,6 +57,18 @@
margin: -4px -5px -5px 4px; margin: -4px -5px -5px 4px;
} }
#page-mod-customcert-rearrange .element.align-left {
text-align: left;
}
#page-mod-customcert-rearrange .element.align-center {
text-align: center;
}
#page-mod-customcert-rearrange .element.align-right {
text-align: right;
}
#page-mod-customcert-rearrange #pdf { #page-mod-customcert-rearrange #pdf {
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;

View file

@ -0,0 +1,169 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template mod_customcert/mobile_view_activity_page
The main page to view the custom certificate activity
Classes required for JS:
* None
Data attibutes required for JS:
* All data attributes are required
Context variables required for this template:
* certificate
* cmid
* hasissues
* issues
* showgroups
* groups
* canmanage
* requiredtimemet
* hasrecipients
* recipients
* fileurl
* showreport
* currenttimestamp
Example context (json):
{
"certificate": {
"id": "1",
"course": "2",
"name": "A rad certificate name!",
"intro": "A certificate",
"requiredtime": "60"
},
"cmid": "25",
"issue": {
"timecreated": "1528370177"
},
"showgroups": "true",
"groups": [
{
"id": "2",
"selected": "false",
"name": "Group A"
}
],
"canmanage": "true",
"requiredtimemet": "true",
"fileurl": "http://yoursite.com/mod/customcert/mobile/pluginfile.php?id=4",
"showreport": "true",
"hasrecipients": "true",
"recipients": [
{
"id": "2",
"issueid": "3",
"displayname": "Michaelangelo (Mickey)",
"fileurl": "http://yoursite.com/mod/customcert/mobile/pluginfile.php?id=4",
"timecreated": "1528370177"
}
],
"currenttimestamp": "1528370177"
}
}}
{{=<% %>=}}
<core-course-module-description description="<% certificate.intro %>" component="mod_customcert" componentId="<% cmid %>"></core-course-module-description>
<ion-list>
<%^canmanage%>
<%#requiredtimemet%>
<ion-item>
<ion-label>
{{ 'plugin.mod_customcert.receiveddate' | translate }}
<br />
<div class="timerewarded">
<%#issue%>
{{ <% timecreated %> | coreToLocaleString }}
<%/issue%>
<%^issue%>
{{ 'plugin.mod_customcert.notissued' | translate }}
<%/issue%>
</div>
</ion-label>
<div slot="end" class="flex-row">
<ion-button fill="clear" [core-download-file]="{fileurl: '<% fileurl %>', timemodified: '<% currenttimestamp %>'}" moduleId="<% cmid %>" courseId="<% certificate.course %>" component="mod_customcert" [attr.aria-label]="'core.download' | translate">
<ion-icon name="cloud-download" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
</div>
</ion-item>
<%/requiredtimemet%>
<%^requiredtimemet%>
<ion-item>
<ion-label>
<p>{{ 'plugin.mod_customcert.requiredtimenotmet' | translate: {$a: { requiredtime: <% certificate.requiredtime %>} } }}</p>
</ion-label>
</ion-item>
<%/requiredtimemet%>
<%/canmanage%>
<%#canmanage%>
<ion-button expand="block" class="ion-margin" core-course-download-module-main-file moduleId="<% cmid %>" courseId="<% certificate.course %>" component="mod_customcert" [files]="[{fileurl: '<% fileurl %>', timemodified: '<% currenttimestamp %>'}]">
<ion-icon name="cloud-download" slot="start" aria-hidden="true"></ion-icon>
{{ 'plugin.mod_customcert.getcustomcert' | translate }}
</ion-button>
<%/canmanage%>
<%#showreport%>
<ion-item>
<ion-label>
{{ 'plugin.mod_customcert.listofissues' | translate: { $a: <% numrecipients %> } }}
</ion-label>
</ion-item>
<%#showgroups%>
<ion-item>
<ion-label>{{ 'plugin.mod_customcert.selectagroup' | translate }}</ion-label>
<ion-select [(ngModel)]="CONTENT_OTHERDATA.group" name="group" (ionChange)="updateContent({cmid: <% cmid %>, courseid: <% certificate.course %>, group: CONTENT_OTHERDATA.group})" interface="popover">
<%#groups%>
<ion-select-option [value]="<% id %>"><% name %></ion-select-option>
<%/groups%>
</ion-select>
</ion-item>
<%/showgroups%>
<%#hasrecipients%>
<%#recipients%>
<ion-item>
<ion-label>
<% displayname %>
<br />
<div class="timerewarded">{{ <% timecreated %> | coreToLocaleString }}</div>
</ion-label>
<div slot="end" class="flex-row">
<ion-button fill="clear" [core-download-file]="{fileurl: '<% fileurl %>', timemodified: '<% currenttimestamp %>'}" moduleId="<% cmid %>" courseId="<% certificate.course %>" component="mod_customcert" [attr.aria-label]="'core.download' | translate">
<ion-icon name="cloud-download" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<%#canmanage%>
<ion-button fill="clear" core-site-plugins-call-ws name="mod_customcert_delete_issue"
[params]="{certificateid: <% certificate.id %>, issueid: <% issueid %>}"
[preSets]="{getFromCache: 0, saveToCache: 0, typeExpected: 'boolean'}"
confirmMessage="{{ 'plugin.mod_customcert.deleteissueconfirm' | translate }}"
refreshOnSuccess="true" [attr.aria-label]="'core.delete' | translate">
<ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<%/canmanage%>
</div>
</ion-item>
<%/recipients%>
<%/hasrecipients%>
<%^hasrecipients%>
<ion-item>
<ion-label>
{{ 'plugin.mod_customcert.nothingtodisplay' | translate }}
</ion-label>
</ion-item>
<%/hasrecipients%>
<%/showreport%>
</ion-list>

View file

@ -8,11 +8,9 @@ Feature: Being able to correctly display options on the certificate activity edi
And the following "users" exist: And the following "users" exist:
| username | firstname | lastname | email | | username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com | | teacher1 | Teacher | 1 | teacher1@example.com |
| manager1 | Manager | 1 | Manager1@example.com |
And the following "course enrolments" exist: And the following "course enrolments" exist:
| user | course | role | | user | course | role |
| teacher1 | C1 | editingteacher | | teacher1 | C1 | editingteacher |
| manager1 | C1 | manager |
And the following "activities" exist: And the following "activities" exist:
| activity | name | intro | course | idnumber | | activity | name | intro | course | idnumber |
| customcert | Custom certificate 1 | Custom certificate 1 intro | C1 | customcert1 | | customcert | Custom certificate 1 | Custom certificate 1 intro | C1 | customcert1 |
@ -29,40 +27,7 @@ Feature: Being able to correctly display options on the certificate activity edi
And I should see "Required minutes in course" And I should see "Required minutes in course"
And I should see "Set protection" And I should see "Set protection"
Scenario: Create an activity as an Editing Teacher I can see all custom certificate options @javascript
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I add a "Custom certificate" to section "1"
And I should see "Email students"
And I should see "Email teachers"
And I should see "Email others"
And I should see "Allow anyone to verify a certificate"
And I should see "Required minutes in course"
And I should see "Set protection"
Scenario: Add an activity as a Manager I can see all custom certificate options
And I log in as "manager1"
And I am on "Course 1" course homepage with editing mode on
And I add a "Custom certificate" to section "1"
And I should see "Email students"
And I should see "Email teachers"
And I should see "Email others"
And I should see "Allow anyone to verify a certificate"
And I should see "Required minutes in course"
And I should see "Set protection"
Scenario: Edit an activity as a Manager Teacher I can see all custom certificate options
And I log in as "manager1"
And I am on "Course 1" course homepage with editing mode on
And I follow "Custom certificate 1"
And I navigate to "Edit settings" in current page administration
And I should see "Email students"
And I should see "Email teachers"
And I should see "Email others"
And I should see "Allow anyone to verify a certificate"
And I should see "Required minutes in course"
And I should see "Set protection"
Scenario: Create an activity as an Editing Teacher without required capabilities I can't see all custom certificate options Scenario: Create an activity as an Editing Teacher without required capabilities I can't see all custom certificate options
And I log in as "admin" And I log in as "admin"
And I set the following system permissions of "Teacher" role: And I set the following system permissions of "Teacher" role:
@ -106,8 +71,9 @@ Feature: Being able to correctly display options on the certificate activity edi
And I should not see "Required minutes in course" And I should not see "Required minutes in course"
And I should not see "Set protection" And I should not see "Set protection"
@javascript
Scenario: Add an activity using default custom certificate options Scenario: Add an activity using default custom certificate options
And I log in as "manager1" And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on And I am on "Course 1" course homepage with editing mode on
And I add a "Custom certificate" to section "0" And I add a "Custom certificate" to section "0"
And the field "emailstudents" matches value "0" And the field "emailstudents" matches value "0"
@ -119,6 +85,7 @@ Feature: Being able to correctly display options on the certificate activity edi
And the field "protection_modify" matches value "0" And the field "protection_modify" matches value "0"
And the field "protection_copy" matches value "0" And the field "protection_copy" matches value "0"
@javascript
Scenario: Add an activity using configured custom certificate options Scenario: Add an activity using configured custom certificate options
And the following config values are set as admin: And the following config values are set as admin:
| emailstudents | 1 | customcert | | emailstudents | 1 | customcert |
@ -129,7 +96,7 @@ Feature: Being able to correctly display options on the certificate activity edi
| protection_print | 1 | customcert | | protection_print | 1 | customcert |
| protection_modify | 1 | customcert | | protection_modify | 1 | customcert |
| protection_copy | 1 | customcert | | protection_copy | 1 | customcert |
And I log in as "manager1" And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on And I am on "Course 1" course homepage with editing mode on
And I add a "Custom certificate" to section "1" And I add a "Custom certificate" to section "1"
And the field "emailstudents" matches value "1" And the field "emailstudents" matches value "1"

View file

@ -40,7 +40,7 @@ class behat_mod_customcert extends behat_base {
/** /**
* Adds an element to the specified page of a template. * Adds an element to the specified page of a template.
* *
* @codingStandardsIgnoreLine * phpcs:ignore
* @Given /^I add the element "(?P<element_name>(?:[^"]|\\")*)" to page "(?P<page_number>\d+)" of the "(?P<template_name>(?:[^"]|\\")*)" certificate template$/ * @Given /^I add the element "(?P<element_name>(?:[^"]|\\")*)" to page "(?P<page_number>\d+)" of the "(?P<template_name>(?:[^"]|\\")*)" certificate template$/
* @param string $elementname * @param string $elementname
* @param int $pagenum * @param int $pagenum

View file

@ -79,10 +79,29 @@ 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"
# Course field.
And I add the element "Course field" to page "1" of the "Custom certificate 1" certificate template
And I set the following fields to these values:
| Font | Helvetica |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
And I press "Save changes"
And I should see "Course field" in the "elementstable" "table"
And I click on ".edit-icon" "css_element" in the "Course field" "table_row"
And the following fields match these values:
| Font | Helvetica |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
And I press "Save changes"
# Course name. # Course name.
And I add the element "Course name" to page "1" of the "Custom certificate 1" certificate template And I add the element "Course 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:
| Font | Helvetica | | Font | Helvetica |
| Type | Short name |
| Size | 20 | | Size | 20 |
| Colour | #045ECD | | Colour | #045ECD |
| Width | 20 | | Width | 20 |
@ -92,6 +111,7 @@ Feature: Being able to manage elements in a certificate template
And I click on ".edit-icon" "css_element" in the "Course name" "table_row" And I click on ".edit-icon" "css_element" in the "Course name" "table_row"
And the following fields match these values: And the following fields match these values:
| Font | Helvetica | | Font | Helvetica |
| Type | Short name |
| Size | 20 | | Size | 20 |
| Colour | #045ECD | | Colour | #045ECD |
| Width | 20 | | Width | 20 |
@ -119,6 +139,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,7 +200,7 @@ 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 |
@ -155,7 +211,7 @@ Feature: Being able to manage elements in a certificate template
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 |
@ -166,7 +222,7 @@ Feature: Being able to manage elements in a certificate template
# 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 |
@ -176,7 +232,7 @@ Feature: Being able to manage elements in a certificate template
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 |
@ -287,6 +343,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

@ -21,6 +21,8 @@ Feature: Being able to view the certificates that have been issued
And the following "activities" exist: And the following "activities" exist:
| activity | name | intro | course | idnumber | | activity | name | intro | course | idnumber |
| customcert | Custom certificate 1 | Custom certificate 1 intro | C1 | customcert1 | | customcert | Custom certificate 1 | Custom certificate 1 intro | C1 | customcert1 |
And the following config values are set as admin:
| showuseridentity | username,email |
Scenario: View the issued certificates Scenario: View the issued certificates
And I log in as "student1" And I log in as "student1"
@ -38,6 +40,12 @@ Feature: Being able to view the certificates that have been issued
And I follow "Custom certificate 1" And I follow "Custom certificate 1"
And I should see "Student 1" And I should see "Student 1"
And I should see "Student 2" And I should see "Student 2"
And I should see "Email address"
And I should see "Username"
And "Student 1" row "Email address" column of "generaltable" table should contain "student1@example.com"
And "Student 1" row "Username" column of "generaltable" table should contain "student1"
And "Student 2" row "Email address" column of "generaltable" table should contain "student2@example.com"
And "Student 2" row "Username" column of "generaltable" table should contain "student2"
Scenario: Delete an issued certificate Scenario: Delete an issued certificate
And I log in as "student1" And I log in as "student1"

View file

@ -40,7 +40,7 @@ class mod_customcert_element_helper_testcase extends advanced_testcase {
/** /**
* Test set up. * Test set up.
*/ */
public function setUp() { public function setUp(): void {
$this->resetAfterTest(); $this->resetAfterTest();
} }
@ -180,28 +180,36 @@ 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);
} }
/** /**
* Test we return the correct grade information for an activity. * Test we return the correct grade information for an activity.
*/ */
public function test_get_mod_grade_info() { public function test_get_mod_grade_info() {
global $CFG;
// Set that we want 3 decimals to display.
$CFG->grade_decimalpoints = 3;
// Create a course. // Create a course.
$course = $this->getDataGenerator()->create_course(); $course = $this->getDataGenerator()->create_course();
// Set that we want 3 decimals to display.
grade_set_setting($course->id, 'decimalpoints', 3);
// Create two users. // Create two users.
$student1 = $this->getDataGenerator()->create_user(); $student1 = $this->getDataGenerator()->create_user();
$student2 = $this->getDataGenerator()->create_user(); $student2 = $this->getDataGenerator()->create_user();
@ -257,23 +265,20 @@ class mod_customcert_element_helper_testcase extends advanced_testcase {
$this->assertEquals(null, $grade->get_grade()); $this->assertEquals(null, $grade->get_grade());
$this->assertEquals('-', $grade->get_displaygrade()); $this->assertEquals('-', $grade->get_displaygrade());
$this->assertEquals(null, $grade->get_dategraded()); $this->assertEquals(null, $grade->get_dategraded());
grade_get_setting($course->id, null, null, true);
} }
/** /**
* Test we return the correct grade information for a course. * Test we return the correct grade information for a course.
*/ */
public function test_get_course_grade_info() { public function test_get_course_grade_info() {
global $CFG;
// Including to use constant.
require_once($CFG->dirroot . '/mod/customcert/element/grade/classes/element.php');
// Set that we want 3 decimals to display.
$CFG->grade_decimalpoints = 3;
// Create a course. // Create a course.
$course = $this->getDataGenerator()->create_course(); $course = $this->getDataGenerator()->create_course();
// Set that we want 3 decimals to display.
grade_set_setting($course->id, 'decimalpoints', 3);
// Create two users. // Create two users.
$student1 = $this->getDataGenerator()->create_user(); $student1 = $this->getDataGenerator()->create_user();
$student2 = $this->getDataGenerator()->create_user(); $student2 = $this->getDataGenerator()->create_user();
@ -320,20 +325,20 @@ class mod_customcert_element_helper_testcase extends advanced_testcase {
$this->assertEquals(null, $grade->get_grade()); $this->assertEquals(null, $grade->get_grade());
$this->assertEquals('-', $grade->get_displaygrade()); $this->assertEquals('-', $grade->get_displaygrade());
$this->assertEquals(null, $grade->get_dategraded()); $this->assertEquals(null, $grade->get_dategraded());
grade_get_setting($course->id, null, null, true);
} }
/** /**
* Test we return the correct grade information for a grade item. * Test we return the correct grade information for a grade item.
*/ */
public function test_get_grade_item_info() { public function test_get_grade_item_info() {
global $CFG;
// Set that we want 3 decimals to display.
$CFG->grade_decimalpoints = 3;
// Create a course. // Create a course.
$course = $this->getDataGenerator()->create_course(); $course = $this->getDataGenerator()->create_course();
// Set that we want 3 decimals to display.
grade_set_setting($course->id, 'decimalpoints', 3);
// Create two users. // Create two users.
$student1 = $this->getDataGenerator()->create_user(); $student1 = $this->getDataGenerator()->create_user();
$student2 = $this->getDataGenerator()->create_user(); $student2 = $this->getDataGenerator()->create_user();
@ -382,5 +387,7 @@ class mod_customcert_element_helper_testcase extends advanced_testcase {
$this->assertEquals(null, $grade->get_grade()); $this->assertEquals(null, $grade->get_grade());
$this->assertEquals('-', $grade->get_displaygrade()); $this->assertEquals('-', $grade->get_displaygrade());
$this->assertEquals(null, $grade->get_dategraded()); $this->assertEquals(null, $grade->get_dategraded());
grade_get_setting($course->id, null, null, true);
} }
} }

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.
* *
@ -40,10 +38,85 @@ class mod_customcert_task_email_certificate_task_testcase extends advanced_testc
/** /**
* Test set up. * Test set up.
*/ */
public function setUp() { public function setUp(): void {
$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.
*/
public function test_email_certificates_no_cap() {
global $DB;
// Create a course.
$course = $this->getDataGenerator()->create_course();
// Create some users.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
// Enrol two of them in the course as students but revoke their right to receive a certificate issue.
$roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
$this->getDataGenerator()->enrol_user($user1->id, $course->id);
$this->getDataGenerator()->enrol_user($user2->id, $course->id);
unassign_capability('mod/customcert:receiveissue', $roleids['student']);
// Create a custom certificate.
$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.
$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.
$this->assertCount(0, $emails);
}
/** /**
* Tests the email certificate task for students. * Tests the email certificate task for students.
*/ */
@ -70,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);
@ -95,11 +184,9 @@ class mod_customcert_task_email_certificate_task_testcase extends advanced_testc
// Confirm that we sent out emails to the two users. // Confirm that we sent out emails to the two users.
$this->assertCount(2, $emails); $this->assertCount(2, $emails);
$this->assertContains(fullname($user3), $emails[0]->header);
$this->assertEquals($CFG->noreplyaddress, $emails[0]->from); $this->assertEquals($CFG->noreplyaddress, $emails[0]->from);
$this->assertEquals($user1->email, $emails[0]->to); $this->assertEquals($user1->email, $emails[0]->to);
$this->assertContains(fullname($user3), $emails[1]->header);
$this->assertEquals($CFG->noreplyaddress, $emails[1]->from); $this->assertEquals($CFG->noreplyaddress, $emails[1]->from);
$this->assertEquals($user2->email, $emails[1]->to); $this->assertEquals($user2->email, $emails[1]->to);
@ -138,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();
@ -150,11 +253,9 @@ class mod_customcert_task_email_certificate_task_testcase extends advanced_testc
// Confirm that we only sent out 2 emails, both emails to the teacher for the two students. // Confirm that we only sent out 2 emails, both emails to the teacher for the two students.
$this->assertCount(2, $emails); $this->assertCount(2, $emails);
$this->assertContains(fullname($user3), utf8_encode($emails[0]->header));
$this->assertEquals($CFG->noreplyaddress, $emails[0]->from); $this->assertEquals($CFG->noreplyaddress, $emails[0]->from);
$this->assertEquals($user3->email, $emails[0]->to); $this->assertEquals($user3->email, $emails[0]->to);
$this->assertContains(fullname($user3), utf8_encode($emails[1]->header));
$this->assertEquals($CFG->noreplyaddress, $emails[1]->from); $this->assertEquals($CFG->noreplyaddress, $emails[1]->from);
$this->assertEquals($user3->email, $emails[1]->to); $this->assertEquals($user3->email, $emails[1]->to);
} }
@ -163,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();
@ -177,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();
@ -189,11 +306,9 @@ class mod_customcert_task_email_certificate_task_testcase extends advanced_testc
// Confirm that we only sent out 2 emails, both emails to the other address that was valid for the two students. // Confirm that we only sent out 2 emails, both emails to the other address that was valid for the two students.
$this->assertCount(2, $emails); $this->assertCount(2, $emails);
$this->assertContains(fullname(get_admin()), utf8_encode($emails[0]->header));
$this->assertEquals($CFG->noreplyaddress, $emails[0]->from); $this->assertEquals($CFG->noreplyaddress, $emails[0]->from);
$this->assertEquals('testcustomcert@example.com', $emails[0]->to); $this->assertEquals('testcustomcert@example.com', $emails[0]->to);
$this->assertContains(fullname(get_admin()), utf8_encode($emails[1]->header));
$this->assertEquals($CFG->noreplyaddress, $emails[1]->from); $this->assertEquals($CFG->noreplyaddress, $emails[1]->from);
$this->assertEquals('testcustomcert@example.com', $emails[1]->to); $this->assertEquals('testcustomcert@example.com', $emails[1]->to);
} }
@ -215,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));
@ -253,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

@ -40,7 +40,7 @@ class mod_customcert_external_test_testcase extends advanced_testcase {
/** /**
* Test set up. * Test set up.
*/ */
public function setUp() { public function setUp(): void {
$this->resetAfterTest(); $this->resetAfterTest();
} }

Some files were not shown because too many files have changed in this diff Show more