Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  namespace mod_bigbluebuttonbn;
  17  
  18  use cache;
  19  use mod_bigbluebuttonbn\local\extension\action_url_addons;
  20  use mod_bigbluebuttonbn\local\extension\mod_form_addons;
  21  use mod_bigbluebuttonbn\local\extension\mod_instance_helper;
  22  use stdClass;
  23  use core_plugin_manager;
  24  
  25  /**
  26   * Generic subplugin management helper
  27   *
  28   * @package   mod_bigbluebuttonbn
  29   * @copyright 2023 onwards, Blindside Networks Inc
  30   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  31   * @author    Laurent David (laurent@call-learning.fr)
  32   */
  33  class extension {
  34      /**
  35       * Plugin name for extension
  36       */
  37      const BBB_EXTENSION_PLUGIN_NAME = 'bbbext';
  38  
  39      /**
  40       * Invoke a subplugin hook that will return additional parameters
  41       *
  42       * @param string $action
  43       * @param array $data
  44       * @param array $metadata
  45       * @param int|null $instanceid
  46       * @return array associative array with the additional data and metadata (indexed by 'data' and
  47       * 'metadata' keys).
  48       */
  49      public static function action_url_addons(
  50          string $action = '',
  51          array $data = [],
  52          array $metadata = [],
  53          ?int $instanceid = null
  54      ): array {
  55          $allmutationclass = self::get_instances_implementing(action_url_addons::class);
  56          $additionaldata = [];
  57          $additionalmetadata = [];
  58          foreach ($allmutationclass as $mutationclass) {
  59              // Here we intentionally just pass data and metadata and not the result as we
  60              // do not want subplugin to assume that another subplugin is doing a modification.
  61              ['data' => $newdata, 'metadata' => $newmetadata] = $mutationclass->execute($action, $data, $metadata, $instanceid);
  62              $additionaldata = array_merge($additionaldata, $newdata ?? []);
  63              $additionalmetadata = array_merge($additionalmetadata, $newmetadata ?? []);
  64          }
  65          return [
  66              'data' => $additionaldata,
  67              'metadata' => $additionalmetadata
  68          ];
  69      }
  70  
  71      /**
  72       * Get new instance of classes that are named on the base of this classname and implementing this class
  73       *
  74       * @param string $classname
  75       * @param array|null $newparameters additional parameters for the constructor.
  76       * @return array
  77       */
  78      protected static function get_instances_implementing(string $classname, ?array $newparameters = []): array {
  79          $classes = self::get_classes_implementing($classname);
  80          sort($classes); // Make sure all extension classes are returned in the same order. This is arbitrarily in
  81          // alphabetical order and depends on the classname but this one way to ensure consistency across calls.
  82          return array_map(function($targetclassname) use ($newparameters) {
  83              // If $newparameters is null, the constructor will be called without parameters.
  84              return new $targetclassname(...$newparameters);
  85          }, $classes);
  86      }
  87  
  88      /**
  89       * Get classes are named on the base of this classname and implementing this class
  90       *
  91       * @param string $classname
  92       * @return array
  93       */
  94      protected static function get_classes_implementing(string $classname): array {
  95          // Get the class basename without Reflection API.
  96          $classnamecomponents = explode("\\", $classname);
  97          $classbasename = end($classnamecomponents);
  98          $allsubs = core_plugin_manager::instance()->get_plugins_of_type(self::BBB_EXTENSION_PLUGIN_NAME);
  99          $extensionclasses = [];
 100          foreach ($allsubs as $sub) {
 101              if (!$sub->is_enabled()) {
 102                  continue;
 103              }
 104              $targetclassname = "\\bbbext_{$sub->name}\\bigbluebuttonbn\\$classbasename";
 105              if (!class_exists($targetclassname)) {
 106                  continue;
 107              }
 108              if (!is_subclass_of($targetclassname, $classname)) {
 109                  debugging("The class $targetclassname should extend $classname in the subplugin {$sub->name}. Ignoring.");
 110                  continue;
 111              }
 112              $extensionclasses[] = $targetclassname;
 113          }
 114          return $extensionclasses;
 115      }
 116  
 117      /**
 118       * Get all mod_form addons classes instances
 119       *
 120       * @param \MoodleQuickForm $mform
 121       * @param stdClass|null $bigbluebuttondata
 122       * @param string|null $suffix
 123       * @return array of custom completion addon classes instances
 124       */
 125      public static function mod_form_addons_instances(\MoodleQuickForm $mform, ?stdClass $bigbluebuttondata = null,
 126          string $suffix = null): array {
 127          return self::get_instances_implementing(mod_form_addons::class, [$mform, $bigbluebuttondata, $suffix]);
 128      }
 129  
 130      /**
 131       * Get additional join tables for instance when extension activated
 132       *
 133       * @return array of additional tables names. They all have a field called bigbluebuttonbnid that identifies the bbb instance.
 134       */
 135      public static function get_join_tables(): array {
 136          global $DB;
 137          // We use cache here as it will be called very often.
 138          $cache = cache::make('mod_bigbluebuttonbn', 'subplugins');
 139          if ($cache->get('additionaltables')) {
 140              return $cache->get('additionaltables');
 141          }
 142          $additionaltablesclasses = self::get_instances_implementing(mod_instance_helper::class);
 143          $tables = [];
 144          foreach ($additionaltablesclasses as $tableclass) {
 145              $tables = array_merge($tables, $tableclass->get_join_tables() ?? []);
 146          }
 147          // Warning and removal for tables that do not have the bigbluebuttonid field.
 148          foreach ($tables as $index => $table) {
 149              $columns = $DB->get_columns($table);
 150              if (empty($columns['bigbluebuttonbnid'])) {
 151                  debugging("get_instance_additional_tables: $table should have a column named bigbluebuttonid");
 152                  unset($tables[$index]);
 153              }
 154          }
 155          $cache->set('additionaltables', $tables);
 156          return $tables;
 157      }
 158  
 159      /**
 160       * Add instance processing
 161       *
 162       * @param stdClass $data data to persist
 163       * @return void
 164       */
 165      public static function add_instance(stdClass $data): void {
 166          $formmanagersclasses = self::get_instances_implementing(mod_instance_helper::class);
 167          foreach ($formmanagersclasses as $fmclass) {
 168              $fmclass->add_instance($data);
 169          }
 170      }
 171  
 172      /**
 173       * Update instance processing
 174       *
 175       * @param stdClass $data data to persist
 176       * @return void
 177       */
 178      public static function update_instance(stdClass $data): void {
 179          $formmanagersclasses = self::get_instances_implementing(mod_instance_helper::class);
 180          foreach ($formmanagersclasses as $fmclass) {
 181              $fmclass->update_instance($data);
 182          }
 183      }
 184  
 185      /**
 186       * Delete instance processing
 187       *
 188       * @param int $id instance id
 189       * @return void
 190       */
 191      public static function delete_instance(int $id): void {
 192          $formmanagersclasses = self::get_instances_implementing(mod_instance_helper::class);
 193          foreach ($formmanagersclasses as $fmclass) {
 194              $fmclass->delete_instance($id);
 195          }
 196      }
 197  }