From 36ee9ed4c9df507304ad133e565a517a14f736dd Mon Sep 17 00:00:00 2001 From: Petr Skoda Date: Mon, 24 Jun 2024 14:53:38 +0200 Subject: [PATCH] Add hook for certificate element classes discovery --- classes/element.php | 13 +++--- classes/element_helper.php | 55 +++++++++++++++++------ classes/hook/element_classes.php | 76 ++++++++++++++++++++++++++++++++ classes/output/element.php | 3 +- 4 files changed, 126 insertions(+), 21 deletions(-) create mode 100644 classes/hook/element_classes.php diff --git a/classes/element.php b/classes/element.php index 2f7c1165..58322940 100644 --- a/classes/element.php +++ b/classes/element.php @@ -69,18 +69,19 @@ public static function instance(int $id = 0, ?\stdClass $obj = null): element { * Helper method to create an instance from persistent * * @param \tool_certificate\persistent\element $persistent - * @return element + * @return element|null */ protected static function instance_from_persistent(\tool_certificate\persistent\element $persistent): ?element { - // Get the class name. - $classname = '\\certificateelement_' . $persistent->get('element') . '\\element'; + $elementclasses = element_helper::get_element_classes(); - // Ensure the necessary class exists. - if (!class_exists($classname) || !is_subclass_of($classname, self::class)) { + $shortname = $persistent->get('element'); + if (!isset($elementclasses[$shortname])) { return null; } - /** @var self $el */ + /** @var class-string $classname */ + $classname = $elementclasses[$shortname]; + $el = new $classname($persistent); $el->persistent = $persistent; return $el; diff --git a/classes/element_helper.php b/classes/element_helper.php index b6e36db6..a6356901 100644 --- a/classes/element_helper.php +++ b/classes/element_helper.php @@ -446,29 +446,58 @@ public static function get_element_sequence($pageid) { return $sequence; } + /** + * Returns all enabled element classes. + * + * @return array> + */ + public static function get_element_classes(): array { + $plugins = self::get_enabled_plugins(); + + if (!class_exists(\core\hook\manager::class)) { + // Legacy code for 4.2.x and older. + $options = []; + foreach ($plugins as $plugin) { + /** @var element $classname */ + $classname = '\\certificateelement_' . $plugin. '\\element'; + // Ensure the necessary class exists. + if (class_exists($classname) && is_subclass_of($classname, element::class)) { + $options[$plugin] = $classname; + } + } + return $options; + } + + $hook = new hook\element_classes(); + + // Loop through the enabled plugins. + foreach ($plugins as $plugin) { + $classname = 'certificateelement_' . $plugin. '\\element'; + $hook->add_class($plugin, $classname); + } + + // Ask other plugins if they define more elements. + \core\hook\manager::get_instance()->dispatch($hook); + + return $hook->get_classes(); + } + /** * Return the list of possible elements to add. * * @return array the list of element types that can be used. */ - public static function get_available_element_types() { - global $CFG; - + public static function get_available_element_types(): array { // Array to store the element types. $options = []; - $plugins = self::get_enabled_plugins(); + $classes = self::get_element_classes(); // Loop through the enabled plugins. - foreach ($plugins as $plugin) { - /** @var element $classname */ - $classname = '\\certificateelement_' . $plugin. '\\element'; - // Ensure the necessary class exists. - if (class_exists($classname) && is_subclass_of($classname, element::class)) { - // Additionally, check if the user is allowed to add the element at all. - if ($classname::can_add()) { - $options[$plugin] = $classname::get_element_type_name(); - } + foreach ($classes as $shortname => $classname) { + // Additionally, check if the user is allowed to add the element at all. + if ($classname::can_add()) { + $options[$shortname] = $classname::get_element_type_name(); } } diff --git a/classes/hook/element_classes.php b/classes/hook/element_classes.php new file mode 100644 index 00000000..356d1d76 --- /dev/null +++ b/classes/hook/element_classes.php @@ -0,0 +1,76 @@ +. + +namespace tool_certificate\hook; + +/** + * Certification element classes discovery hook. + * + * @package tool_certificate + * @copyright 2024 Petr Skoda + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +final class element_classes implements \core\hook\described_hook { + /** + * @var array> + */ + protected $classes = []; + + /** + * Add known enabled element class. + * + * @param string $shortname name of certificateelement sub-plugin or other unique name + * @param string $classname \tool_certificate\element class + */ + public function add_class(string $shortname, string $classname): void { + if (!class_exists($classname) || !is_subclass_of($classname, \tool_certificate\element::class)) { + debugging('Invalid certificate element class: ' . $classname, DEBUG_DEVELOPER); + return; + } + if (isset($this->classes[$shortname])) { + debugging('Duplicate certificate element short name detected: ' . $shortname, DEBUG_DEVELOPER); + // Override previous in case admins forgot to uninstall element add-on. + } + $this->classes[$shortname] = $classname; + } + + /** + * Returns known enabled element classes indexed with their short names. + * + * @return array> + */ + public function get_classes(): array { + return $this->classes; + } + + /** + * Hook description. + * + * @return string + */ + public static function get_hook_description(): string { + return 'Certificate element class discovery'; + } + + /** + * Hook tags. + * + * @return array + */ + public static function get_hook_tags(): array { + return []; + } +} diff --git a/classes/output/element.php b/classes/output/element.php index f2d3b7c7..c79f2409 100644 --- a/classes/output/element.php +++ b/classes/output/element.php @@ -89,11 +89,10 @@ protected static function define_other_properties(): array { */ protected function get_other_values(\renderer_base $output): array { $element = $this->get_element(); - $pluginname = 'certificateelement_' . $this->persistent->get('element'); return [ 'displayname' => $element->get_display_name(), 'editablename' => $element->get_inplace_editable()->export_for_template($output), - 'elementtype' => get_string('pluginname', $pluginname), + 'elementtype' => $element::get_element_type_name(), 'movetitle' => get_string('changeelementsequence', 'tool_certificate'), 'icon' => $output->render($this->get_element()->get_element_type_image(true)), 'html' => $this->get_element()->render_html(),