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 311 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   * Unit tests for the block_html implementation of the privacy API.
  19   *
  20   * @package    block_html
  21   * @category   test
  22   * @copyright  2018 Andrew Nicols <andrew@nicols.co.uk>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  namespace block_html\privacy;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  use core_privacy\local\request\writer;
  30  use core_privacy\local\request\approved_contextlist;
  31  use core_privacy\local\request\approved_userlist;
  32  use block_html\privacy\provider;
  33  
  34  /**
  35   * Unit tests for the block_html implementation of the privacy API.
  36   *
  37   * @copyright  2018 Andrew Nicols <andrew@nicols.co.uk>
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class provider_test extends \core_privacy\tests\provider_testcase {
  41      /**
  42       * Get the list of standard format options for comparison.
  43       *
  44       * @return \stdClass
  45       */
  46      protected function get_format_options() {
  47          return (object) [
  48              'overflowdiv' => true,
  49              'noclean' => true,
  50          ];
  51      }
  52  
  53      /**
  54       * Creates an HTML block on a user.
  55       *
  56       * @param   string  $title
  57       * @param   string  $body
  58       * @param   string  $format
  59       * @return  \block_instance
  60       */
  61      protected function create_user_block($title, $body, $format) {
  62          global $USER;
  63  
  64          $configdata = (object) [
  65              'title' => $title,
  66              'text' => [
  67                  'itemid' => 19,
  68                  'text' => $body,
  69                  'format' => $format,
  70              ],
  71          ];
  72  
  73          $this->create_block($this->construct_user_page($USER));
  74          $block = $this->get_last_block_on_page($this->construct_user_page($USER));
  75          $block = block_instance('html', $block->instance);
  76          $block->instance_config_save((object) $configdata);
  77  
  78          return $block;
  79      }
  80  
  81      /**
  82       * Creates an HTML block on a course.
  83       *
  84       * @param   \stdClass $course
  85       * @param   string  $title
  86       * @param   string  $body
  87       * @param   string  $format
  88       * @return  \block_instance
  89       */
  90      protected function create_course_block($course, $title, $body, $format) {
  91          global $USER;
  92  
  93          $configdata = (object) [
  94              'title' => $title,
  95              'text' => [
  96                  'itemid' => 19,
  97                  'text' => $body,
  98                  'format' => $format,
  99              ],
 100          ];
 101  
 102          $this->create_block($this->construct_course_page($course));
 103          $block = $this->get_last_block_on_page($this->construct_course_page($course));
 104          $block = block_instance('html', $block->instance);
 105          $block->instance_config_save((object) $configdata);
 106  
 107          return $block;
 108      }
 109  
 110      /**
 111       * Creates an HTML block on a page.
 112       *
 113       * @param \page $page Page
 114       */
 115      protected function create_block($page) {
 116          $page->blocks->add_block_at_end_of_default_region('html');
 117      }
 118  
 119      /**
 120       * Get the last block on the page.
 121       *
 122       * @param \page $page Page
 123       * @return \block_html Block instance object
 124       */
 125      protected function get_last_block_on_page($page) {
 126          $blocks = $page->blocks->get_blocks_for_region($page->blocks->get_default_region());
 127          $block = end($blocks);
 128  
 129          return $block;
 130      }
 131  
 132      /**
 133       * Constructs a Page object for the User Dashboard.
 134       *
 135       * @param   \stdClass       $user User to create Dashboard for.
 136       * @return  \moodle_page
 137       */
 138      protected function construct_user_page(\stdClass $user) {
 139          $page = new \moodle_page();
 140          $page->set_context(\context_user::instance($user->id));
 141          $page->set_pagelayout('mydashboard');
 142          $page->set_pagetype('my-index');
 143          $page->blocks->load_blocks();
 144          return $page;
 145      }
 146  
 147      /**
 148       * Constructs a Page object for the User Dashboard.
 149       *
 150       * @param   \stdClass       $course Course to create Dashboard for.
 151       * @return  \moodle_page
 152       */
 153      protected function construct_course_page(\stdClass $course) {
 154          $page = new \moodle_page();
 155          $page->set_context(\context_course::instance($course->id));
 156          $page->set_pagelayout('standard');
 157          $page->set_pagetype('course-view');
 158          $page->set_course($course);
 159          $page->blocks->load_blocks();
 160          return $page;
 161      }
 162  
 163      /**
 164       * Test that a block on the dashboard is exported.
 165       */
 166      public function test_user_block() {
 167          $this->resetAfterTest();
 168  
 169          $title = 'Example title';
 170          $content = 'Example content';
 171          $format = FORMAT_PLAIN;
 172  
 173          // Test setup.
 174          $user = $this->getDataGenerator()->create_user();
 175          $this->setUser($user);
 176          $block = $this->create_user_block($title, $content, $format);
 177          $context = \context_block::instance($block->instance->id);
 178  
 179          // Get the contexts.
 180          $contextlist = provider::get_contexts_for_userid($user->id);
 181  
 182          // Only the user context should be returned.
 183          $this->assertCount(1, $contextlist);
 184          $this->assertEquals($context, $contextlist->current());
 185  
 186          // Export the data.
 187          $this->export_context_data_for_user($user->id, $context, 'block_html');
 188          /** @var \core_privacy\tests\request\content_writer $writer */
 189          $writer = \core_privacy\local\request\writer::with_context($context);
 190          $this->assertTrue($writer->has_any_data());
 191  
 192          // Check the data.
 193          $data = $writer->get_data([]);
 194          $this->assertInstanceOf('stdClass', $data);
 195          $this->assertEquals($title, $data->title);
 196          $this->assertEquals(format_text($content, $format, $this->get_format_options()), $data->content);
 197  
 198          // Delete the context.
 199          provider::delete_data_for_all_users_in_context($context);
 200  
 201          // Re-fetch the contexts - it should no longer be returned.
 202          $contextlist = provider::get_contexts_for_userid($user->id);
 203          $this->assertCount(0, $contextlist);
 204      }
 205  
 206      /**
 207       * Test that a block on the dashboard which is not configured is _not_ exported.
 208       */
 209      public function test_user_block_unconfigured() {
 210          global $DB;
 211  
 212          $this->resetAfterTest();
 213  
 214          $title = 'Example title';
 215          $content = 'Example content';
 216          $format = FORMAT_PLAIN;
 217  
 218          // Test setup.
 219          $user = $this->getDataGenerator()->create_user();
 220          $this->setUser($user);
 221          $block = $this->create_user_block($title, $content, $format);
 222          $block->instance->configdata = '';
 223          $DB->update_record('block_instances', $block->instance);
 224          $block = block_instance('html', $block->instance);
 225  
 226          $context = \context_block::instance($block->instance->id);
 227  
 228          // Get the contexts.
 229          $contextlist = provider::get_contexts_for_userid($user->id);
 230  
 231          // Only the user context should be returned.
 232          $this->assertCount(1, $contextlist);
 233          $this->assertEquals($context, $contextlist->current());
 234  
 235          // Export the data.
 236          $this->export_context_data_for_user($user->id, $context, 'block_html');
 237          /** @var \core_privacy\tests\request\content_writer $writer */
 238          $writer = \core_privacy\local\request\writer::with_context($context);
 239          $this->assertFalse($writer->has_any_data());
 240      }
 241  
 242      /**
 243       * Test that a block on the dashboard is exported.
 244       */
 245      public function test_user_multiple_blocks_exported() {
 246          $this->resetAfterTest();
 247  
 248          $title = 'Example title';
 249          $content = 'Example content';
 250          $format = FORMAT_PLAIN;
 251  
 252          // Test setup.
 253          $blocks = [];
 254          $contexts = [];
 255          $user = $this->getDataGenerator()->create_user();
 256          $this->setUser($user);
 257  
 258          $block = $this->create_user_block($title, $content, $format);
 259          $context = \context_block::instance($block->instance->id);
 260          $contexts[$context->id] = $context;
 261  
 262          $block = $this->create_user_block($title, $content, $format);
 263          $context = \context_block::instance($block->instance->id);
 264          $contexts[$context->id] = $context;
 265  
 266          // Get the contexts.
 267          $contextlist = provider::get_contexts_for_userid($user->id);
 268  
 269          // There are now two blocks on the user context.
 270          $this->assertCount(2, $contextlist);
 271          foreach ($contextlist as $context) {
 272              $this->assertTrue(isset($contexts[$context->id]));
 273          }
 274  
 275          // Turn them into an approved_contextlist.
 276          $approvedlist = new approved_contextlist($user, 'block_html', $contextlist->get_contextids());
 277  
 278          // Delete using delete_data_for_user.
 279          provider::delete_data_for_user($approvedlist);
 280  
 281          // Re-fetch the contexts - it should no longer be returned.
 282          $contextlist = provider::get_contexts_for_userid($user->id);
 283          $this->assertCount(0, $contextlist);
 284      }
 285  
 286      /**
 287       * Test that a block on the dashboard is not exported.
 288       */
 289      public function test_course_blocks_not_exported() {
 290          $this->resetAfterTest();
 291  
 292          $title = 'Example title';
 293          $content = 'Example content';
 294          $format = FORMAT_PLAIN;
 295  
 296          // Test setup.
 297          $user = $this->getDataGenerator()->create_user();
 298          $course = $this->getDataGenerator()->create_course();
 299          $this->setUser($user);
 300  
 301          $block = $this->create_course_block($course, $title, $content, $format);
 302          $context = \context_block::instance($block->instance->id);
 303  
 304          // Get the contexts.
 305          $contextlist = provider::get_contexts_for_userid($user->id);
 306  
 307          // No blocks should be returned.
 308          $this->assertCount(0, $contextlist);
 309      }
 310  
 311      /**
 312       * Test that a block on the dashboard is exported.
 313       */
 314      public function test_mixed_multiple_blocks_exported() {
 315          $this->resetAfterTest();
 316  
 317          $title = 'Example title';
 318          $content = 'Example content';
 319          $format = FORMAT_PLAIN;
 320  
 321          // Test setup.
 322          $contexts = [];
 323  
 324          $user = $this->getDataGenerator()->create_user();
 325          $course = $this->getDataGenerator()->create_course();
 326          $this->setUser($user);
 327  
 328          $block = $this->create_course_block($course, $title, $content, $format);
 329          $context = \context_block::instance($block->instance->id);
 330  
 331          $block = $this->create_user_block($title, $content, $format);
 332          $context = \context_block::instance($block->instance->id);
 333          $contexts[$context->id] = $context;
 334  
 335          $block = $this->create_user_block($title, $content, $format);
 336          $context = \context_block::instance($block->instance->id);
 337          $contexts[$context->id] = $context;
 338  
 339          // Get the contexts.
 340          $contextlist = provider::get_contexts_for_userid($user->id);
 341  
 342          // There are now two blocks on the user context.
 343          $this->assertCount(2, $contextlist);
 344          foreach ($contextlist as $context) {
 345              $this->assertTrue(isset($contexts[$context->id]));
 346          }
 347      }
 348  
 349      /**
 350       * Test that only users with a user context HTML block are fetched.
 351       */
 352      public function test_get_users_in_context() {
 353          $this->resetAfterTest();
 354  
 355          $component = 'block_html';
 356          $title = 'Block title';
 357          $content = 'Block content';
 358          $blockformat = FORMAT_PLAIN;
 359  
 360          // Create a user with a user context HTML block.
 361          $user1 = $this->getDataGenerator()->create_user();
 362          $this->setUser($user1);
 363  
 364          $userblock = $this->create_user_block($title, $content, $blockformat);
 365          $usercontext = \context_block::instance($userblock->instance->id);
 366  
 367          // Create a user with a course context HTML block.
 368          $user2 = $this->getDataGenerator()->create_user();
 369          $this->setUser($user2);
 370  
 371          $course = $this->getDataGenerator()->create_course();
 372          $courseblock = $this->create_course_block($course, $title, $content, $blockformat);
 373          $coursecontext = \context_block::instance($courseblock->instance->id);
 374  
 375          // Ensure only the user with a user context HTML block is returned.
 376          $userlist = new \core_privacy\local\request\userlist($usercontext, $component);
 377          \block_html\privacy\provider::get_users_in_context($userlist);
 378  
 379          $this->assertCount(1, $userlist);
 380  
 381          $expected = [$user1->id];
 382          $actual = $userlist->get_userids();
 383  
 384          $this->assertEquals($expected, $actual);
 385  
 386          // Ensure passing the course context returns no users.
 387          $userlist = new \core_privacy\local\request\userlist($coursecontext, $component);
 388          \mod_forum\privacy\provider::get_users_in_context($userlist);
 389          $this->assertEmpty($userlist);
 390      }
 391  
 392      /**
 393       * Test that data for users in approved userlist is deleted.
 394       */
 395      public function test_delete_data_for_users() {
 396          $this->resetAfterTest();
 397  
 398          $component = 'block_html';
 399          $title = 'Block title';
 400          $content = 'Block content';
 401          $blockformat = FORMAT_PLAIN;
 402  
 403          // Create 2 user swith a user context HTML blocks.
 404          $user1 = $this->getDataGenerator()->create_user();
 405          $this->setUser($user1);
 406  
 407          $block1 = $this->create_user_block($title, $content, $blockformat);
 408          $context1 = \context_block::instance($block1->instance->id);
 409  
 410          $user2 = $this->getDataGenerator()->create_user();
 411          $this->setUser($user2);
 412          $block2 = $this->create_user_block($title, $content, $blockformat);
 413          $context2 = \context_block::instance($block2->instance->id);
 414  
 415          // Create and populate the userlists.
 416          $userlist1 = new \core_privacy\local\request\userlist($context1, $component);
 417          \block_html\privacy\provider::get_users_in_context($userlist1);
 418          $userlist2 = new \core_privacy\local\request\userlist($context2, $component);
 419          \block_html\privacy\provider::get_users_in_context($userlist2);
 420  
 421          // Ensure both members are included.
 422          $this->assertCount(1, $userlist1);
 423          $this->assertCount(1, $userlist2);
 424  
 425          // Convert $userlist1 into an approved_contextlist.
 426          $approvedlist = new approved_userlist($context1, 'block_html', $userlist1->get_userids());
 427  
 428          // Delete using delete_data_for_user.
 429          provider::delete_data_for_users($approvedlist);
 430  
 431          // Re-fetch users in the contexts - only the first one should now be empty.
 432          $userlist1 = new \core_privacy\local\request\userlist($context1, $component);
 433          \block_html\privacy\provider::get_users_in_context($userlist1);
 434          $this->assertCount(0, $userlist1);
 435  
 436          $userlist2 = new \core_privacy\local\request\userlist($context2, $component);
 437          \block_html\privacy\provider::get_users_in_context($userlist2);
 438          $this->assertCount(1, $userlist2);
 439      }
 440  }