Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 400 and 402] [Versions 400 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   * 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          $writer = \core_privacy\local\request\writer::with_context($context);
 189          $this->assertTrue($writer->has_any_data());
 190  
 191          // Check the data.
 192          $data = $writer->get_data([]);
 193          $this->assertInstanceOf('stdClass', $data);
 194          $this->assertEquals($title, $data->title);
 195          $this->assertEquals(format_text($content, $format, $this->get_format_options()), $data->content);
 196  
 197          // Delete the context.
 198          provider::delete_data_for_all_users_in_context($context);
 199  
 200          // Re-fetch the contexts - it should no longer be returned.
 201          $contextlist = provider::get_contexts_for_userid($user->id);
 202          $this->assertCount(0, $contextlist);
 203      }
 204  
 205      /**
 206       * Test that a block on the dashboard which is not configured is _not_ exported.
 207       */
 208      public function test_user_block_unconfigured() {
 209          global $DB;
 210  
 211          $this->resetAfterTest();
 212  
 213          $title = 'Example title';
 214          $content = 'Example content';
 215          $format = FORMAT_PLAIN;
 216  
 217          // Test setup.
 218          $user = $this->getDataGenerator()->create_user();
 219          $this->setUser($user);
 220          $block = $this->create_user_block($title, $content, $format);
 221          $block->instance->configdata = '';
 222          $DB->update_record('block_instances', $block->instance);
 223          $block = block_instance('html', $block->instance);
 224  
 225          $context = \context_block::instance($block->instance->id);
 226  
 227          // Get the contexts.
 228          $contextlist = provider::get_contexts_for_userid($user->id);
 229  
 230          // Only the user context should be returned.
 231          $this->assertCount(1, $contextlist);
 232          $this->assertEquals($context, $contextlist->current());
 233  
 234          // Export the data.
 235          $this->export_context_data_for_user($user->id, $context, 'block_html');
 236          $writer = \core_privacy\local\request\writer::with_context($context);
 237          $this->assertFalse($writer->has_any_data());
 238      }
 239  
 240      /**
 241       * Test that a block on the dashboard is exported.
 242       */
 243      public function test_user_multiple_blocks_exported() {
 244          $this->resetAfterTest();
 245  
 246          $title = 'Example title';
 247          $content = 'Example content';
 248          $format = FORMAT_PLAIN;
 249  
 250          // Test setup.
 251          $blocks = [];
 252          $contexts = [];
 253          $user = $this->getDataGenerator()->create_user();
 254          $this->setUser($user);
 255  
 256          $block = $this->create_user_block($title, $content, $format);
 257          $context = \context_block::instance($block->instance->id);
 258          $contexts[$context->id] = $context;
 259  
 260          $block = $this->create_user_block($title, $content, $format);
 261          $context = \context_block::instance($block->instance->id);
 262          $contexts[$context->id] = $context;
 263  
 264          // Get the contexts.
 265          $contextlist = provider::get_contexts_for_userid($user->id);
 266  
 267          // There are now two blocks on the user context.
 268          $this->assertCount(2, $contextlist);
 269          foreach ($contextlist as $context) {
 270              $this->assertTrue(isset($contexts[$context->id]));
 271          }
 272  
 273          // Turn them into an approved_contextlist.
 274          $approvedlist = new approved_contextlist($user, 'block_html', $contextlist->get_contextids());
 275  
 276          // Delete using delete_data_for_user.
 277          provider::delete_data_for_user($approvedlist);
 278  
 279          // Re-fetch the contexts - it should no longer be returned.
 280          $contextlist = provider::get_contexts_for_userid($user->id);
 281          $this->assertCount(0, $contextlist);
 282      }
 283  
 284      /**
 285       * Test that a block on the dashboard is not exported.
 286       */
 287      public function test_course_blocks_not_exported() {
 288          $this->resetAfterTest();
 289  
 290          $title = 'Example title';
 291          $content = 'Example content';
 292          $format = FORMAT_PLAIN;
 293  
 294          // Test setup.
 295          $user = $this->getDataGenerator()->create_user();
 296          $course = $this->getDataGenerator()->create_course();
 297          $this->setUser($user);
 298  
 299          $block = $this->create_course_block($course, $title, $content, $format);
 300          $context = \context_block::instance($block->instance->id);
 301  
 302          // Get the contexts.
 303          $contextlist = provider::get_contexts_for_userid($user->id);
 304  
 305          // No blocks should be returned.
 306          $this->assertCount(0, $contextlist);
 307      }
 308  
 309      /**
 310       * Test that a block on the dashboard is exported.
 311       */
 312      public function test_mixed_multiple_blocks_exported() {
 313          $this->resetAfterTest();
 314  
 315          $title = 'Example title';
 316          $content = 'Example content';
 317          $format = FORMAT_PLAIN;
 318  
 319          // Test setup.
 320          $contexts = [];
 321  
 322          $user = $this->getDataGenerator()->create_user();
 323          $course = $this->getDataGenerator()->create_course();
 324          $this->setUser($user);
 325  
 326          $block = $this->create_course_block($course, $title, $content, $format);
 327          $context = \context_block::instance($block->instance->id);
 328  
 329          $block = $this->create_user_block($title, $content, $format);
 330          $context = \context_block::instance($block->instance->id);
 331          $contexts[$context->id] = $context;
 332  
 333          $block = $this->create_user_block($title, $content, $format);
 334          $context = \context_block::instance($block->instance->id);
 335          $contexts[$context->id] = $context;
 336  
 337          // Get the contexts.
 338          $contextlist = provider::get_contexts_for_userid($user->id);
 339  
 340          // There are now two blocks on the user context.
 341          $this->assertCount(2, $contextlist);
 342          foreach ($contextlist as $context) {
 343              $this->assertTrue(isset($contexts[$context->id]));
 344          }
 345      }
 346  
 347      /**
 348       * Test that only users with a user context HTML block are fetched.
 349       */
 350      public function test_get_users_in_context() {
 351          $this->resetAfterTest();
 352  
 353          $component = 'block_html';
 354          $title = 'Block title';
 355          $content = 'Block content';
 356          $blockformat = FORMAT_PLAIN;
 357  
 358          // Create a user with a user context HTML block.
 359          $user1 = $this->getDataGenerator()->create_user();
 360          $this->setUser($user1);
 361  
 362          $userblock = $this->create_user_block($title, $content, $blockformat);
 363          $usercontext = \context_block::instance($userblock->instance->id);
 364  
 365          // Create a user with a course context HTML block.
 366          $user2 = $this->getDataGenerator()->create_user();
 367          $this->setUser($user2);
 368  
 369          $course = $this->getDataGenerator()->create_course();
 370          $courseblock = $this->create_course_block($course, $title, $content, $blockformat);
 371          $coursecontext = \context_block::instance($courseblock->instance->id);
 372  
 373          // Ensure only the user with a user context HTML block is returned.
 374          $userlist = new \core_privacy\local\request\userlist($usercontext, $component);
 375          \block_html\privacy\provider::get_users_in_context($userlist);
 376  
 377          $this->assertCount(1, $userlist);
 378  
 379          $expected = [$user1->id];
 380          $actual = $userlist->get_userids();
 381  
 382          $this->assertEquals($expected, $actual);
 383  
 384          // Ensure passing the course context returns no users.
 385          $userlist = new \core_privacy\local\request\userlist($coursecontext, $component);
 386          \mod_forum\privacy\provider::get_users_in_context($userlist);
 387          $this->assertEmpty($userlist);
 388      }
 389  
 390      /**
 391       * Test that data for users in approved userlist is deleted.
 392       */
 393      public function test_delete_data_for_users() {
 394          $this->resetAfterTest();
 395  
 396          $component = 'block_html';
 397          $title = 'Block title';
 398          $content = 'Block content';
 399          $blockformat = FORMAT_PLAIN;
 400  
 401          // Create 2 user swith a user context HTML blocks.
 402          $user1 = $this->getDataGenerator()->create_user();
 403          $this->setUser($user1);
 404  
 405          $block1 = $this->create_user_block($title, $content, $blockformat);
 406          $context1 = \context_block::instance($block1->instance->id);
 407  
 408          $user2 = $this->getDataGenerator()->create_user();
 409          $this->setUser($user2);
 410          $block2 = $this->create_user_block($title, $content, $blockformat);
 411          $context2 = \context_block::instance($block2->instance->id);
 412  
 413          // Create and populate the userlists.
 414          $userlist1 = new \core_privacy\local\request\userlist($context1, $component);
 415          \block_html\privacy\provider::get_users_in_context($userlist1);
 416          $userlist2 = new \core_privacy\local\request\userlist($context2, $component);
 417          \block_html\privacy\provider::get_users_in_context($userlist2);
 418  
 419          // Ensure both members are included.
 420          $this->assertCount(1, $userlist1);
 421          $this->assertCount(1, $userlist2);
 422  
 423          // Convert $userlist1 into an approved_contextlist.
 424          $approvedlist = new approved_userlist($context1, 'block_html', $userlist1->get_userids());
 425  
 426          // Delete using delete_data_for_user.
 427          provider::delete_data_for_users($approvedlist);
 428  
 429          // Re-fetch users in the contexts - only the first one should now be empty.
 430          $userlist1 = new \core_privacy\local\request\userlist($context1, $component);
 431          \block_html\privacy\provider::get_users_in_context($userlist1);
 432          $this->assertCount(0, $userlist1);
 433  
 434          $userlist2 = new \core_privacy\local\request\userlist($context2, $component);
 435          \block_html\privacy\provider::get_users_in_context($userlist2);
 436          $this->assertCount(1, $userlist2);
 437      }
 438  }