From b16bf5ccb6e170ac4dce745395ec8b1fc8bcabd4 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Thu, 3 Jan 2019 11:05:25 +0800 Subject: [PATCH] Added QR code element (#146) --- classes/element.php | 1 + classes/element_helper.php | 9 + element/qrcode/classes/element.php | 202 ++++++++++++++++++ element/qrcode/classes/privacy/provider.php | 46 ++++ .../lang/en/customcertelement_qrcode.php | 30 +++ element/qrcode/version.php | 29 +++ verify_certificate.php | 8 +- 7 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 element/qrcode/classes/element.php create mode 100644 element/qrcode/classes/privacy/provider.php create mode 100644 element/qrcode/lang/en/customcertelement_qrcode.php create mode 100644 element/qrcode/version.php diff --git a/classes/element.php b/classes/element.php index 0211dab..4d1c8e0 100644 --- a/classes/element.php +++ b/classes/element.php @@ -245,6 +245,7 @@ abstract class element { element_helper::render_form_element_position($mform); } element_helper::render_form_element_width($mform); + element_helper::render_form_element_refpoint($mform); } /** diff --git a/classes/element_helper.php b/classes/element_helper.php index e4b89c4..f8061c2 100644 --- a/classes/element_helper.php +++ b/classes/element_helper.php @@ -186,10 +186,19 @@ class element_helper { $mform->setType('width', PARAM_INT); $mform->setDefault('width', 0); $mform->addHelpButton('width', 'elementwidth', 'customcert'); + } + + /** + * Helper function to render the refpoint element. + * + * @param \mod_customcert\edit_element_form $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); diff --git a/element/qrcode/classes/element.php b/element/qrcode/classes/element.php new file mode 100644 index 0000000..eb036f2 --- /dev/null +++ b/element/qrcode/classes/element.php @@ -0,0 +1,202 @@ +. + +/** + * This file contains the customcert element QR code's core interaction API. + * + * @package customcertelement_qrcode + * @copyright 2019 Mark Nelson + * @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 + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class element extends \mod_customcert\element { + + + const BARCODETYPE = 'QRCODE'; + + /** + * This function renders the form elements when adding a customcert element. + * + * @param \mod_customcert\edit_element_form $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', 'customcertelement_qrcode'); + } + + 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 \mod_customcert\edit_element_form $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); + } +} diff --git a/element/qrcode/classes/privacy/provider.php b/element/qrcode/classes/privacy/provider.php new file mode 100644 index 0000000..e4a0ee1 --- /dev/null +++ b/element/qrcode/classes/privacy/provider.php @@ -0,0 +1,46 @@ +. + +/** + * Privacy Subsystem implementation for customcertelement_qrcode. + * + * @package customcertelement_qrcode + * @copyright 2019 Mark Nelson + * @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 + * @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'; + } +} diff --git a/element/qrcode/lang/en/customcertelement_qrcode.php b/element/qrcode/lang/en/customcertelement_qrcode.php new file mode 100644 index 0000000..ebca7b5 --- /dev/null +++ b/element/qrcode/lang/en/customcertelement_qrcode.php @@ -0,0 +1,30 @@ +. + +/** + * Strings for component 'customcertelement_qrcode', language 'en'. + * + * @package customcertelement_qrcode + * @copyright 2019 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$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'; diff --git a/element/qrcode/version.php b/element/qrcode/version.php new file mode 100644 index 0000000..b9c9a9f --- /dev/null +++ b/element/qrcode/version.php @@ -0,0 +1,29 @@ +. + +/** + * This file contains the version information for the QR code plugin. + * + * @package customcertelement_qrcode + * @copyright 2019 Mark Nelson + * @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 = 2018120300; // The current module version (Date: YYYYMMDDXX). +$plugin->requires = 2018120300; // Requires this Moodle version (3.6). +$plugin->component = 'customcertelement_qrcode'; diff --git a/verify_certificate.php b/verify_certificate.php index 6f4b426..9ed0018 100644 --- a/verify_certificate.php +++ b/verify_certificate.php @@ -28,6 +28,7 @@ require_once('../../config.php'); $contextid = optional_param('contextid', context_system::instance()->id, PARAM_INT); $code = optional_param('code', '', PARAM_ALPHANUM); // The code for the certificate we are verifying. +$qrcode = optional_param('qrcode', false, PARAM_BOOL); $context = context::instance_by_id($contextid); @@ -87,7 +88,7 @@ if ($checkallofsite) { // The form we are using to verify these codes. $form = new \mod_customcert\verify_certificate_form($pageurl); -if ($form->get_data()) { +if ($code) { $result = new stdClass(); $result->issues = array(); @@ -128,7 +129,10 @@ if ($form->get_data()) { echo $OUTPUT->header(); echo $OUTPUT->heading($heading); -echo $form->display(); +// Don't show the form if we are coming from a QR code. +if (!$qrcode) { + echo $form->display(); +} if (isset($result)) { $renderer = $PAGE->get_renderer('mod_customcert'); $result = new \mod_customcert\output\verify_certificate_results($result);