Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]

   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  
  17  /**
  18   * Class containing helper methods for processing data requests.
  19   *
  20   * @package    tool_dataprivacy
  21   * @copyright  2018 Adrian Greeve
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  namespace tool_dataprivacy;
  25  
  26  use core_privacy\local\metadata\types\type;
  27  
  28  defined('MOODLE_INTERNAL') || die();
  29  
  30  /**
  31   * Class containing helper methods for processing data requests.
  32   *
  33   * @copyright  2018 Adrian Greeve
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class metadata_registry {
  37  
  38      /**
  39       * Returns plugin types / plugins and the user data that it stores in a format that can be sent to a template.
  40       *
  41       * @return array An array with all of the plugin types / plugins and the user data they store.
  42       */
  43      public function get_registry_metadata() {
  44          $manager = new \core_privacy\manager();
  45          $manager->set_observer(new \tool_dataprivacy\manager_observer());
  46  
  47          $pluginman = \core_plugin_manager::instance();
  48          $contributedplugins = $this->get_contrib_list();
  49          $metadata = $manager->get_metadata_for_components();
  50          $fullyrichtree = $this->get_full_component_list();
  51          foreach ($fullyrichtree as $branch => $leaves) {
  52              $plugintype = $leaves['plugin_type'];
  53              $plugins = array_map(function($component) use ($manager, $metadata, $contributedplugins, $plugintype, $pluginman) {
  54                  // Use the plugin name for the plugins, ignore for core subsystems.
  55                  $internaldata = ($plugintype == 'core') ? ['component' => $component] :
  56                          ['component' => $pluginman->plugin_name($component)];
  57                  $internaldata['raw_component'] = $component;
  58                  if ($manager->component_is_compliant($component)) {
  59                      $internaldata['compliant'] = true;
  60                      if (isset($metadata[$component])) {
  61                          $collection = $metadata[$component]->get_collection();
  62                          $internaldata = $this->format_metadata($collection, $component, $internaldata);
  63                      } else if ($manager->is_empty_subsystem($component)) {
  64                          // This is an unused subsystem.
  65                          // Use the generic string.
  66                          $internaldata['nullprovider'] = get_string('privacy:subsystem:empty', 'core_privacy');
  67                      } else {
  68                          // Call get_reason for null provider.
  69                          $internaldata['nullprovider'] = get_string($manager->get_null_provider_reason($component), $component);
  70                      }
  71                  } else {
  72                      $internaldata['compliant'] = false;
  73                  }
  74                  // Check to see if we are an external plugin.
  75                  // Plugin names can contain _ characters, limit to 2 to just remove initial plugintype.
  76                  $componentshortname = explode('_', $component, 2);
  77                  $shortname = array_pop($componentshortname);
  78                  if (isset($contributedplugins[$plugintype][$shortname])) {
  79                      $internaldata['external'] = true;
  80                  }
  81  
  82                  // Additional interface checks.
  83                  if (!$manager->is_empty_subsystem($component)) {
  84                      $classname = $manager->get_provider_classname_for_component($component);
  85                      if (class_exists($classname)) {
  86                          $componentclass = new $classname();
  87                          // Check if the interface is deprecated.
  88                          if ($componentclass instanceof \core_privacy\local\deprecated) {
  89                              $internaldata['deprecated'] = true;
  90                          }
  91  
  92                          // Check that the core_userlist_provider is implemented for all user data providers.
  93                          if ($componentclass instanceof \core_privacy\local\request\core_user_data_provider
  94                                  && !$componentclass instanceof \core_privacy\local\request\core_userlist_provider) {
  95                              $internaldata['userlistnoncompliance'] = true;
  96                          }
  97  
  98                          // Check that any type of userlist_provider is implemented for all shared data providers.
  99                          if ($componentclass instanceof \core_privacy\local\request\shared_data_provider
 100                                  && !$componentclass instanceof \core_privacy\local\request\userlist_provider) {
 101                              $internaldata['userlistnoncompliance'] = true;
 102                          }
 103                      }
 104                  }
 105  
 106                  return $internaldata;
 107              }, $leaves['plugins']);
 108              $fullyrichtree[$branch]['plugin_type_raw'] = $plugintype;
 109              // We're done using the plugin type. Convert it to a readable string.
 110              $fullyrichtree[$branch]['plugin_type'] = $pluginman->plugintype_name($plugintype);
 111              $fullyrichtree[$branch]['plugins'] = $plugins;
 112          }
 113          return $fullyrichtree;
 114      }
 115  
 116      /**
 117       * Formats the metadata for use with a template.
 118       *
 119       * @param  type[] $collection The collection associated with the component that we want to expand and format.
 120       * @param  string $component The component that we are dealing in
 121       * @param  array $internaldata The array to add the formatted metadata to.
 122       * @return array The internal data array with the formatted metadata.
 123       */
 124      protected function format_metadata($collection, $component, $internaldata) {
 125          foreach ($collection as $collectioninfo) {
 126              $privacyfields = $collectioninfo->get_privacy_fields();
 127              $fields = '';
 128              if (!empty($privacyfields)) {
 129                  $fields = array_map(function($key, $field) use ($component) {
 130                      return [
 131                          'field_name' => $key,
 132                          'field_summary' => get_string($field, $component)
 133                      ];
 134                  }, array_keys($privacyfields), $privacyfields);
 135              }
 136              // Can the metadata types be located somewhere else besides core?
 137              $items = explode('\\', get_class($collectioninfo));
 138              $type = array_pop($items);
 139              $typedata = [
 140                  'name' => $collectioninfo->get_name(),
 141                  'type' => $type,
 142                  'fields' => $fields,
 143                  'summary' => get_string($collectioninfo->get_summary(), $component)
 144              ];
 145              if (strpos($type, 'subsystem_link') === 0 || strpos($type, 'plugintype_link') === 0) {
 146                  $typedata['link'] = true;
 147              }
 148              $internaldata['metadata'][] = $typedata;
 149          }
 150          return $internaldata;
 151      }
 152  
 153      /**
 154       * Return the full list of components.
 155       *
 156       * @return array An array of plugin types which contain plugin data.
 157       */
 158      protected function get_full_component_list() {
 159          global $CFG;
 160  
 161          $list = \core_component::get_component_list();
 162          $list['core']['core'] = "{$CFG->dirroot}/lib";
 163          $formattedlist = [];
 164          foreach ($list as $plugintype => $plugin) {
 165              $formattedlist[] = ['plugin_type' => $plugintype, 'plugins' => array_keys($plugin)];
 166          }
 167  
 168          return $formattedlist;
 169      }
 170  
 171      /**
 172       * Returns a list of contributed plugins installed on the system.
 173       *
 174       * @return array A list of contributed plugins installed.
 175       */
 176      protected function get_contrib_list() {
 177          return array_map(function($plugins) {
 178              return array_filter($plugins, function($plugindata) {
 179                  return !$plugindata->is_standard();
 180              });
 181          }, \core_plugin_manager::instance()->get_plugins());
 182      }
 183  }