From 783e20ede0227c83a7e69b404f71d70ed8107709 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Thu, 25 Jan 2024 17:26:49 +0000 Subject: [PATCH] WIP (#591) --- classes/persistent/element.php | 105 ++++++ classes/persistent/page.php | 77 ++++ classes/persistent/template.php | 596 +++++++++++++++++++++++++++++++ classes/template.php | 597 -------------------------------- db/install.xml | 6 + db/upgrade.php | 35 ++ version.php | 2 +- 7 files changed, 820 insertions(+), 598 deletions(-) create mode 100644 classes/persistent/element.php create mode 100644 classes/persistent/page.php create mode 100644 classes/persistent/template.php diff --git a/classes/persistent/element.php b/classes/persistent/element.php new file mode 100644 index 00000000..fabc50fe --- /dev/null +++ b/classes/persistent/element.php @@ -0,0 +1,105 @@ +. + +/** + * Class represents a customcert element. + * + * @package mod_customcert + * @copyright 2024 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_customcert\persistent; + +use core\persistent; + +/** + * Class represents a customcert element. + * + * @package mod_customcert + * @copyright 2024 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class element extends persistent { + + /** @var string */ + const TABLE = 'tool_certificate_elements'; + + /** + * Return the definition of the properties of this model. + * + * @return array + */ + protected static function define_properties() { + return [ + 'pageid' => [ + 'type' => PARAM_INT, + ], + 'name' => [ + 'type' => PARAM_TEXT, + 'default' => '', + ], + 'element' => [ + 'type' => PARAM_ALPHANUMEXT, + ], + 'data' => [ + 'type' => PARAM_RAW, + 'null' => NULL_ALLOWED, + 'default' => null, + ], + 'font' => [ + 'type' => PARAM_NOTAGS, + 'null' => NULL_ALLOWED, + 'default' => 'freesans', + ], + 'fontsize' => [ + 'type' => PARAM_INT, + 'null' => NULL_ALLOWED, + 'default' => null, + ], + 'colour' => [ + 'type' => PARAM_NOTAGS, + 'null' => NULL_ALLOWED, + 'default' => null, + ], + 'posx' => [ + 'type' => PARAM_INT, + 'null' => NULL_ALLOWED, + 'default' => null, + ], + 'posy' => [ + 'type' => PARAM_INT, + 'null' => NULL_ALLOWED, + 'default' => null, + ], + 'width' => [ + 'type' => PARAM_INT, + 'null' => NULL_ALLOWED, + 'default' => null, + ], + 'refpoint' => [ + 'type' => PARAM_INT, + 'null' => NULL_ALLOWED, + 'default' => null, + ], + 'sequence' => [ + 'type' => PARAM_INT, + 'null' => NULL_ALLOWED, + 'default' => null, + ], + ]; + } +} \ No newline at end of file diff --git a/classes/persistent/page.php b/classes/persistent/page.php new file mode 100644 index 00000000..f680078b --- /dev/null +++ b/classes/persistent/page.php @@ -0,0 +1,77 @@ +. + +/** + * Class represents a customcert page. + * + * @package mod_customcert + * @copyright 2024 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_customcert\persistent; + +use core\persistent; + +/** + * Class represents a customcert page. + * + * @package mod_customcert + * @copyright 2024 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class page extends persistent { + + /** @var string The table name. */ + public const TABLE = 'customcert_pages'; + + /** + * Return the definition of the properties of this model. + * + * @return array + */ + protected static function define_properties() { + return [ + 'templateid' => [ + 'type' => PARAM_INT + ], + 'width' => [ + 'type' => PARAM_INT, + 'default' => 297 + ], + 'height' => [ + 'type' => PARAM_INT, + 'default' => 210 + ], + 'leftmargin' => [ + 'type' => PARAM_INT, + 'default' => 0 + ], + 'rightmargin' => [ + 'type' => PARAM_INT, + 'default' => 0 + ], + 'sequence' => [ + 'type' => PARAM_INT + ], + ]; + } + + public function after_create() + { + \mod_customcert\event\page_created::create_from_page($this->get('id'), $this)->trigger(); + } +} diff --git a/classes/persistent/template.php b/classes/persistent/template.php new file mode 100644 index 00000000..247910b1 --- /dev/null +++ b/classes/persistent/template.php @@ -0,0 +1,596 @@ +. + +/** + * Class represents a customcert template. + * + * @package mod_customcert + * @copyright 2016 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_customcert\persistent; + +use core\persistent; + +/** + * Class represents a customcert template. + * + * @package mod_customcert + * @copyright 2016 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class template extends persistent { + + /** + * @var int $id The id of the template. + */ + protected $id; + + /** + * @var string $name The name of this template + */ + protected $name; + + /** + * @var int $contextid The context id of this template + */ + protected $contextid; + + /** + * The constructor. + * + * @param \stdClass $template + */ + public function __construct($template) { + $this->id = $template->id; + $this->name = $template->name; + $this->contextid = $template->contextid; + } + + /** + * Handles saving data. + * + * @param \stdClass $data the template data + */ + public function save($data) { + global $DB; + + $savedata = new \stdClass(); + $savedata->id = $this->id; + $savedata->name = $data->name; + $savedata->timemodified = time(); + + $DB->update_record('customcert_templates', $savedata); + + // Only trigger event if the name has changed. + if ($savedata->name != $data->name) { + \mod_customcert\event\template_updated::create_from_template($this)->trigger(); + } + } + + /** + * Handles adding another page to the template. + * + * @param bool $triggertemplateupdatedevent + * @return int the id of the page + */ + public function add_page(bool $triggertemplateupdatedevent = true) { + global $DB; + + // Set the page number to 1 to begin with. + $sequence = 1; + // Get the max page number. + $sql = "SELECT MAX(sequence) as maxpage + FROM {customcert_pages} cp + WHERE cp.templateid = :templateid"; + if ($maxpage = $DB->get_record_sql($sql, array('templateid' => $this->id))) { + $sequence = $maxpage->maxpage + 1; + } + + // New page creation. + $data = new \stdClass(); + $data->templateid = $this->id; + $data->width = '210'; + $data->height = '297'; + $data->sequence = $sequence; + + // Insert the page. + $persistent = new page(0, $data); + $persistent->create(); + + \mod_customcert\event\page_created::create_from_page($page, $this)->trigger(); + + if ($triggertemplateupdatedevent) { + \mod_customcert\event\template_updated::create_from_template($this)->trigger(); + } + + return $page->id; + } + + /** + * Handles saving page data. + * + * @param \stdClass $data the template data + */ + public function save_page($data) { + global $DB; + + // Set the time to a variable. + $time = time(); + + // Get the existing pages and save the page data. + if ($pages = $DB->get_records('customcert_pages', array('templateid' => $data->tid))) { + // Loop through existing pages. + foreach ($pages as $page) { + // Only update if there is a difference. + if ($this->has_page_been_updated($page, $data)) { + $width = 'pagewidth_' . $page->id; + $height = 'pageheight_' . $page->id; + $leftmargin = 'pageleftmargin_' . $page->id; + $rightmargin = 'pagerightmargin_' . $page->id; + + $p = new \stdClass(); + $p->id = $page->id; + $p->width = $data->$width; + $p->height = $data->$height; + $p->leftmargin = $data->$leftmargin; + $p->rightmargin = $data->$rightmargin; + $p->timemodified = $time; + + // Update the page. + $DB->update_record('customcert_pages', $p); + + \mod_customcert\event\page_updated::create_from_page($p, $this)->trigger(); + + \mod_customcert\event\template_updated::create_from_template($this)->trigger(); + } + } + } + } + + /** + * Handles deleting the template. + * + * @return bool return true if the deletion was successful, false otherwise + */ + public function delete() { + global $DB; + + // Delete the elements. + $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' => $this->id))) { + foreach ($elements as $element) { + // Get an instance of the element class. + if ($e = \mod_customcert\element_factory::get_element_instance($element)) { + $e->delete(); + } else { + // The plugin files are missing, so just remove the entry from the DB. + $DB->delete_records('customcert_elements', array('id' => $element->id)); + } + } + } + + // Delete the pages. + if (!$DB->delete_records('customcert_pages', array('templateid' => $this->id))) { + return false; + } + + // Now, finally delete the actual template. + if (!$DB->delete_records('customcert_templates', array('id' => $this->id))) { + return false; + } + + \mod_customcert\event\template_deleted::create_from_template($this)->trigger(); + + return true; + } + + /** + * Handles deleting a page from the template. + * + * @param int $pageid the template page + */ + public function delete_page($pageid) { + global $DB; + + // Get the page. + $page = $DB->get_record('customcert_pages', array('id' => $pageid), '*', MUST_EXIST); + + // The element may have some extra tasks it needs to complete to completely delete itself. + if ($elements = $DB->get_records('customcert_elements', array('pageid' => $page->id))) { + foreach ($elements as $element) { + // Get an instance of the element class. + if ($e = \mod_customcert\element_factory::get_element_instance($element)) { + $e->delete(); + } else { + // The plugin files are missing, so just remove the entry from the DB. + $DB->delete_records('customcert_elements', array('id' => $element->id)); + } + } + } + + // Delete this page. + $DB->delete_records('customcert_pages', array('id' => $page->id)); + + \mod_customcert\event\page_deleted::create_from_page($page, $this)->trigger(); + + // Now we want to decrease the page number values of + // the pages that are greater than the page we deleted. + $sql = "UPDATE {customcert_pages} + SET sequence = sequence - 1 + WHERE templateid = :templateid + AND sequence > :sequence"; + $DB->execute($sql, array('templateid' => $this->id, 'sequence' => $page->sequence)); + + \mod_customcert\event\template_updated::create_from_template($this)->trigger(); + } + + /** + * Handles deleting an element from the template. + * + * @param int $elementid the template page + */ + public function delete_element($elementid) { + global $DB; + + // Ensure element exists and delete it. + $element = $DB->get_record('customcert_elements', array('id' => $elementid), '*', MUST_EXIST); + + // Get an instance of the element class. + if ($e = \mod_customcert\element_factory::get_element_instance($element)) { + $e->delete(); + } else { + // The plugin files are missing, so just remove the entry from the DB. + $DB->delete_records('customcert_elements', array('id' => $elementid)); + } + + // Now we want to decrease the sequence numbers of the elements + // that are greater than the element we deleted. + $sql = "UPDATE {customcert_elements} + SET sequence = sequence - 1 + WHERE pageid = :pageid + AND sequence > :sequence"; + $DB->execute($sql, array('pageid' => $element->pageid, 'sequence' => $element->sequence)); + + \mod_customcert\event\template_updated::create_from_template($this)->trigger(); + } + + /** + * Generate the PDF for the template. + * + * @param bool $preview true if it is a preview, false otherwise + * @param int $userid the id of the user whose certificate we want to view + * @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(bool $preview = false, int $userid = null, bool $return = false) { + global $CFG, $DB, $USER; + + if (empty($userid)) { + $user = $USER; + } else { + $user = \core_user::get_user($userid); + } + + require_once($CFG->libdir . '/pdflib.php'); + require_once($CFG->dirroot . '/mod/customcert/lib.php'); + + // 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]); + + // I want to have my digital diplomas without having to change my preferred language. + $userlang = $USER->lang ?? current_language(); + + // Check the $customcert exists as it is false when previewing from mod/customcert/manage_templates.php. + if ($customcert) { + $forcelang = mod_customcert_force_current_language($customcert->language); + if (!empty($forcelang)) { + // This is a failsafe -- if an exception triggers during the template rendering, this should still execute. + // Preventing a user from getting trapped with the wrong language. + \core_shutdown_manager::register_function('force_current_language', [$userlang]); + } + } + + // If the template belongs to a certificate then we need to check what permissions we set for it. + 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($filename); + $pdf->SetAutoPageBreak(true, 0); + + // 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) { + // Add the page to the PDF. + if ($page->width > $page->height) { + $orientation = 'L'; + } else { + $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); + } + } + } + } + + // Check the $customcert exists as it is false when previewing from mod/customcert/manage_templates.php. + if ($customcert) { + // We restore original language. + if ($userlang != $customcert->language) { + mod_customcert_force_current_language($userlang); + } + } + + if ($return) { + return $pdf->Output('', 'S'); + } + + $pdf->Output($filename, $deliveryoption); + } + } + + /** + * Handles copying this template into another. + * + * @param object $copytotemplate The template instance to copy to + */ + public function copy_to_template($copytotemplate) { + global $DB; + + $copytotemplateid = $copytotemplate->get_id(); + + // 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. + foreach ($templatepages as $templatepage) { + $page = clone($templatepage); + $page->templateid = $copytotemplateid; + $page->timecreated = time(); + $page->timemodified = $page->timecreated; + // Insert into the database. + $page->id = $DB->insert_record('customcert_pages', $page); + \mod_customcert\event\page_created::create_from_page($page, $this)->trigger(); + // Now go through the elements we want to load. + if ($templateelements = $DB->get_records('customcert_elements', array('pageid' => $templatepage->id))) { + foreach ($templateelements as $templateelement) { + $element = clone($templateelement); + $element->pageid = $page->id; + $element->timecreated = time(); + $element->timemodified = $element->timecreated; + // Ok, now we want to insert this into the database. + $element->id = $DB->insert_record('customcert_elements', $element); + // Load any other information the element may need to for the template. + if ($e = \mod_customcert\element_factory::get_element_instance($element)) { + if (!$e->copy_element($templateelement)) { + // Failed to copy - delete the element. + $e->delete(); + } else { + \mod_customcert\event\element_created::create_from_element($e)->trigger(); + } + } + } + } + } + + // Trigger event for template instance being copied to. + if ($copytotemplate->get_context() == \context_system::instance()) { + // If CONTEXT_SYSTEM we're creating a new template. + \mod_customcert\event\template_created::create_from_template($copytotemplate)->trigger(); + } else { + // Otherwise we're loading template in a course module instance. + \mod_customcert\event\template_updated::create_from_template($copytotemplate)->trigger(); + } + } + } + + /** + * Handles moving an item on a template. + * + * @param string $itemname the item we are moving + * @param int $itemid the id of the item + * @param string $direction the direction + */ + public function move_item($itemname, $itemid, $direction) { + global $DB; + + $table = 'customcert_'; + if ($itemname == 'page') { + $table .= 'pages'; + } else { // Must be an element. + $table .= 'elements'; + } + + if ($moveitem = $DB->get_record($table, array('id' => $itemid))) { + // Check which direction we are going. + if ($direction == 'up') { + $sequence = $moveitem->sequence - 1; + } else { // Must be down. + $sequence = $moveitem->sequence + 1; + } + + // Get the item we will be swapping with. Make sure it is related to the same template (if it's + // a page) or the same page (if it's an element). + if ($itemname == 'page') { + $params = array('templateid' => $moveitem->templateid); + } else { // Must be an element. + $params = array('pageid' => $moveitem->pageid); + } + $swapitem = $DB->get_record($table, $params + array('sequence' => $sequence)); + } + + // Check that there is an item to move, and an item to swap it with. + if ($moveitem && !empty($swapitem)) { + $DB->set_field($table, 'sequence', $swapitem->sequence, array('id' => $moveitem->id)); + $DB->set_field($table, 'sequence', $moveitem->sequence, array('id' => $swapitem->id)); + + \mod_customcert\event\template_updated::create_from_template($this)->trigger(); + } + } + + /** + * Returns the id of the template. + * + * @return int the id of the template + */ + public function get_id() { + return $this->id; + } + + /** + * Returns the name of the template. + * + * @return string the name of the template + */ + public function get_name() { + return $this->name; + } + + /** + * Returns the context id. + * + * @return int the context id + */ + public function get_contextid() { + return $this->contextid; + } + + /** + * Returns the context id. + * + * @return \context the context + */ + public function get_context() { + return \context::instance_by_id($this->contextid); + } + + /** + * Returns the context id. + * + * @return \context_module|null the context module, null if there is none + */ + public function get_cm() { + $context = $this->get_context(); + if ($context->contextlevel === CONTEXT_MODULE) { + return get_coursemodule_from_id('customcert', $context->instanceid, 0, false, MUST_EXIST); + } + + return null; + } + + /** + * Ensures the user has the proper capabilities to manage this template. + * + * @throws \required_capability_exception if the user does not have the necessary capabilities (ie. Fred) + */ + public function require_manage() { + require_capability('mod/customcert:manage', $this->get_context()); + } + + /** + * Creates a template. + * + * @param string $templatename the name of the template + * @param int $contextid the context id + * @return \mod_customcert\template the template object + */ + public static function create($templatename, $contextid) { + global $DB; + + $template = new \stdClass(); + $template->name = $templatename; + $template->contextid = $contextid; + $template->timecreated = time(); + $template->timemodified = $template->timecreated; + $template->id = $DB->insert_record('customcert_templates', $template); + + $template = new \mod_customcert\template($template); + + \mod_customcert\event\template_created::create_from_template($template)->trigger(); + + return $template; + } + + /** + * Checks if a page has been updated given form information + * + * @param \stdClass $page + * @param \stdClass $formdata + * @return bool + */ + private function has_page_been_updated($page, $formdata): bool { + $width = 'pagewidth_' . $page->id; + $height = 'pageheight_' . $page->id; + $leftmargin = 'pageleftmargin_' . $page->id; + $rightmargin = 'pagerightmargin_' . $page->id; + + if ($page->width != $formdata->$width) { + return true; + } + + if ($page->height != $formdata->$height) { + return true; + } + + if ($page->leftmargin != $formdata->$leftmargin) { + return true; + } + + if ($page->rightmargin != $formdata->$rightmargin) { + return true; + } + + return false; + } +} diff --git a/classes/template.php b/classes/template.php index f66fade6..e69de29b 100644 --- a/classes/template.php +++ b/classes/template.php @@ -1,597 +0,0 @@ -. - -/** - * Class represents a customcert template. - * - * @package mod_customcert - * @copyright 2016 Mark Nelson - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_customcert; - -/** - * Class represents a customcert template. - * - * @package mod_customcert - * @copyright 2016 Mark Nelson - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class template { - - /** - * @var int $id The id of the template. - */ - protected $id; - - /** - * @var string $name The name of this template - */ - protected $name; - - /** - * @var int $contextid The context id of this template - */ - protected $contextid; - - /** - * The constructor. - * - * @param \stdClass $template - */ - public function __construct($template) { - $this->id = $template->id; - $this->name = $template->name; - $this->contextid = $template->contextid; - } - - /** - * Handles saving data. - * - * @param \stdClass $data the template data - */ - public function save($data) { - global $DB; - - $savedata = new \stdClass(); - $savedata->id = $this->id; - $savedata->name = $data->name; - $savedata->timemodified = time(); - - $DB->update_record('customcert_templates', $savedata); - - // Only trigger event if the name has changed. - if ($savedata->name != $data->name) { - \mod_customcert\event\template_updated::create_from_template($this)->trigger(); - } - } - - /** - * Handles adding another page to the template. - * - * @param bool $triggertemplateupdatedevent - * @return int the id of the page - */ - public function add_page(bool $triggertemplateupdatedevent = true) { - global $DB; - - // Set the page number to 1 to begin with. - $sequence = 1; - // Get the max page number. - $sql = "SELECT MAX(sequence) as maxpage - FROM {customcert_pages} cp - WHERE cp.templateid = :templateid"; - if ($maxpage = $DB->get_record_sql($sql, array('templateid' => $this->id))) { - $sequence = $maxpage->maxpage + 1; - } - - // New page creation. - $page = new \stdClass(); - $page->templateid = $this->id; - $page->width = '210'; - $page->height = '297'; - $page->sequence = $sequence; - $page->timecreated = time(); - $page->timemodified = $page->timecreated; - - // Insert the page. - $pageid = $DB->insert_record('customcert_pages', $page); - - $page->id = $pageid; - - \mod_customcert\event\page_created::create_from_page($page, $this)->trigger(); - - if ($triggertemplateupdatedevent) { - \mod_customcert\event\template_updated::create_from_template($this)->trigger(); - } - - return $page->id; - } - - /** - * Handles saving page data. - * - * @param \stdClass $data the template data - */ - public function save_page($data) { - global $DB; - - // Set the time to a variable. - $time = time(); - - // Get the existing pages and save the page data. - if ($pages = $DB->get_records('customcert_pages', array('templateid' => $data->tid))) { - // Loop through existing pages. - foreach ($pages as $page) { - // Only update if there is a difference. - if ($this->has_page_been_updated($page, $data)) { - $width = 'pagewidth_' . $page->id; - $height = 'pageheight_' . $page->id; - $leftmargin = 'pageleftmargin_' . $page->id; - $rightmargin = 'pagerightmargin_' . $page->id; - - $p = new \stdClass(); - $p->id = $page->id; - $p->width = $data->$width; - $p->height = $data->$height; - $p->leftmargin = $data->$leftmargin; - $p->rightmargin = $data->$rightmargin; - $p->timemodified = $time; - - // Update the page. - $DB->update_record('customcert_pages', $p); - - \mod_customcert\event\page_updated::create_from_page($p, $this)->trigger(); - - \mod_customcert\event\template_updated::create_from_template($this)->trigger(); - } - } - } - } - - /** - * Handles deleting the template. - * - * @return bool return true if the deletion was successful, false otherwise - */ - public function delete() { - global $DB; - - // Delete the elements. - $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' => $this->id))) { - foreach ($elements as $element) { - // Get an instance of the element class. - if ($e = \mod_customcert\element_factory::get_element_instance($element)) { - $e->delete(); - } else { - // The plugin files are missing, so just remove the entry from the DB. - $DB->delete_records('customcert_elements', array('id' => $element->id)); - } - } - } - - // Delete the pages. - if (!$DB->delete_records('customcert_pages', array('templateid' => $this->id))) { - return false; - } - - // Now, finally delete the actual template. - if (!$DB->delete_records('customcert_templates', array('id' => $this->id))) { - return false; - } - - \mod_customcert\event\template_deleted::create_from_template($this)->trigger(); - - return true; - } - - /** - * Handles deleting a page from the template. - * - * @param int $pageid the template page - */ - public function delete_page($pageid) { - global $DB; - - // Get the page. - $page = $DB->get_record('customcert_pages', array('id' => $pageid), '*', MUST_EXIST); - - // The element may have some extra tasks it needs to complete to completely delete itself. - if ($elements = $DB->get_records('customcert_elements', array('pageid' => $page->id))) { - foreach ($elements as $element) { - // Get an instance of the element class. - if ($e = \mod_customcert\element_factory::get_element_instance($element)) { - $e->delete(); - } else { - // The plugin files are missing, so just remove the entry from the DB. - $DB->delete_records('customcert_elements', array('id' => $element->id)); - } - } - } - - // Delete this page. - $DB->delete_records('customcert_pages', array('id' => $page->id)); - - \mod_customcert\event\page_deleted::create_from_page($page, $this)->trigger(); - - // Now we want to decrease the page number values of - // the pages that are greater than the page we deleted. - $sql = "UPDATE {customcert_pages} - SET sequence = sequence - 1 - WHERE templateid = :templateid - AND sequence > :sequence"; - $DB->execute($sql, array('templateid' => $this->id, 'sequence' => $page->sequence)); - - \mod_customcert\event\template_updated::create_from_template($this)->trigger(); - } - - /** - * Handles deleting an element from the template. - * - * @param int $elementid the template page - */ - public function delete_element($elementid) { - global $DB; - - // Ensure element exists and delete it. - $element = $DB->get_record('customcert_elements', array('id' => $elementid), '*', MUST_EXIST); - - // Get an instance of the element class. - if ($e = \mod_customcert\element_factory::get_element_instance($element)) { - $e->delete(); - } else { - // The plugin files are missing, so just remove the entry from the DB. - $DB->delete_records('customcert_elements', array('id' => $elementid)); - } - - // Now we want to decrease the sequence numbers of the elements - // that are greater than the element we deleted. - $sql = "UPDATE {customcert_elements} - SET sequence = sequence - 1 - WHERE pageid = :pageid - AND sequence > :sequence"; - $DB->execute($sql, array('pageid' => $element->pageid, 'sequence' => $element->sequence)); - - \mod_customcert\event\template_updated::create_from_template($this)->trigger(); - } - - /** - * Generate the PDF for the template. - * - * @param bool $preview true if it is a preview, false otherwise - * @param int $userid the id of the user whose certificate we want to view - * @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(bool $preview = false, int $userid = null, bool $return = false) { - global $CFG, $DB, $USER; - - if (empty($userid)) { - $user = $USER; - } else { - $user = \core_user::get_user($userid); - } - - require_once($CFG->libdir . '/pdflib.php'); - require_once($CFG->dirroot . '/mod/customcert/lib.php'); - - // 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]); - - // I want to have my digital diplomas without having to change my preferred language. - $userlang = $USER->lang ?? current_language(); - - // Check the $customcert exists as it is false when previewing from mod/customcert/manage_templates.php. - if ($customcert) { - $forcelang = mod_customcert_force_current_language($customcert->language); - if (!empty($forcelang)) { - // This is a failsafe -- if an exception triggers during the template rendering, this should still execute. - // Preventing a user from getting trapped with the wrong language. - \core_shutdown_manager::register_function('force_current_language', [$userlang]); - } - } - - // If the template belongs to a certificate then we need to check what permissions we set for it. - 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($filename); - $pdf->SetAutoPageBreak(true, 0); - - // 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) { - // Add the page to the PDF. - if ($page->width > $page->height) { - $orientation = 'L'; - } else { - $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); - } - } - } - } - - // Check the $customcert exists as it is false when previewing from mod/customcert/manage_templates.php. - if ($customcert) { - // We restore original language. - if ($userlang != $customcert->language) { - mod_customcert_force_current_language($userlang); - } - } - - if ($return) { - return $pdf->Output('', 'S'); - } - - $pdf->Output($filename, $deliveryoption); - } - } - - /** - * Handles copying this template into another. - * - * @param object $copytotemplate The template instance to copy to - */ - public function copy_to_template($copytotemplate) { - global $DB; - - $copytotemplateid = $copytotemplate->get_id(); - - // 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. - foreach ($templatepages as $templatepage) { - $page = clone($templatepage); - $page->templateid = $copytotemplateid; - $page->timecreated = time(); - $page->timemodified = $page->timecreated; - // Insert into the database. - $page->id = $DB->insert_record('customcert_pages', $page); - \mod_customcert\event\page_created::create_from_page($page, $this)->trigger(); - // Now go through the elements we want to load. - if ($templateelements = $DB->get_records('customcert_elements', array('pageid' => $templatepage->id))) { - foreach ($templateelements as $templateelement) { - $element = clone($templateelement); - $element->pageid = $page->id; - $element->timecreated = time(); - $element->timemodified = $element->timecreated; - // Ok, now we want to insert this into the database. - $element->id = $DB->insert_record('customcert_elements', $element); - // Load any other information the element may need to for the template. - if ($e = \mod_customcert\element_factory::get_element_instance($element)) { - if (!$e->copy_element($templateelement)) { - // Failed to copy - delete the element. - $e->delete(); - } else { - \mod_customcert\event\element_created::create_from_element($e)->trigger(); - } - } - } - } - } - - // Trigger event for template instance being copied to. - if ($copytotemplate->get_context() == \context_system::instance()) { - // If CONTEXT_SYSTEM we're creating a new template. - \mod_customcert\event\template_created::create_from_template($copytotemplate)->trigger(); - } else { - // Otherwise we're loading template in a course module instance. - \mod_customcert\event\template_updated::create_from_template($copytotemplate)->trigger(); - } - } - } - - /** - * Handles moving an item on a template. - * - * @param string $itemname the item we are moving - * @param int $itemid the id of the item - * @param string $direction the direction - */ - public function move_item($itemname, $itemid, $direction) { - global $DB; - - $table = 'customcert_'; - if ($itemname == 'page') { - $table .= 'pages'; - } else { // Must be an element. - $table .= 'elements'; - } - - if ($moveitem = $DB->get_record($table, array('id' => $itemid))) { - // Check which direction we are going. - if ($direction == 'up') { - $sequence = $moveitem->sequence - 1; - } else { // Must be down. - $sequence = $moveitem->sequence + 1; - } - - // Get the item we will be swapping with. Make sure it is related to the same template (if it's - // a page) or the same page (if it's an element). - if ($itemname == 'page') { - $params = array('templateid' => $moveitem->templateid); - } else { // Must be an element. - $params = array('pageid' => $moveitem->pageid); - } - $swapitem = $DB->get_record($table, $params + array('sequence' => $sequence)); - } - - // Check that there is an item to move, and an item to swap it with. - if ($moveitem && !empty($swapitem)) { - $DB->set_field($table, 'sequence', $swapitem->sequence, array('id' => $moveitem->id)); - $DB->set_field($table, 'sequence', $moveitem->sequence, array('id' => $swapitem->id)); - - \mod_customcert\event\template_updated::create_from_template($this)->trigger(); - } - } - - /** - * Returns the id of the template. - * - * @return int the id of the template - */ - public function get_id() { - return $this->id; - } - - /** - * Returns the name of the template. - * - * @return string the name of the template - */ - public function get_name() { - return $this->name; - } - - /** - * Returns the context id. - * - * @return int the context id - */ - public function get_contextid() { - return $this->contextid; - } - - /** - * Returns the context id. - * - * @return \context the context - */ - public function get_context() { - return \context::instance_by_id($this->contextid); - } - - /** - * Returns the context id. - * - * @return \context_module|null the context module, null if there is none - */ - public function get_cm() { - $context = $this->get_context(); - if ($context->contextlevel === CONTEXT_MODULE) { - return get_coursemodule_from_id('customcert', $context->instanceid, 0, false, MUST_EXIST); - } - - return null; - } - - /** - * Ensures the user has the proper capabilities to manage this template. - * - * @throws \required_capability_exception if the user does not have the necessary capabilities (ie. Fred) - */ - public function require_manage() { - require_capability('mod/customcert:manage', $this->get_context()); - } - - /** - * Creates a template. - * - * @param string $templatename the name of the template - * @param int $contextid the context id - * @return \mod_customcert\template the template object - */ - public static function create($templatename, $contextid) { - global $DB; - - $template = new \stdClass(); - $template->name = $templatename; - $template->contextid = $contextid; - $template->timecreated = time(); - $template->timemodified = $template->timecreated; - $template->id = $DB->insert_record('customcert_templates', $template); - - $template = new \mod_customcert\template($template); - - \mod_customcert\event\template_created::create_from_template($template)->trigger(); - - return $template; - } - - /** - * Checks if a page has been updated given form information - * - * @param \stdClass $page - * @param \stdClass $formdata - * @return bool - */ - private function has_page_been_updated($page, $formdata): bool { - $width = 'pagewidth_' . $page->id; - $height = 'pageheight_' . $page->id; - $leftmargin = 'pageleftmargin_' . $page->id; - $rightmargin = 'pagerightmargin_' . $page->id; - - if ($page->width != $formdata->$width) { - return true; - } - - if ($page->height != $formdata->$height) { - return true; - } - - if ($page->leftmargin != $formdata->$leftmargin) { - return true; - } - - if ($page->rightmargin != $formdata->$rightmargin) { - return true; - } - - return false; - } -} diff --git a/db/install.xml b/db/install.xml index c4226720..c4724262 100644 --- a/db/install.xml +++ b/db/install.xml @@ -35,10 +35,12 @@ + + @@ -70,10 +72,12 @@ + +
@@ -94,10 +98,12 @@ + +
diff --git a/db/upgrade.php b/db/upgrade.php index c089d027..f5d3cfca 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -235,5 +235,40 @@ function xmldb_customcert_upgrade($oldversion) { upgrade_mod_savepoint(true, 2023042405, 'customcert'); } + if ($oldversion < 2023042406) { + // Add new 'usermodified' field to the customcert_templates table as we are adding logic to a persistent. + $table = new xmldb_table('customcert_templates'); + $field = new xmldb_field('usermodified', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', + 'timemodified'); + + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); + $dbman->add_key($table, $key); + + // Add new 'usermodified' field to the customcert_pages table as we are adding logic to a persistent. + $table = new xmldb_table('customcert_pages'); + $field = new xmldb_field('usermodified', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', + 'timemodified'); + + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Add new 'usermodified' field to the customcert_elements table as we are adding logic to a persistent. + $table = new xmldb_table('customcert_elements'); + $field = new xmldb_field('usermodified', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', + 'timemodified'); + + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Customcert savepoint reached. + upgrade_mod_savepoint(true, 2023042406, 'customcert'); + } + return true; } diff --git a/version.php b/version.php index 3891a958..cebb19f0 100644 --- a/version.php +++ b/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); -$plugin->version = 2023042405; // The current module version (Date: YYYYMMDDXX). +$plugin->version = 2023042406; // The current module version (Date: YYYYMMDDXX). $plugin->requires = 2023042400; // Requires this Moodle version (4.2). $plugin->cron = 0; // Period for cron to check this module (secs). $plugin->component = 'mod_customcert';