Compare commits

...

185 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
112 changed files with 4052 additions and 587 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_35_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,7 +2,150 @@
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.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
- Make it clear what element values are just an example when previewing the PDF (#144).
### Fixed
- Missing implementation for privacy provider (#260).
- Use course module context when calling format_string/text (#200).
- Exception being thrown when adding the 'teachername' element to site template (#261).
## [3.5.5] - 2018-12-20
### Added
@ -20,12 +163,15 @@ Note - All hash comments refer to the issue number. Eg. #169 refers to https://g
- Allow verification of deleted users (#159).
- The 'element' field in the 'customcert_elements' table has been changed from a Text field to varchar(255) (#241).
- The 'Completion date' option in the 'date' element is only displayed when completion is enabled (#160).
- Instead of assuming 2 decimal points for percentages, we now make use of the decimal value setting, which the
function `grade_format_gradevalue` does by default if no decimal value is passed.
### Fixed
- Issue with scales not displaying correctly (#242).
- The report now respects the setting 'Show user identity' (#224).
- Removed incorrect course reset logic (#223).
- Description strings referring to the wrong setting (#254).
## [3.5.4] - 2018-07-13
### Fixed

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.
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.
### 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.
```
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.
@ -36,4 +38,4 @@ Visit the [wiki page](https://docs.moodle.org/en/Custom_certificate_module "Wiki
## License
Licensed under the [GNU GPL License](http://www.gnu.org/copyleft/gpl.html).
Licensed under the [GNU GPL License](http://www.gnu.org/copyleft/gpl.html).

View file

@ -54,4 +54,4 @@ foreach ($values as $value) {
$element->posx = $value->posx;
$element->posy = $value->posy;
$DB->update_record('customcert_elements', $element);
}
}

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.
*
* @module mod_customcert/dialogue
* @package mod_customcert
* @copyright 2016 Mark Nelson <markn@moodle.com>
* @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.
*
* @module mod_customcert/rearrange-area
* @package mod_customcert
* @copyright 2016 Mark Nelson <markn@moodle.com>
* @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
* @return mixed|string
*/
static public function encode_content_links($content) {
public static function encode_content_links($content) {
global $CFG;
$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(
'pageid', 'name', 'element', 'data', 'font', 'fontsize',
'colour', 'posx', 'posy', 'width', 'refpoint', 'sequence',
'timecreated', 'timemodified'));
'alignment', 'timecreated', 'timemodified'));
// The 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.
*/
static public function define_decode_contents() {
public static function define_decode_contents() {
$contents = array();
$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.
*/
static public function define_decode_rules() {
public static function define_decode_rules() {
$rules = array();
$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
* customcert logs. It must return one array of {@link restore_log_rule} objects.
* 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 {@see restore_log_rule} objects.
*
* @return array the restore log rules
*/
static public function define_restore_log_rules() {
public static function define_restore_log_rules() {
$rules = array();
$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->timecreated = $this->apply_date_offset($data->timecreated);
$data->userid = $this->get_mappingid('user', $data->userid);
$newitemid = $DB->insert_record('customcert_issues', $data);
$this->set_mapping('customcert_issue', $oldid, $newitemid);

View file

@ -80,4 +80,4 @@ class admin_setting_link extends \admin_setting_configtext {
return format_admin_setting($this, $this->visiblename,
\html_writer::link($this->link, $this->linkname), $this->description, true, '', null, $query);
}
}
}

View file

@ -37,6 +37,16 @@ defined('MOODLE_INTERNAL') || die();
*/
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
*/
@ -165,7 +175,7 @@ class certificate {
* @param int $userid
* @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;
if (empty($userid)) {
@ -217,7 +227,7 @@ class certificate {
$totaltime = 0;
}
$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 timeout Register session value so that we have found a session!
$login = $log->$timefield;
@ -260,12 +270,20 @@ class certificate {
$allparams = $conditionsparams + array('customcertid' => $customcertid);
// Return the issues.
$extrafields = get_extra_user_fields(\context_module::instance($cm->id));
$ufields = \user_picture::fields('u', $extrafields);
$sql = "SELECT $ufields, ci.id as issueid, ci.code, ci.timecreated
$context = \context_module::instance($cm->id);
$extrafields = \core_user\fields::for_identity($context)->get_required_fields();
$ufields = \core_user\fields::for_userpic()->including(...$extrafields);
[
'selects' => $userfieldsselects,
'joins' => $userfieldsjoin,
'params' => $userfieldsparams
] = (array) $ufields->get_sql('u', true);
$allparams = array_merge($allparams, $userfieldsparams);
$sql = "SELECT ci.id as issueid, ci.code, ci.timecreated $userfieldsselects
FROM {user} u
INNER JOIN {customcert_issues} ci
ON u.id = ci.userid
INNER JOIN {customcert_issues} ci ON u.id = ci.userid
$userfieldsjoin
WHERE u.deleted = 0
AND ci.customcertid = :customcertid
$conditionssql";

View file

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

View file

@ -247,7 +247,7 @@ class edit_form extends \moodleform {
// Create a table to display these elements.
$table = new \html_table();
$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');
// Loop through and add the elements to the table.
foreach ($elements as $element) {

View file

@ -37,6 +37,21 @@ defined('MOODLE_INTERNAL') || die();
*/
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.
*/
@ -97,11 +112,21 @@ abstract class element {
*/
protected $refpoint;
/**
* @var string The alignment.
*/
protected $alignment;
/**
* @var bool $showposxy Show position XY form elements?
*/
protected $showposxy;
/**
* @var edit_element_form Element edit form instance.
*/
private $editelementform;
/**
* Constructor.
*
@ -125,6 +150,7 @@ abstract class element {
$this->width = $element->width;
$this->refpoint = $element->refpoint;
$this->showposxy = isset($showposxy) && $showposxy;
$this->set_alignment($element->alignment ?? self::ALIGN_LEFT);
}
/**
@ -226,11 +252,36 @@ abstract class element {
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.
* 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) {
// Render the common elements.
@ -240,6 +291,8 @@ abstract class element {
element_helper::render_form_element_position($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,
'posy' => $this->posy,
'width' => $this->width,
'refpoint' => $this->refpoint
'refpoint' => $this->refpoint,
'alignment' => $this->get_alignment()
];
foreach ($properties as $property => $value) {
if (!is_null($value) && $mform->elementExists($property)) {
@ -305,15 +359,16 @@ abstract class element {
$element = new \stdClass();
$element->name = $data->name;
$element->data = $this->save_unique_data($data);
$element->font = (isset($data->font)) ? $data->font : null;
$element->fontsize = (isset($data->fontsize)) ? $data->fontsize : null;
$element->colour = (isset($data->colour)) ? $data->colour : null;
$element->font = $data->font ?? null;
$element->fontsize = $data->fontsize ?? null;
$element->colour = $data->colour ?? null;
if ($this->showposxy) {
$element->posx = (isset($data->posx)) ? $data->posx : null;
$element->posy = (isset($data->posy)) ? $data->posy : null;
$element->posx = $data->posx ?? null;
$element->posy = $data->posy ?? null;
}
$element->width = (isset($data->width)) ? $data->width : null;
$element->refpoint = (isset($data->refpoint)) ? $data->refpoint : null;
$element->width = $data->width ?? null;
$element->refpoint = $data->refpoint ?? null;
$element->alignment = $data->alignment ?? self::ALIGN_LEFT;
$element->timemodified = time();
// 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 \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.
@ -383,7 +438,7 @@ abstract class element {
*
* @return string the html
*/
public abstract function render_html();
abstract public function render_html();
/**
* Handles deleting any data this element may have introduced.
@ -421,4 +476,27 @@ abstract class element {
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';
$data = new \stdClass();
$data->id = isset($element->id) ? $element->id : null;
$data->pageid = isset($element->pageid) ? $element->pageid : null;
$data->name = isset($element->name) ? $element->name : get_string('pluginname', 'customcertelement_' . $element->element);
$data->id = $element->id ?? null;
$data->pageid = $element->pageid ?? null;
$data->name = $element->name ?? get_string('pluginname', 'customcertelement_' . $element->element);
$data->element = $element->element;
$data->data = isset($element->data) ? $element->data : null;
$data->font = isset($element->font) ? $element->font : null;
$data->fontsize = isset($element->fontsize) ? $element->fontsize : null;
$data->colour = isset($element->colour) ? $element->colour : null;
$data->posx = isset($element->posx) ? $element->posx : null;
$data->posy = isset($element->posy) ? $element->posy : null;
$data->width = isset($element->width) ? $element->width : null;
$data->refpoint = isset($element->refpoint) ? $element->refpoint : null;
$data->data = $element->data ?? null;
$data->font = $element->font ?? null;
$data->fontsize = $element->fontsize ?? null;
$data->colour = $element->colour ?? null;
$data->posx = $element->posx ?? null;
$data->posy = $element->posy ?? null;
$data->width = $element->width ?? null;
$data->refpoint = $element->refpoint ?? null;
$data->alignment = $element->alignment ?? null;
// Ensure the necessary class exists.
if (class_exists($classname)) {

View file

@ -74,6 +74,7 @@ class element_helper {
$w = $element->get_width();
$refpoint = $element->get_refpoint();
$actualwidth = $pdf->GetStringWidth($content);
$alignment = $element->get_alignment();
if ($w and $w < $actualwidth) {
$actualwidth = $w;
@ -104,7 +105,7 @@ class element_helper {
$w += 0.0001;
}
$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.
*
* @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) {
$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.
*
* @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) {
$mform->addElement('customcert_colourpicker', 'colour', get_string('fontcolour', 'customcert'));
@ -163,7 +164,7 @@ class element_helper {
/**
* 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) {
$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.
*
* @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) {
$mform->addElement('text', 'width', get_string('elementwidth', 'customcert'), array('size' => 10));
$mform->setType('width', PARAM_INT);
$mform->setDefault('width', 0);
$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[self::CUSTOMCERT_REF_POINT_TOPLEFT] = get_string('topleft', 'customcert');
$refpointoptions[self::CUSTOMCERT_REF_POINT_TOPCENTER] = get_string('topcenter', 'customcert');
$refpointoptions[self::CUSTOMCERT_REF_POINT_TOPRIGHT] = get_string('topright', 'customcert');
$mform->addElement('select', 'refpoint', get_string('refpoint', 'customcert'), $refpointoptions);
$mform->setType('refpoint', PARAM_INT);
$mform->setDefault('refpoint', self::CUSTOMCERT_REF_POINT_TOPCENTER);
$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.
*
@ -383,6 +410,27 @@ class element_helper {
}
}
/**
* Helper function that returns the context for this element.
*
* @param int $elementid The element id
* @return \context The context
*/
public static function get_context(int $elementid) : \context {
global $DB;
$sql = "SELECT ct.contextid
FROM {customcert_templates} ct
INNER JOIN {customcert_pages} cp
ON ct.id = cp.templateid
INNER JOIN {customcert_elements} ce
ON cp.id = ce.pageid
WHERE ce.id = :elementid";
$contextid = $DB->get_field_sql($sql, array('elementid' => $elementid), MUST_EXIST);
return \context::instance_by_id($contextid);
}
/**
* Return the list of possible elements to add.
*
@ -432,70 +480,40 @@ class element_helper {
* @return array the array of gradeable items in the course
*/
public static function get_grade_items($course) {
global $DB;
// Array to store the grade items.
$modules = 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;
}
}
}
}
}
$arrgradeitems = array();
// Get other non-module related grade items.
if ($gradeitems = \grade_item::fetch_all(['courseid' => $course->id])) {
$arrgradeitems = [];
foreach ($gradeitems as $gi) {
// Skip the course and mod items since we already have them.
if ($gi->itemtype == 'mod' || $gi->itemtype == 'course') {
continue;
if ($gi->is_course_item()) {
continue; // Skipping for legacy reasons - this was added to individual elements.
}
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.
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';
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.
$context = $this->_customdata['context'];
$syscontext = \context_system::instance();
$mform->addElement('header', 'loadtemplateheader', get_string('loadtemplate', 'customcert'));
// 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'),
get_string('managetemplates', 'customcert'));
$mform->addElement('static', 'managetemplates', '', $link);
}
$templates = $DB->get_records_menu('customcert_templates',
array('contextid' => \context_system::instance()->id), 'name ASC', 'id, name');
if ($templates) {
$arrtemplates = $DB->get_records_menu('customcert_templates', ['contextid' => $syscontext->id], 'name ASC', 'id, name');
if ($arrtemplates) {
$templates = [];
foreach ($arrtemplates as $key => $template) {
$templates[$key] = format_string($template, true, ['context' => $context]);
}
$group = array();
$group[] = $mform->createElement('select', 'ltid', '', $templates);
$group[] = $mform->createElement('submit', 'loadtemplatesubmit', get_string('load', 'customcert'));

View file

@ -77,7 +77,7 @@ class manage_templates_table extends \table_sql {
* @return string
*/
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;
$args = (object) $args;
$versionname = $args->appversioncode >= 3950 ? 'latest' : 'ionic3';
$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.
$cm = get_coursemodule_from_id('customcert', $cmid);
@ -114,6 +115,7 @@ class mobile {
'showreport' => $showreport,
'hasrecipients' => !empty($recipients),
'recipients' => array_values($recipients),
'numrecipients' => count($recipients),
'currenttimestamp' => time()
];
@ -121,11 +123,13 @@ class mobile {
'templates' => [
[
'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' => '',
'otherdata' => ''
'otherdata' => [
'group' => $groupid,
]
];
}

View file

@ -42,7 +42,8 @@ defined('MOODLE_INTERNAL') || die();
*/
class provider implements
\core_privacy\local\metadata\provider,
\core_privacy\local\request\plugin\provider {
\core_privacy\local\request\plugin\provider,
\core_privacy\local\request\core_userlist_provider {
/**
* Return the fields which contain personal data.

View file

@ -66,7 +66,7 @@ class report_table extends \table_sql {
parent::__construct('mod_customcert_report_table');
$context = \context_module::instance($cm->id);
$extrafields = get_extra_user_fields($context);
$extrafields = \core_user\fields::for_identity($context)->get_required_fields();
$columns = [];
$columns[] = 'fullname';
@ -74,13 +74,15 @@ class report_table extends \table_sql {
$columns[] = $extrafield;
}
$columns[] = 'timecreated';
$columns[] = 'code';
$headers = [];
$headers[] = get_string('fullname');
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('code', 'customcert');
// Check if we were passed a filename, which means we want to download it.
if ($download) {
@ -101,6 +103,7 @@ class report_table extends \table_sql {
$this->define_headers($headers);
$this->collapsible(false);
$this->sortable(true);
$this->no_sorting('code');
$this->no_sorting('download');
$this->is_downloadable(true);
@ -135,6 +138,16 @@ class report_table extends \table_sql {
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.
*

View file

@ -23,6 +23,8 @@
*/
namespace mod_customcert\task;
use mod_customcert\helper;
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');
$textrenderer = $PAGE->get_renderer('mod_customcert', 'email', 'textemail');
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.
$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.
// Look through the teachers.
if ($teachers = get_enrolled_users($context, 'moodle/course:update')) {
$teachers = sort_by_roleassignment_authority($teachers, $context);
$userfrom = reset($teachers);
} else { // Ok, no teachers, use administrator name.
$userfrom = get_admin();
}
$userfrom = \core_user::get_noreply_user();
// Store teachers for later.
$teachers = get_enrolled_users($context, 'moodle/course:update');
$courseshortname = format_string($customcert->courseshortname, 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;
// 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
FROM {customcert_issues} ci
JOIN {user} u
@ -120,6 +134,11 @@ class email_certificate_task extends \core\task\scheduled_task {
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.
if (!empty($customcert->requiredtime)) {
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.
$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)) {
// Ok, issue them the certificate.
$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.
foreach ($issuedusers as $user) {
// Set up the user.
cron_setup_user($user);
$userfullname = fullname($user);
$info->userfullname = $userfullname;
// Now, get the PDF.
$template = new \stdClass();
@ -190,7 +213,8 @@ class email_certificate_task extends \core\task\scheduled_task {
$subject = get_string('emailstudentsubject', 'customcert', $info);
$message = $textrenderer->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) {
@ -201,8 +225,8 @@ class email_certificate_task extends \core\task\scheduled_task {
$message = $textrenderer->render($renderable);
$messagehtml = $htmlrenderer->render($renderable);
foreach ($teachers as $teacher) {
email_to_user($teacher, fullname($userfrom), $subject, $message, $messagehtml, $tempfile,
$filename);
email_to_user($teacher, fullname($userfrom), html_entity_decode($subject), $message, $messagehtml,
$tempfile, $filename);
}
}
@ -221,8 +245,8 @@ class email_certificate_task extends \core\task\scheduled_task {
$emailuser = new \stdClass();
$emailuser->id = -1;
$emailuser->email = $email;
email_to_user($emailuser, fullname($userfrom), $subject, $message, $messagehtml, $tempfile,
$filename);
email_to_user($emailuser, fullname($userfrom), html_entity_decode($subject), $message,
$messagehtml, $tempfile, $filename);
}
}
}

View file

@ -18,13 +18,19 @@
* Class represents a customcert template.
*
* @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
*/
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.
@ -50,6 +56,8 @@ class template {
*/
protected $contextid;
protected $html;
/**
* The constructor.
*
@ -59,6 +67,7 @@ class template {
$this->id = $template->id;
$this->name = $template->name;
$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?
* @return string|void Can return the PDF in string format if specified.
*/
public function generate_pdf($preview = false, $userid = null, $return = false) {
global $CFG, $DB, $USER;
public function generate_pdf(bool $preview = false, int $userid = null, bool $return = false) {
global $CFG, $DB, $USER, $SITE;
if (empty($userid)) {
$user = $USER;
@ -264,25 +273,44 @@ class template {
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.
if ($pages = $DB->get_records('customcert_pages', array('templateid' => $this->id), 'sequence ASC')) {
// Create the pdf object.
$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 ($protection = $DB->get_field('customcert', 'protection', array('templateid' => $this->id))) {
if (!empty($protection)) {
$protection = explode(', ', $protection);
$pdf->SetProtection($protection);
}
if (!empty($customcert->protection)) {
$protection = explode(', ', $customcert->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->setPrintFooter(false);
$pdf->SetTitle($this->name);
$pdf->SetTitle($filename);
$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');
// Loop through the pages and display their content.
foreach ($pages as $page) {
@ -293,22 +321,91 @@ class template {
$orientation = 'P';
}
$pdf->AddPage($orientation, array($page->width, $page->height));
$pdf->SetMargins($page->leftmargin, 0, $page->rightmargin);
// Get the elements for the page.
if ($elements = $DB->get_records('customcert_elements', array('pageid' => $page->id), 'sequence ASC')) {
// Loop through and display.
foreach ($elements as $element) {
// Get an instance of the element class.
if ($e = \mod_customcert\element_factory::get_element_instance($element)) {
$e->render($pdf, $preview, $user);
}
}
}
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);
// Get the elements for the page.
if ($elements = $DB->get_records('customcert_elements', array('pageid' => $page->id), 'sequence ASC')) {
// Loop through and display.
foreach ($elements as $element) {
// Get an instance of the element class.
if ($e = \mod_customcert\element_factory::get_element_instance($element)) {
$e->render($pdf, $preview, $user);
}
}
}
}
}
if ($return) {
if ($return && !$this->html) {
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) {
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.
if ($templatepages = $DB->get_records('customcert_pages', array('templateid' => $this->id))) {
// Loop through the pages.
@ -412,6 +513,10 @@ class template {
return $this->name;
}
public function get_html() {
return $this->html;
}
/**
* Returns the context id.
*

View file

@ -1,8 +1,9 @@
{
"name": "markn86/moodle-mod_customcert",
"name": "mdjnelson/moodle-mod_customcert",
"type": "moodle-mod",
"require": {
"composer/installers": "~1.0"
"composer/installers": "~1.0",
"mikehaertl/phpwkhtmltopdf": "^2.5"
},
"extra": {
"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(
'captype' => 'read',

View file

@ -1,5 +1,5 @@
<?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"
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="templateid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" 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="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="deliveryoption" TYPE="char" LENGTH="255" NOTNULL="false" 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="emailothers" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
@ -23,7 +24,7 @@
</FIELDS>
<KEYS>
<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>
</TABLE>
<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="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="html" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Primary key for customcert_template"/>
@ -67,7 +69,7 @@
</FIELDS>
<KEYS>
<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>
</TABLE>
<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="name" 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="fontsize" TYPE="int" LENGTH="10" 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="width" TYPE="int" LENGTH="10" 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="timecreated" 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.
'displaydata' => [
'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).
'method' => 'mobile_view_activity', // Main function in \mod_customcert\output\mobile.

5
db/subplugins.json Normal file
View file

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

View file

@ -146,5 +146,58 @@ function xmldb_customcert_upgrade($oldversion) {
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;
}

View file

@ -38,7 +38,7 @@ class element extends \customcertelement_image\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) {
$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.');
$plugin->version = 2018051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2018051700; // Requires this Moodle version (3.5).
$plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$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.
*
* @param \mod_customcert\edit_element_form $mform the edit_form instance
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function render_form_elements($mform) {
// 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.
*
* @param \mod_customcert\edit_element_form $mform the edit_form instance
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function definition_after_data($mform) {
if (!empty($this->get_data())) {

View file

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

View file

@ -43,7 +43,7 @@ class element extends \mod_customcert\element {
* @param \stdClass $user the user we are rendering this for
*/
public function render($pdf, $preview, $user) {
\mod_customcert\element_helper::render_content($pdf, $this, self::get_category_name($this->get_id()));
\mod_customcert\element_helper::render_content($pdf, $this, $this->get_category_name());
}
/**
@ -55,30 +55,28 @@ class element extends \mod_customcert\element {
* @return string the html
*/
public function render_html() {
global $COURSE;
$categoryname = format_string($COURSE->fullname, true, ['context' => \context_course::instance($COURSE->id)]);
return \mod_customcert\element_helper::render_html_content($this, $categoryname);
return \mod_customcert\element_helper::render_html_content($this, $this->get_category_name());
}
/**
* Helper function that returns the category name.
*
* @param int $elementid
* @return string
*/
protected static function get_category_name($elementid) {
protected function get_category_name() : string {
global $DB, $SITE;
$courseid = \mod_customcert\element_helper::get_courseid($elementid);
$courseid = \mod_customcert\element_helper::get_courseid($this->get_id());
$course = get_course($courseid);
$context = \mod_customcert\element_helper::get_context($this->get_id());
// Check that there is a course category available.
if (!empty($course->category)) {
$categoryname = $DB->get_field('course_categories', 'name', array('id' => $course->category), MUST_EXIST);
return format_string($categoryname, true, ['context' => \context_course::instance($courseid)]);
} else { // Must be in a site template.
return format_string($SITE->fullname, true, ['context' => \context_system::instance()]);
$categoryname = $SITE->fullname;
}
return format_string($categoryname, true, ['context' => $context]);
}
}

View file

@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
$plugin->version = 2018051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2018051700; // Requires this Moodle version (3.5).
$plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$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);
// Now we can get the issue for this user.
$issue = $DB->get_record('customcert_issues', array('userid' => $user->id, 'customcertid' => $customcert->id),
'*', MUST_EXIST);
'*', IGNORE_MULTIPLE);
$code = $issue->code;
}

View file

@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
$plugin->version = 2018051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2018051700; // Requires this Moodle version (3.5).
$plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$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 {
/**
* 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.
*
@ -43,11 +79,7 @@ class element extends \mod_customcert\element {
* @param \stdClass $user the user we are rendering this for
*/
public function render($pdf, $preview, $user) {
$courseid = \mod_customcert\element_helper::get_courseid($this->get_id());
$course = get_course($courseid);
$coursename = format_string($course->fullname, true, ['context' => \context_course::instance($courseid)]);
\mod_customcert\element_helper::render_content($pdf, $this, $coursename);
\mod_customcert\element_helper::render_content($pdf, $this, $this->get_course_name_detail());
}
/**
@ -59,9 +91,52 @@ class element extends \mod_customcert\element {
* @return string the html
*/
public function render_html() {
global $COURSE;
return \mod_customcert\element_helper::render_html_content($this, $this->get_course_name_detail());
}
$coursename = format_string($COURSE->fullname, true, ['context' => \context_course::instance($COURSE->id)]);
return \mod_customcert\element_helper::render_html_content($this, $coursename);
/**
* 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
*/
protected function get_course_name_detail(): string {
$courseid = \mod_customcert\element_helper::get_courseid($this->get_id());
$course = get_course($courseid);
$context = \mod_customcert\element_helper::get_context($this->get_id());
// 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
*/
$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['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.');
$plugin->version = 2018051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2018051700; // Requires this Moodle version (3.5).
$plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_coursename';

View file

@ -51,6 +51,21 @@ define('CUSTOMCERT_DATE_COURSE_START', '-3');
*/
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');
/**
@ -65,7 +80,7 @@ class element extends \mod_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) {
global $CFG, $COURSE;
@ -73,10 +88,14 @@ class element extends \mod_customcert\element {
// Get the possible date options.
$dateoptions = array();
$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);
if ($completionenabled) {
$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_END] = get_string('courseenddate', '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);
// Now we can get the issue for this user.
$issue = $DB->get_record('customcert_issues', array('userid' => $user->id, 'customcertid' => $customcert->id),
'*', MUST_EXIST);
'*', IGNORE_MULTIPLE);
if ($dateitem == CUSTOMCERT_DATE_ISSUE) {
$date = $issue->timecreated;
} else if ($dateitem == CUSTOMCERT_DATE_CURRENT_DATE) {
$date = time();
} else if ($dateitem == CUSTOMCERT_DATE_COMPLETION) {
// Get the last completion date.
$sql = "SELECT MAX(c.timecompleted) as timecompleted
@ -156,6 +177,26 @@ class element extends \mod_customcert\element {
$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) {
$date = $DB->get_field('course', 'startdate', array('id' => $courseid));
} else if ($dateitem == CUSTOMCERT_DATE_COURSE_END) {
@ -218,7 +259,7 @@ class element extends \mod_customcert\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) {
// Set the item and format for this element.

View file

@ -26,6 +26,9 @@ $string['completiondate'] = 'Completion date';
$string['courseenddate'] = 'Course end date';
$string['coursegradedate'] = 'Course grade 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_help'] = 'This is the format of the date that will be displayed';
$string['dateitem'] = 'Date item';

View file

@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
$plugin->version = 2018051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2018051700; // Requires this Moodle version (3.5).
$plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$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.
*
* @param \mod_customcert\edit_element_form $mform the edit_form instance
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function render_form_elements($mform) {
$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.
*
* @param \mod_customcert\edit_element_form $mform the edit_form instance
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function definition_after_data($mform) {
global $COURSE, $SITE;

View file

@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
$plugin->version = 2018051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2018051700; // Requires this Moodle version (3.5).
$plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$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.
*
* @param \mod_customcert\edit_element_form $mform the edit_form instance
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function render_form_elements($mform) {
global $COURSE;
@ -107,7 +107,7 @@ class element extends \mod_customcert\element {
// If we are previewing this certificate then just show a demonstration grade.
if ($preview) {
$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);;
} else {
if ($gradeitem == CUSTOMCERT_GRADE_COURSE) {
$grade = \mod_customcert\element_helper::get_course_grade_info(
@ -167,7 +167,7 @@ class element extends \mod_customcert\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) {
// 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.');
$plugin->version = 2018051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2018051700; // Requires this Moodle version (3.5).
$plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$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.
*
* @param \mod_customcert\edit_element_form $mform the edit_form instance
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function render_form_elements($mform) {
global $COURSE;
@ -73,20 +73,9 @@ class element extends \mod_customcert\element {
* @param \stdClass $user the user we are rendering this for
*/
public function render($pdf, $preview, $user) {
global $DB;
// Check that the grade item is not empty.
if (!empty($this->get_data())) {
// Get the course module information.
$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.
$courseid = \mod_customcert\element_helper::get_courseid($this->get_data());
$itemname = $DB->get_field($module->name, 'name', array('id' => $cm->instance), MUST_EXIST);
$itemname = format_string($itemname, true, ['context' => \context_course::instance($courseid)]);
\mod_customcert\element_helper::render_content($pdf, $this, $itemname);
\mod_customcert\element_helper::render_content($pdf, $this, $this->get_grade_item_name());
}
}
@ -99,20 +88,9 @@ class element extends \mod_customcert\element {
* @return string the html
*/
public function render_html() {
global $DB;
// Check that the grade item is not empty.
if (!empty($this->get_data())) {
// Get the course module information.
$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.
$courseid = \mod_customcert\element_helper::get_courseid($this->get_data());
$itemname = $DB->get_field($module->name, 'name', array('id' => $cm->instance), MUST_EXIST);
$itemname = format_string($itemname, true, ['context' => \context_course::instance($courseid)]);
return \mod_customcert\element_helper::render_html_content($this, $itemname);
return \mod_customcert\element_helper::render_html_content($this, $this->get_grade_item_name());
}
return '';
@ -121,7 +99,7 @@ class element extends \mod_customcert\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) {
if (!empty($this->get_data())) {
@ -130,4 +108,42 @@ class element extends \mod_customcert\element {
}
parent::definition_after_data($mform);
}
/**
* Helper function that returns the grade item name.
*
* @return string
*/
protected function get_grade_item_name() : string {
global $DB;
$gradeitem = $this->get_data();
if (strpos($gradeitem, 'gradeitem:') === 0) {
$gradeitemid = substr($gradeitem, 10);
$gradeitem = \grade_item::fetch(['id' => $gradeitemid]);
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.');
$plugin->version = 2018051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2018051700; // Requires this Moodle version (3.5).
$plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$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.
*
* @param \mod_customcert\edit_element_form $mform the edit_form instance
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function render_form_elements($mform) {
$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.
*
* @param \mod_customcert\edit_element_form $mform the edit_form instance
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function definition_after_data($mform) {
global $COURSE, $SITE;
@ -382,14 +382,14 @@ class element extends \mod_customcert\element {
// 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)) {
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.
if ($files = $fs->get_area_files(\context_course::instance($COURSE->id)->id, 'mod_customcert', 'image', false,
'filename', false)) {
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;
}
/**
* 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_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_help'] = 'Height of the image in mm. If equal to zero, it is automatically calculated.';
$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['pluginname'] = 'Image';
$string['privacy:metadata'] = 'The Image plugin does not store any personal data.';
$string['systemimage'] = 'Site image: {$a}';
$string['width'] = 'Width';
$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.');
$plugin->version = 2018051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2018051700; // Requires this Moodle version (3.5).
$plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$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/>.
/**
* Definition of sub-plugins.
* Strings for component 'customcertelement_qrcode', language 'en'.
*
* @package mod_customcert
* @copyright 2013 Mark Nelson <markn@moodle.com>
* @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();
$subplugins = array('customcertelement' => 'mod/customcert/element');
$string['height'] = 'Height';
$string['height_help'] = 'Height help';
$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.');
$plugin->version = 2018051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2018051700; // Requires this Moodle version (3.5).
$plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$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.
*
* @param \mod_customcert\edit_element_form $mform the edit_form instance
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function render_form_elements($mform) {
$mform->addElement('select', 'teacher', get_string('teacher', 'customcertelement_teachername'),
@ -102,6 +102,11 @@ class element extends \mod_customcert\element {
protected function get_list_of_teachers() {
global $PAGE;
// Return early if we are in a site template.
if ($PAGE->context->id == \context_system::instance()->id) {
return [];
}
// The list of teachers to return.
$teachers = array();
@ -118,7 +123,7 @@ class element extends \mod_customcert\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) {
if (!empty($this->get_data())) {

View file

@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
$plugin->version = 2018051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2018051700; // Requires this Moodle version (3.5).
$plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$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.
*
* @param \mod_customcert\edit_element_form $mform the edit_form instance
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function render_form_elements($mform) {
$mform->addElement('textarea', 'text', get_string('text', 'customcertelement_text'));
@ -67,9 +67,7 @@ class element extends \mod_customcert\element {
* @param \stdClass $user the user we are rendering this for
*/
public function render($pdf, $preview, $user) {
$courseid = \mod_customcert\element_helper::get_courseid($this->get_id());
$text = format_text($this->get_data(), FORMAT_HTML, ['context' => \context_course::instance($courseid)]);
\mod_customcert\element_helper::render_content($pdf, $this, $text);
\mod_customcert\element_helper::render_content($pdf, $this, $this->get_text());
}
/**
@ -81,15 +79,13 @@ class element extends \mod_customcert\element {
* @return string the html
*/
public function render_html() {
$courseid = \mod_customcert\element_helper::get_courseid($this->get_id());
$text = format_text($this->get_data(), FORMAT_HTML, ['context' => \context_course::instance($courseid)]);
return \mod_customcert\element_helper::render_html_content($this, $text);
return \mod_customcert\element_helper::render_html_content($this, $this->get_text());
}
/**
* 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) {
if (!empty($this->get_data())) {
@ -98,4 +94,14 @@ class element extends \mod_customcert\element {
}
parent::definition_after_data($mform);
}
/**
* Helper function that returns the text.
*
* @return string
*/
protected function get_text() : string {
$context = \mod_customcert\element_helper::get_context($this->get_id());
return format_text($this->get_data(), FORMAT_HTML, ['context' => $context]);
}
}

View file

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

View file

@ -24,6 +24,8 @@
namespace customcertelement_userfield;
use core_user\fields;
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.
*
* @param \mod_customcert\edit_element_form $mform the edit_form instance
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function render_form_elements($mform) {
// Get the user profile fields.
$userfields = array(
'firstname' => get_user_field_name('firstname'),
'lastname' => get_user_field_name('lastname'),
'email' => get_user_field_name('email'),
'city' => get_user_field_name('city'),
'country' => get_user_field_name('country'),
'url' => get_user_field_name('url'),
'icq' => get_user_field_name('icq'),
'skype' => get_user_field_name('skype'),
'aim' => get_user_field_name('aim'),
'yahoo' => get_user_field_name('yahoo'),
'msn' => get_user_field_name('msn'),
'idnumber' => get_user_field_name('idnumber'),
'institution' => get_user_field_name('institution'),
'department' => get_user_field_name('department'),
'phone1' => get_user_field_name('phone1'),
'phone2' => get_user_field_name('phone2'),
'address' => get_user_field_name('address')
'firstname' => fields::get_display_name('firstname'),
'lastname' => fields::get_display_name('lastname'),
'username' => fields::get_display_name('username'),
'email' => fields::get_display_name('email'),
'city' => fields::get_display_name('city'),
'country' => fields::get_display_name('country'),
'url' => fields::get_display_name('url'),
'skype' => fields::get_display_name('skype'),
'idnumber' => fields::get_display_name('idnumber'),
'institution' => fields::get_display_name('institution'),
'department' => fields::get_display_name('department'),
'phone1' => fields::get_display_name('phone1'),
'phone2' => fields::get_display_name('phone2'),
'address' => fields::get_display_name('address')
);
// Get the user custom fields.
$arrcustomfields = \availability_profile\condition::get_custom_profile_fields();
$customfields = array();
foreach ($arrcustomfields as $key => $customfield) {
$customfields[$customfield->id] = $key;
$customfields[$customfield->id] = $customfield->name;
}
// Combine the two.
$fields = $userfields + $customfields;
@ -98,14 +97,56 @@ class element extends \mod_customcert\element {
* @param \stdClass $user the user we are rendering this for
*/
public function render($pdf, $preview, $user) {
\mod_customcert\element_helper::render_content($pdf, $this, $this->get_user_field_value($user, $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 $USER;
return \mod_customcert\element_helper::render_html_content($this, $this->get_user_field_value($USER, 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('userfield');
$element->setValue($this->get_data());
}
parent::definition_after_data($mform);
}
/**
* Helper function that returns the text.
*
* @param \stdClass $user the user we are rendering this for
* @param bool $preview Is this a preview?
* @return string
*/
protected function get_user_field_value(\stdClass $user, bool $preview) : string {
global $CFG, $DB;
// The user field to display.
$field = $this->get_data();
// The value to display on the PDF.
$value = '';
// 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 user profile field.
if ($field = $DB->get_record('user_info_field', array('id' => $field))) {
// Found the field name, let's update the value to display.
$value = $field->name;
$file = $CFG->dirroot . '/user/profile/field/' . $field->datatype . '/field.class.php';
if (file_exists($file)) {
require_once($CFG->dirroot . '/user/profile/lib.php');
@ -119,59 +160,7 @@ class element extends \mod_customcert\element {
$value = $user->$field;
}
$courseid = \mod_customcert\element_helper::get_courseid($this->get_id());
$value = format_string($value, true, ['context' => \context_course::instance($courseid)]);
\mod_customcert\element_helper::render_content($pdf, $this, $value);
}
/**
* 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 $CFG, $DB, $USER;
// 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.
$value = $field;
if (is_number($field)) { // Must be a custom user profile field.
if ($field = $DB->get_record('user_info_field', array('id' => $field))) {
// Found the field name, let's update the value to display.
$value = $field->name;
$file = $CFG->dirroot . '/user/profile/field/' . $field->datatype . '/field.class.php';
if (file_exists($file)) {
require_once($CFG->dirroot . '/user/profile/lib.php');
require_once($file);
$class = "profile_field_{$field->datatype}";
$field = new $class($field->id, $USER->id);
if ($fieldvalue = $field->display_data()) {
// Ok, found a value for the user, let's show that instead.
$value = $fieldvalue;
}
}
}
} else if (!empty($USER->$field)) { // Field in the user table.
$value = $USER->$field;
}
$courseid = \mod_customcert\element_helper::get_courseid($this->get_id());
$value = format_string($value, true, ['context' => \context_course::instance($courseid)]);
return \mod_customcert\element_helper::render_html_content($this, $value);
}
/**
* Sets the data on the form when editing an element.
*
* @param \mod_customcert\edit_element_form $mform the edit_form instance
*/
public function definition_after_data($mform) {
if (!empty($this->get_data())) {
$element = $mform->getElement('userfield');
$element->setValue($this->get_data());
}
parent::definition_after_data($mform);
$context = \mod_customcert\element_helper::get_context($this->get_id());
return format_string($value, true, ['context' => $context]);
}
}

View file

@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
$plugin->version = 2018051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2018051700; // Requires this Moodle version (3.5).
$plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$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.
*
* @param \mod_customcert\edit_element_form $mform the edit_form instance
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function render_form_elements($mform) {
$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.
*
* @param \mod_customcert\edit_element_form $mform the edit_form instance
* @param \MoodleQuickForm $mform the edit_form instance
*/
public function definition_after_data($mform) {
// 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.');
$plugin->version = 2018051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2018051700; // Requires this Moodle version (3.5).
$plugin->version = 2021051700; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2021051700; // Requires this Moodle version (3.11).
$plugin->component = 'customcertelement_userpicture';

View file

@ -22,8 +22,14 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['activity'] = 'Activity';
$string['addcertpage'] = 'Add page';
$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['cannotverifyallcertificates'] = 'You do not have the permission to verify all certificates on the site.';
$string['certificate'] = 'Certificate';
@ -41,6 +47,7 @@ $string['customcert:manageemailothers'] = 'Manage email others setting';
$string['customcert:manageverifyany'] = 'Manage verification setting';
$string['customcert:managerequiredtime'] = 'Manage time required setting';
$string['customcert:manageprotection'] = 'Manage protection setting';
$string['customcert:receiveissue'] = 'Receive a certificate';
$string['customcert:view'] = 'View a custom certificate';
$string['customcert:viewreport'] = 'View course report';
$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['deletepageconfirm'] = 'Are you sure you want to delete this certificate page?';
$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['duplicate'] = 'Duplicate';
$string['duplicateconfirm'] = 'Duplicate confirmation';
@ -82,11 +92,12 @@ $string['emailstudentcertificatelinktext'] = 'View certificate';
$string['emailstudentgreeting'] = 'Dear {$a}';
$string['emailstudentsubject'] = '{$a->coursefullname}: {$a->certificatename}';
$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_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_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['exampledatawarning'] = 'Some of these values may just be an example to ensure positioning of the elements is possible.';
$string['font'] = 'Font';
$string['font_help'] = 'The font used when generating this element.';
$string['fontcolour'] = 'Colour';
@ -94,6 +105,7 @@ $string['fontcolour_help'] = 'The colour of the font.';
$string['fontsize'] = 'Size';
$string['fontsize_help'] = 'The size of the font in points.';
$string['getcustomcert'] = 'View certificate';
$string['gradeoutcome'] = 'Outcome';
$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['invalidcode'] = 'Invalid code supplied.';
@ -106,7 +118,7 @@ $string['invalidwidth'] = 'The width has to be a valid number greater than 0.';
$string['landscape'] = 'Landscape';
$string['leftmargin'] = 'Left margin';
$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['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.';

View file

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

View file

@ -97,4 +97,4 @@ $PAGE->navbar->add(get_string('loadtemplate', 'customcert'));
echo $OUTPUT->header();
echo $OUTPUT->heading($heading);
echo $OUTPUT->confirm(get_string('loadtemplatemsg', 'customcert'), $yesurl, $nourl);
echo $OUTPUT->footer();
echo $OUTPUT->footer();

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);
$title = $SITE->fullname;
$heading = $title;
// Set up the page.
$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'));
}
$heading = format_string($title, true, ['context' => $context]);
if ($tid) {
if ($action && confirm_sesskey()) {
$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($CFG->libdir . '/filelib.php');
require_once($CFG->libdir . '/completionlib.php');
require_once($CFG->dirroot . '/webservice/lib.php');
// Allow CORS requests.
@ -54,6 +55,7 @@ if (empty($enabledfiledownload)) {
}
$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);
$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);
// Set the custom certificate as viewed.
$completion = new completion_info($course);
$completion->set_module_viewed($cm);
}
// 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
*/
use mod_customcert\certificate;
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
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'));
$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())) {
$mform->addElement('selectyesno', 'emailstudents', get_string('emailstudents', 'customcert'));
$mform->setDefault('emailstudents', get_config('customcert', 'emailstudents'));
$mform->addHelpButton('emailstudents', 'emailstudents', 'customcert');
$mform->setType('emailstudents', PARAM_INT);
$firstoption = 'emailstudents';
}
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->addHelpButton('emailteachers', 'emailteachers', 'customcert');
$mform->setType('emailteachers', PARAM_INT);
$firstoption = empty($firstoption) ? 'emailteachers' : $firstoption;
}
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->setDefault('emailothers', get_config('customcert', 'emailothers'));
$mform->setType('emailothers', PARAM_TEXT);
$firstoption = empty($firstoption) ? 'emailothers' : $firstoption;
}
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->setDefault('verifyany', get_config('customcert', 'verifyany'));
$mform->setType('verifyany', PARAM_INT);
$firstoption = empty($firstoption) ? 'verifyany' : $firstoption;
}
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->setDefault('requiredtime', get_config('customcert', 'requiredtime'));
$mform->setType('requiredtime', PARAM_INT);
$firstoption = empty($firstoption) ? 'requiredtime' : $firstoption;
}
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_modify', 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();
@ -227,13 +226,13 @@ class mod_customcert_mod_form extends moodleform_mod {
$protection = explode(', ', $protection);
if (in_array(\mod_customcert\certificate::PROTECTION_PRINT, $protection)) {
if (in_array(certificate::PROTECTION_PRINT, $protection)) {
$data->protection_print = 1;
}
if (in_array(\mod_customcert\certificate::PROTECTION_MODIFY, $protection)) {
if (in_array(certificate::PROTECTION_MODIFY, $protection)) {
$data->protection_modify = 1;
}
if (in_array(\mod_customcert\certificate::PROTECTION_COPY, $protection)) {
if (in_array(certificate::PROTECTION_COPY, $protection)) {
$data->protection_copy = 1;
}

View file

@ -30,6 +30,11 @@ $downloadcert = optional_param('downloadcert', '', PARAM_BOOL);
if ($downloadcert) {
$certificateid = required_param('certificateid', PARAM_INT);
$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);
$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 (($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.
if ($downloadcert) {
$template = $DB->get_record('customcert_templates', array('id' => $customcert->templateid), '*', MUST_EXIST);
@ -63,12 +74,6 @@ if ($table->is_downloading()) {
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.
$PAGE->navbar->add(get_string('profile'), new moodle_url('/user/profile.php', array('id' => $userid)));
$PAGE->navbar->add(get_string('mycertificates', 'customcert'));

View file

@ -112,6 +112,18 @@ if ($elements) {
default:
$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,
'data-refpoint' => $element->refpoint, 'id' => 'element-' . $element->id));
}
@ -125,7 +137,8 @@ $html .= html_writer::end_tag('div');
echo $OUTPUT->header();
echo $OUTPUT->heading($heading);
echo $OUTPUT->heading(get_string('rearrangeelementsheading', 'customcert'), 4);
echo $OUTPUT->heading(get_string('rearrangeelementsheading', 'customcert'), 3);
echo $OUTPUT->notification(get_string('exampledatawarning', 'customcert'), \core\output\notification::NOTIFY_WARNING);
echo $html;
$PAGE->requires->js_call_amd('mod_customcert/rearrange-area', 'init', array('#pdf'));
echo $OUTPUT->footer();

View file

@ -57,6 +57,18 @@
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 {
border-style: solid;
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:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| manager1 | Manager | 1 | Manager1@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| manager1 | C1 | manager |
And the following "activities" exist:
| activity | name | intro | course | idnumber |
| 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 "Set protection"
Scenario: Create an activity as an Editing Teacher I can see all custom certificate options
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"
@javascript
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 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 "Set protection"
@javascript
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 add a "Custom certificate" to section "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_copy" matches value "0"
@javascript
Scenario: Add an activity using configured custom certificate options
And the following config values are set as admin:
| emailstudents | 1 | customcert |
@ -129,7 +96,7 @@ Feature: Being able to correctly display options on the certificate activity edi
| protection_print | 1 | customcert |
| protection_modify | 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 add a "Custom certificate" to section "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.
*
* @codingStandardsIgnoreLine
* phpcs:ignore
* @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 int $pagenum

View file

@ -79,8 +79,8 @@ Feature: Being able to manage elements in a certificate template
| Width | 20 |
| Reference point location | Top left |
And I press "Save changes"
# Course name.
And I add the element "Course name" to page "1" of the "Custom certificate 1" certificate template
# 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 |
@ -88,8 +88,8 @@ Feature: Being able to manage elements in a certificate template
| Width | 20 |
| Reference point location | Top left |
And I press "Save changes"
And I should see "Course name" in the "elementstable" "table"
And I click on ".edit-icon" "css_element" in the "Course name" "table_row"
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 |
@ -97,6 +97,26 @@ Feature: Being able to manage elements in a certificate template
| Width | 20 |
| Reference point location | Top left |
And I press "Save changes"
# Course name.
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:
| Font | Helvetica |
| Type | Short name |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
And I press "Save changes"
And I should see "Course name" in the "elementstable" "table"
And I click on ".edit-icon" "css_element" in the "Course name" "table_row"
And the following fields match these values:
| Font | Helvetica |
| Type | Short name |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
And I press "Save changes"
# Date.
And I add the element "Date" to page "1" of the "Custom certificate 1" certificate template
And I set the following fields to these values:
@ -119,6 +139,42 @@ Feature: Being able to manage elements in a certificate template
| Width | 20 |
| Reference point location | Top left |
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.
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:
@ -144,44 +200,44 @@ Feature: Being able to manage elements in a certificate template
# Grade.
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:
| Grade item | Topic 0 : Assignment 1 |
| Grade format | Percentage |
| Font | Helvetica |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
| Grade item | Activity : Assignment 1 |
| Grade format | Percentage |
| Font | Helvetica |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
And I press "Save changes"
And I should see "Grade" in the "elementstable" "table"
And I click on ".edit-icon" "css_element" in the "Grade" "table_row"
And the following fields match these values:
| Grade item | Topic 0 : Assignment 1 |
| Grade format | Percentage |
| Font | Helvetica |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
| Grade item | Activity : Assignment 1 |
| Grade format | Percentage |
| Font | Helvetica |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
And I press "Save changes"
# Grade item name.
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:
| Grade item | Topic 0 : Assignment 2 |
| Font | Helvetica |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
| Grade item | Activity : Assignment 2 |
| Font | Helvetica |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
And I press "Save changes"
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 the following fields match these values:
| Grade item | Topic 0 : Assignment 2 |
| Font | Helvetica |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
| Grade item | Activity : Assignment 2 |
| Font | Helvetica |
| Size | 20 |
| Colour | #045ECD |
| Width | 20 |
| Reference point location | Top left |
And I press "Save changes"
# Image.
And I add the element "Image" to page "1" of the "Custom certificate 1" certificate template
@ -287,6 +343,18 @@ Feature: Being able to manage elements in a certificate template
| Width | 10 |
| Height | 10 |
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.
And I follow "Reposition elements"
And I press "Save and close"

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