Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403]

   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   * Data registry renderable.
  19   *
  20   * @package    tool_dataprivacy
  21   * @copyright  2018 David Monllao
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  namespace tool_dataprivacy\output;
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  use renderable;
  28  use renderer_base;
  29  use stdClass;
  30  use templatable;
  31  use tool_dataprivacy\data_registry;
  32  
  33  require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/dataprivacy/lib.php');
  34  require_once($CFG->libdir . '/blocklib.php');
  35  
  36  /**
  37   * Class containing the data registry renderable
  38   *
  39   * @copyright  2018 David Monllao
  40   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   */
  42  class data_registry_page implements renderable, templatable {
  43  
  44      /**
  45       * @var int
  46       */
  47      private $defaultcontextlevel;
  48  
  49      /**
  50       * @var int
  51       */
  52      private $defaultcontextid;
  53  
  54      /**
  55       * Constructor.
  56       *
  57       * @param int $defaultcontextlevel
  58       * @param int $defaultcontextid
  59       * @return null
  60       */
  61      public function __construct($defaultcontextlevel = false, $defaultcontextid = false) {
  62          $this->defaultcontextlevel = $defaultcontextlevel;
  63          $this->defaultcontextid = $defaultcontextid;
  64      }
  65  
  66      /**
  67       * Export this data so it can be used as the context for a mustache template.
  68       *
  69       * @param renderer_base $output
  70       * @return stdClass
  71       */
  72      public function export_for_template(renderer_base $output) {
  73          global $PAGE;
  74  
  75          $params = [\context_system::instance()->id, $this->defaultcontextlevel, $this->defaultcontextid];
  76          $PAGE->requires->js_call_amd('tool_dataprivacy/data_registry', 'init', $params);
  77  
  78          $data = new stdClass();
  79          $defaultsbutton = new \action_link(
  80              new \moodle_url('/admin/tool/dataprivacy/defaults.php'),
  81              get_string('setdefaults', 'tool_dataprivacy'),
  82              null,
  83              ['class' => 'btn btn-primary']
  84          );
  85          $data->defaultsbutton = $defaultsbutton->export_for_template($output);
  86  
  87          $actionmenu = new \action_menu();
  88          $actionmenu->set_menu_trigger(get_string('edit'), 'btn btn-primary');
  89          $actionmenu->set_owner_selector('dataregistry-actions');
  90          $actionmenu->set_alignment(\action_menu::TL, \action_menu::BL);
  91  
  92          $url = new \moodle_url('/admin/tool/dataprivacy/categories.php');
  93          $categories = new \action_menu_link_secondary($url, null, get_string('categories', 'tool_dataprivacy'));
  94          $actionmenu->add($categories);
  95  
  96          $url = new \moodle_url('/admin/tool/dataprivacy/purposes.php');
  97          $purposes = new \action_menu_link_secondary($url, null, get_string('purposes', 'tool_dataprivacy'));
  98          $actionmenu->add($purposes);
  99  
 100          $data->actions = $actionmenu->export_for_template($output);
 101  
 102          if (!data_registry::defaults_set()) {
 103              $data->info = (object)[
 104                      'message' => get_string('dataregistryinfo', 'tool_dataprivacy'),
 105                      'announce' => 1
 106              ];
 107              $data->nosystemdefaults = (object)[
 108                  'message' => get_string('nosystemdefaults', 'tool_dataprivacy'),
 109                  'announce' => 1
 110              ];
 111          }
 112  
 113          $data->tree = $this->get_default_tree_structure();
 114  
 115          return $data;
 116      }
 117  
 118      /**
 119       * Returns the tree default structure.
 120       *
 121       * @return array
 122       */
 123      private function get_default_tree_structure() {
 124  
 125          $frontpage = \context_course::instance(SITEID);
 126  
 127          $categorybranches = $this->get_all_category_branches();
 128  
 129          $elements = [
 130              'text' => get_string('contextlevelname' . CONTEXT_SYSTEM, 'tool_dataprivacy'),
 131              'contextlevel' => CONTEXT_SYSTEM,
 132              'branches' => [
 133                  [
 134                      'text' => get_string('user'),
 135                      'contextlevel' => CONTEXT_USER,
 136                  ], [
 137                      'text' => get_string('categories'),
 138                      'branches' => $categorybranches,
 139                      'expandelement' => 'category',
 140                  ], [
 141                      'text' => get_string('frontpagecourse', 'tool_dataprivacy'),
 142                      'contextid' => $frontpage->id,
 143                      'branches' => [
 144                          [
 145                              'text' => get_string('activitiesandresources', 'tool_dataprivacy'),
 146                              'expandcontextid' => $frontpage->id,
 147                              'expandelement' => 'module',
 148                              'expanded' => 0,
 149                          ], [
 150                              'text' => get_string('blocks'),
 151                              'expandcontextid' => $frontpage->id,
 152                              'expandelement' => 'block',
 153                              'expanded' => 0,
 154                          ],
 155                      ]
 156                  ]
 157              ]
 158          ];
 159  
 160          // Returned as an array to follow a common array format.
 161          return [self::complete($elements, $this->defaultcontextlevel, $this->defaultcontextid)];
 162      }
 163  
 164      /**
 165       * Returns the hierarchy of system course categories.
 166       *
 167       * @return array
 168       */
 169      private function get_all_category_branches() {
 170  
 171          $categories = data_registry::get_site_categories();
 172  
 173          $categoriesbranch = [];
 174          while (count($categories) > 0) {
 175              foreach ($categories as $key => $category) {
 176  
 177                  $context = \context_coursecat::instance($category->id);
 178                  $newnode = [
 179                      'text' => shorten_text(format_string($category->name, true, ['context' => $context])),
 180                      'categoryid' => $category->id,
 181                      'contextid' => $context->id,
 182                  ];
 183                  if ($category->coursecount > 0) {
 184                      $newnode['branches'] = [
 185                          [
 186                              'text' => get_string('courses'),
 187                              'expandcontextid' => $context->id,
 188                              'expandelement' => 'course',
 189                              'expanded' => 0,
 190                          ]
 191                      ];
 192                  }
 193  
 194                  $added = false;
 195                  if ($category->parent == 0) {
 196                      // New categories root-level node.
 197                      $categoriesbranch[] = $newnode;
 198                      $added = true;
 199  
 200                  } else {
 201                      // Add the new node under the appropriate parent.
 202                      if ($this->add_to_parent_category_branch($category, $newnode, $categoriesbranch)) {
 203                          $added = true;
 204                      }
 205                  }
 206  
 207                  if ($added) {
 208                      unset($categories[$key]);
 209                  }
 210              }
 211          }
 212  
 213          return $categoriesbranch;
 214      }
 215  
 216      /**
 217       * Gets the courses branch for the provided category.
 218       *
 219       * @param \context $catcontext
 220       * @return array
 221       */
 222      public static function get_courses_branch(\context $catcontext) {
 223  
 224          if ($catcontext->contextlevel !== CONTEXT_COURSECAT) {
 225              throw new \coding_exception('A course category context should be provided');
 226          }
 227  
 228          $coursecat = \core_course_category::get($catcontext->instanceid);
 229          $courses = $coursecat->get_courses();
 230  
 231          $branches = [];
 232  
 233          foreach ($courses as $course) {
 234  
 235              $coursecontext = \context_course::instance($course->id);
 236  
 237              $coursenode = [
 238                  'text' => shorten_text(format_string($course->shortname, true, ['context' => $coursecontext])),
 239                  'contextid' => $coursecontext->id,
 240                  'branches' => [
 241                      [
 242                          'text' => get_string('activitiesandresources', 'tool_dataprivacy'),
 243                          'expandcontextid' => $coursecontext->id,
 244                          'expandelement' => 'module',
 245                          'expanded' => 0,
 246                      ], [
 247                          'text' => get_string('blocks'),
 248                          'expandcontextid' => $coursecontext->id,
 249                          'expandelement' => 'block',
 250                          'expanded' => 0,
 251                      ],
 252                  ]
 253              ];
 254              $branches[] = self::complete($coursenode);
 255          }
 256  
 257          return $branches;
 258      }
 259  
 260      /**
 261       * Gets the modules branch for the provided course.
 262       *
 263       * @param \context $coursecontext
 264       * @return array
 265       */
 266      public static function get_modules_branch(\context $coursecontext) {
 267  
 268          if ($coursecontext->contextlevel !== CONTEXT_COURSE) {
 269              throw new \coding_exception('A course context should be provided');
 270          }
 271  
 272          $branches = [];
 273  
 274          // Using the current user.
 275          $modinfo = get_fast_modinfo($coursecontext->instanceid);
 276          foreach ($modinfo->get_instances() as $moduletype => $instances) {
 277              foreach ($instances as $cm) {
 278  
 279                  if (!$cm->uservisible) {
 280                      continue;
 281                  }
 282  
 283                  $a = (object)[
 284                      'instancename' => shorten_text($cm->get_formatted_name()),
 285                      'modulename' => get_string('pluginname', 'mod_' . $moduletype),
 286                  ];
 287  
 288                  $text = get_string('moduleinstancename', 'tool_dataprivacy', $a);
 289                  $branches[] = self::complete([
 290                      'text' => $text,
 291                      'contextid' => $cm->context->id,
 292                  ]);
 293              }
 294          }
 295  
 296          return $branches;
 297      }
 298  
 299      /**
 300       * Gets the blocks branch for the provided course.
 301       *
 302       * @param \context $coursecontext
 303       * @return null
 304       */
 305      public static function get_blocks_branch(\context $coursecontext) {
 306          global $DB;
 307  
 308          if ($coursecontext->contextlevel !== CONTEXT_COURSE) {
 309              throw new \coding_exception('A course context should be provided');
 310          }
 311  
 312          $branches = [];
 313  
 314          $children = $coursecontext->get_child_contexts();
 315          foreach ($children as $childcontext) {
 316  
 317              if ($childcontext->contextlevel !== CONTEXT_BLOCK) {
 318                  continue;
 319              }
 320  
 321              $blockinstance = block_instance_by_id($childcontext->instanceid);
 322              $displayname = shorten_text(format_string($blockinstance->get_title(), true, ['context' => $childcontext]));
 323              $branches[] = self::complete([
 324                  'text' => $displayname,
 325                  'contextid' => $childcontext->id,
 326              ]);
 327  
 328          }
 329  
 330          return $branches;
 331      }
 332  
 333      /**
 334       * Adds the provided category to the categories branch.
 335       *
 336       * @param stdClass $category
 337       * @param array $newnode
 338       * @param array $categoriesbranch
 339       * @return bool
 340       */
 341      private function add_to_parent_category_branch($category, $newnode, &$categoriesbranch) {
 342  
 343          foreach ($categoriesbranch as $key => $branch) {
 344              if (!empty($branch['categoryid']) && $branch['categoryid'] == $category->parent) {
 345                  // It may be empty (if it does not contain courses and this is the first child cat).
 346                  if (!isset($categoriesbranch[$key]['branches'])) {
 347                      $categoriesbranch[$key]['branches'] = [];
 348                  }
 349                  $categoriesbranch[$key]['branches'][] = $newnode;
 350                  return true;
 351              }
 352              if (!empty($branch['branches'])) {
 353                  $parent = $this->add_to_parent_category_branch($category, $newnode, $categoriesbranch[$key]['branches']);
 354                  if ($parent) {
 355                      return true;
 356                  }
 357              }
 358          }
 359  
 360          return false;
 361      }
 362  
 363      /**
 364       * Completes tree nodes with default values.
 365       *
 366       * @param array $node
 367       * @param int|false $currentcontextlevel
 368       * @param int|false $currentcontextid
 369       * @return array
 370       */
 371      private static function complete($node, $currentcontextlevel = false, $currentcontextid = false) {
 372          if (!isset($node['active'])) {
 373              if ($currentcontextlevel && !empty($node['contextlevel']) &&
 374                      $currentcontextlevel == $node['contextlevel'] &&
 375                      empty($currentcontextid)) {
 376                  // This is the active context level, we also checked that there
 377                  // is no default contextid set.
 378                  $node['active'] = true;
 379              } else if ($currentcontextid && !empty($node['contextid']) &&
 380                      $currentcontextid == $node['contextid']) {
 381                  $node['active'] = true;
 382              } else {
 383                  $node['active'] = null;
 384              }
 385          }
 386  
 387          if (!isset($node['branches'])) {
 388              $node['branches'] = [];
 389          } else {
 390              foreach ($node['branches'] as $key => $childnode) {
 391                  $node['branches'][$key] = self::complete($childnode, $currentcontextlevel, $currentcontextid);
 392              }
 393          }
 394  
 395          if (!isset($node['expandelement'])) {
 396              $node['expandelement'] = null;
 397          }
 398  
 399          if (!isset($node['expandcontextid'])) {
 400              $node['expandcontextid'] = null;
 401          }
 402  
 403          if (!isset($node['contextid'])) {
 404              $node['contextid'] = null;
 405          }
 406  
 407          if (!isset($node['contextlevel'])) {
 408              $node['contextlevel'] = null;
 409          }
 410  
 411          if (!isset($node['expanded'])) {
 412              if (!empty($node['branches'])) {
 413                  $node['expanded'] = 1;
 414              } else {
 415                  $node['expanded'] = 0;
 416              }
 417          }
 418          return $node;
 419      }
 420  
 421      /**
 422       * From a list of purpose persistents to a list of id => name purposes.
 423       *
 424       * @param \tool_dataprivacy\purpose[] $purposes
 425       * @param bool $includenotset
 426       * @param bool $includeinherit
 427       * @return string[]
 428       */
 429      public static function purpose_options($purposes, $includenotset = true, $includeinherit = true) {
 430          $options = self::base_options($includenotset, $includeinherit);
 431          foreach ($purposes as $purpose) {
 432              $options[$purpose->get('id')] = $purpose->get('name');
 433          }
 434  
 435          return $options;
 436      }
 437  
 438      /**
 439       * From a list of category persistents to a list of id => name categories.
 440       *
 441       * @param \tool_dataprivacy\category[] $categories
 442       * @param bool $includenotset
 443       * @param bool $includeinherit
 444       * @return string[]
 445       */
 446      public static function category_options($categories, $includenotset = true, $includeinherit = true) {
 447          $options = self::base_options($includenotset, $includeinherit);
 448          foreach ($categories as $category) {
 449              $options[$category->get('id')] = $category->get('name');
 450          }
 451  
 452          return $options;
 453      }
 454  
 455      /**
 456       * Base not set and inherit options.
 457       *
 458       * @param bool $includenotset
 459       * @param bool $includeinherit
 460       * @return array
 461       */
 462      private static function base_options($includenotset = true, $includeinherit = true) {
 463  
 464          $options = [];
 465  
 466          if ($includenotset) {
 467              $options[\tool_dataprivacy\context_instance::NOTSET] = get_string('notset', 'tool_dataprivacy');
 468          }
 469  
 470          if ($includeinherit) {
 471              $options[\tool_dataprivacy\context_instance::INHERIT] = get_string('inherit', 'tool_dataprivacy');
 472          }
 473  
 474          return $options;
 475      }
 476  }