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.

Differences Between: [Versions 311 and 403] [Versions 400 and 403] [Versions 401 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   * Privacy provider tests.
  19   *
  20   * @package    block_comments
  21   * @copyright  2018 Shamim Rezaie <shamim@moodle.com>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  namespace block_comments\privacy;
  25  
  26  use core_privacy\local\metadata\collection;
  27  use block_comments\privacy\provider;
  28  use core_privacy\local\request\approved_userlist;
  29  use stdClass;
  30  
  31  defined('MOODLE_INTERNAL') || die();
  32  
  33  /**
  34   * Privacy provider test for block_comments.
  35   *
  36   * @copyright  2018 Shamim Rezaie <shamim@moodle.com>
  37   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38   */
  39  class provider_test extends \core_privacy\tests\provider_testcase {
  40  
  41      /** @var stdClass A student who is only enrolled in course1. */
  42      protected $student1;
  43  
  44      /** @var stdClass A student who is only enrolled in course2. */
  45      protected $student2;
  46  
  47      /** @var stdClass A student who is enrolled in both course1 and course2. */
  48      protected $student12;
  49  
  50      /** @var stdClass A test course. */
  51      protected $course1;
  52  
  53      /** @var stdClass A test course. */
  54      protected $course2;
  55  
  56      protected function setUp(): void {
  57          global $DB;
  58  
  59          $this->resetAfterTest();
  60          $this->setAdminUser();
  61  
  62          // Create courses.
  63          $generator = $this->getDataGenerator();
  64          $this->course1 = $generator->create_course();
  65          $this->course2 = $generator->create_course();
  66  
  67          // Create and enrol students.
  68          $this->student1 = $generator->create_user();
  69          $this->student2 = $generator->create_user();
  70          $this->student12 = $generator->create_user();
  71  
  72          $studentrole = $DB->get_record('role', ['shortname' => 'student']);
  73          $generator->enrol_user($this->student1->id,  $this->course1->id, $studentrole->id);
  74          $generator->enrol_user($this->student2->id,  $this->course2->id, $studentrole->id);
  75          $generator->enrol_user($this->student12->id,  $this->course1->id, $studentrole->id);
  76          $generator->enrol_user($this->student12->id,  $this->course2->id, $studentrole->id);
  77  
  78          // Comment block on course pages.
  79          $block = $this->add_comments_block_in_context(\context_course::instance($this->course1->id));
  80          $block = $this->add_comments_block_in_context(\context_course::instance($this->course2->id));
  81      }
  82  
  83      /**
  84       * Posts a comment on a given context.
  85       *
  86       * @param string $text The comment's text.
  87       * @param \context $context The context on which we want to put the comment.
  88       */
  89      protected function add_comment($text, \context $context) {
  90          $args = new \stdClass;
  91          $args->context = $context;
  92          $args->area = 'page_comments';
  93          $args->itemid = 0;
  94          $args->component = 'block_comments';
  95          $args->linktext = get_string('showcomments');
  96          $args->notoggle = true;
  97          $args->autostart = true;
  98          $args->displaycancel = false;
  99          $comment = new \comment($args);
 100  
 101          $comment->add($text);
 102      }
 103  
 104      /**
 105       * Creates a comments block on a context.
 106       *
 107       * @param \context $context The context on which we want to put the block.
 108       * @return \block_base The created block instance.
 109       * @throws \coding_exception
 110       */
 111      protected function add_comments_block_in_context(\context $context) {
 112          global $DB;
 113  
 114          $course = null;
 115  
 116          $page = new \moodle_page();
 117          $page->set_context($context);
 118  
 119          switch ($context->contextlevel) {
 120              case CONTEXT_SYSTEM:
 121                  $page->set_pagelayout('frontpage');
 122                  $page->set_pagetype('site-index');
 123                  break;
 124              case CONTEXT_COURSE:
 125                  $page->set_pagelayout('standard');
 126                  $page->set_pagetype('course-view');
 127                  $course = $DB->get_record('course', ['id' => $context->instanceid]);
 128                  $page->set_course($course);
 129                  break;
 130              case CONTEXT_MODULE:
 131                  $page->set_pagelayout('standard');
 132                  $mod = $DB->get_field_sql("SELECT m.name
 133                                               FROM {modules} m
 134                                               JOIN {course_modules} cm on cm.module = m.id
 135                                              WHERE cm.id = ?", [$context->instanceid]);
 136                  $page->set_pagetype("mod-$mod-view");
 137                  break;
 138              case CONTEXT_USER:
 139                  $page->set_pagelayout('mydashboard');
 140                  $page->set_pagetype('my-index');
 141                  break;
 142              default:
 143                  throw new coding_exception('Unsupported context for test');
 144          }
 145  
 146          $page->blocks->load_blocks();
 147  
 148          $page->blocks->add_block_at_end_of_default_region('comments');
 149  
 150          // We need to use another page object as load_blocks() only loads the blocks once.
 151          $page2 = new \moodle_page();
 152          $page2->set_context($page->context);
 153          $page2->set_pagelayout($page->pagelayout);
 154          $page2->set_pagetype($page->pagetype);
 155          if ($course) {
 156              $page2->set_course($course);
 157          }
 158  
 159          $page->blocks->load_blocks();
 160          $page2->blocks->load_blocks();
 161          $blocks = $page2->blocks->get_blocks_for_region($page2->blocks->get_default_region());
 162          $block = end($blocks);
 163  
 164          $block = block_instance('comments', $block->instance);
 165  
 166          return $block;
 167      }
 168  
 169      /**
 170       * Test for provider::get_metadata().
 171       */
 172      public function test_get_metadata() {
 173          $collection = new collection('block_comments');
 174          $newcollection = provider::get_metadata($collection);
 175          $itemcollection = $newcollection->get_collection();
 176          $this->assertCount(1, $itemcollection);
 177  
 178          $link = reset($itemcollection);
 179  
 180          $this->assertEquals('core_comment', $link->get_name());
 181          $this->assertEmpty($link->get_privacy_fields());
 182          $this->assertEquals('privacy:metadata:core_comment', $link->get_summary());
 183      }
 184  
 185      /**
 186       * Test for provider::get_contexts_for_userid() when user had not posted any comments..
 187       */
 188      public function test_get_contexts_for_userid_no_comment() {
 189          $this->setUser($this->student1);
 190          $coursecontext1 = \context_course::instance($this->course1->id);
 191          $this->add_comment('New comment', $coursecontext1);
 192  
 193          $this->setUser($this->student2);
 194          $contextlist = provider::get_contexts_for_userid($this->student2->id);
 195          $this->assertCount(0, $contextlist);
 196      }
 197  
 198      /**
 199       * Test for provider::get_contexts_for_userid().
 200       */
 201      public function test_get_contexts_for_userid() {
 202          $coursecontext1 = \context_course::instance($this->course1->id);
 203          $coursecontext2 = \context_course::instance($this->course2->id);
 204  
 205          $this->setUser($this->student12);
 206          $this->add_comment('New comment', $coursecontext1);
 207          $this->add_comment('New comment', $coursecontext1);
 208          $this->add_comment('New comment', $coursecontext2);
 209  
 210          $contextlist = provider::get_contexts_for_userid($this->student12->id);
 211          $this->assertCount(2, $contextlist);
 212  
 213          $contextids = $contextlist->get_contextids();
 214          $this->assertEqualsCanonicalizing([$coursecontext1->id, $coursecontext2->id], $contextids);
 215      }
 216  
 217      /**
 218       * Test for provider::export_user_data() when the user has not posted any comments.
 219       */
 220      public function test_export_for_context_no_comment() {
 221          $coursecontext1 = \context_course::instance($this->course1->id);
 222          $coursecontext2 = \context_course::instance($this->course2->id);
 223  
 224          $this->setUser($this->student1);
 225          $this->add_comment('New comment', $coursecontext1);
 226  
 227          $this->setUser($this->student2);
 228  
 229          $this->setUser($this->student2);
 230          $this->export_context_data_for_user($this->student2->id, $coursecontext2, 'block_comments');
 231          $writer = \core_privacy\local\request\writer::with_context($coursecontext2);
 232          $this->assertFalse($writer->has_any_data());
 233  
 234      }
 235  
 236      /**
 237       * Test for provider::export_user_data().
 238       */
 239      public function test_export_for_context() {
 240          $coursecontext1 = \context_course::instance($this->course1->id);
 241          $coursecontext2 = \context_course::instance($this->course2->id);
 242  
 243          $this->setUser($this->student12);
 244          $this->add_comment('New comment', $coursecontext1);
 245          $this->add_comment('New comment', $coursecontext1);
 246          $this->add_comment('New comment', $coursecontext2);
 247  
 248          // Export all of the data for the context.
 249          $this->export_context_data_for_user($this->student12->id, $coursecontext1, 'block_comments');
 250          $writer = \core_privacy\local\request\writer::with_context($coursecontext1);
 251          $this->assertTrue($writer->has_any_data());
 252      }
 253  
 254      /**
 255       * Test for provider::delete_data_for_all_users_in_context().
 256       */
 257      public function test_delete_data_for_all_users_in_context() {
 258          global $DB;
 259  
 260          $coursecontext1 = \context_course::instance($this->course1->id);
 261          $coursecontext2 = \context_course::instance($this->course2->id);
 262  
 263          $this->setUser($this->student1);
 264          $this->add_comment('New comment', $coursecontext1);
 265  
 266          $this->setUser($this->student2);
 267          $this->add_comment('New comment', $coursecontext2);
 268  
 269          $this->setUser($this->student12);
 270          $this->add_comment('New comment', $coursecontext1);
 271          $this->add_comment('New comment', $coursecontext1);
 272          $this->add_comment('New comment', $coursecontext2);
 273  
 274          // Before deletion, we should have 3 comments in $coursecontext1 and 2 comments in $coursecontext2.
 275          $this->assertEquals(
 276                  3,
 277                  $DB->count_records('comments', ['component' => 'block_comments', 'contextid' => $coursecontext1->id])
 278          );
 279          $this->assertEquals(
 280                  2,
 281                  $DB->count_records('comments', ['component' => 'block_comments', 'contextid' => $coursecontext2->id])
 282          );
 283  
 284          // Delete data based on context.
 285          provider::delete_data_for_all_users_in_context($coursecontext1);
 286  
 287          // After deletion, the comments for $coursecontext1 should have been deleted.
 288          $this->assertEquals(
 289                  0,
 290                  $DB->count_records('comments', ['component' => 'block_comments', 'contextid' => $coursecontext1->id])
 291          );
 292          $this->assertEquals(
 293                  2,
 294                  $DB->count_records('comments', ['component' => 'block_comments', 'contextid' => $coursecontext2->id])
 295          );
 296      }
 297  
 298      /**
 299       * Test for provider::delete_data_for_all_users_in_context() when there are also comments from other plugins.
 300       */
 301      public function test_delete_data_for_all_users_in_context_with_comments_from_other_plugins() {
 302          global $DB;
 303  
 304          $assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
 305          $instance = $assigngenerator->create_instance(['course' => $this->course1]);
 306          $cm = get_coursemodule_from_instance('assign', $instance->id);
 307          $assigncontext = \context_module::instance($cm->id);
 308          $assign = new \assign($assigncontext, $cm, $this->course1);
 309  
 310          // Add a comments block in the assignment page.
 311          $this->add_comments_block_in_context($assigncontext);
 312  
 313          $submission = $assign->get_user_submission($this->student1->id, true);
 314  
 315          $options = new \stdClass();
 316          $options->area = 'submission_comments';
 317          $options->course = $assign->get_course();
 318          $options->context = $assigncontext;
 319          $options->itemid = $submission->id;
 320          $options->component = 'assignsubmission_comments';
 321          $options->showcount = true;
 322          $options->displaycancel = true;
 323  
 324          $comment = new \comment($options);
 325          $comment->set_post_permission(true);
 326  
 327          $this->setUser($this->student1);
 328          $comment->add('Comment from student 1');
 329  
 330          $this->add_comment('New comment', $assigncontext);
 331  
 332          $this->setUser($this->student2);
 333          $this->add_comment('New comment', $assigncontext);
 334  
 335          // Before deletion, we should have 3 comments in $assigncontext.
 336          // One comment is for the assignment submission and 2 are for the comments block.
 337          $this->assertEquals(
 338                  3,
 339                  $DB->count_records('comments', ['contextid' => $assigncontext->id])
 340          );
 341          $this->assertEquals(
 342                  2,
 343                  $DB->count_records('comments', ['component' => 'block_comments', 'contextid' => $assigncontext->id])
 344          );
 345  
 346          provider::delete_data_for_all_users_in_context($assigncontext);
 347  
 348          // After deletion, the comments for $assigncontext in the comment block should have been deleted,
 349          // but the assignment submission comment should be left.
 350          $this->assertEquals(
 351                  1,
 352                  $DB->count_records('comments', ['contextid' => $assigncontext->id])
 353          );
 354          $this->assertEquals(
 355                  0,
 356                  $DB->count_records('comments', ['component' => 'block_comments', 'contextid' => $assigncontext->id])
 357          );
 358      }
 359  
 360      /**
 361       * Test for provider::delete_data_for_user().
 362       */
 363      public function test_delete_data_for_user() {
 364          global $DB;
 365  
 366          $coursecontext1 = \context_course::instance($this->course1->id);
 367          $coursecontext2 = \context_course::instance($this->course2->id);
 368  
 369          $this->setUser($this->student1);
 370          $this->add_comment('New comment', $coursecontext1);
 371  
 372          $this->setUser($this->student2);
 373          $this->add_comment('New comment', $coursecontext2);
 374  
 375          $this->setUser($this->student12);
 376          $this->add_comment('New comment', $coursecontext1);
 377          $this->add_comment('New comment', $coursecontext1);
 378          $this->add_comment('New comment', $coursecontext2);
 379  
 380          // Before deletion, we should have 3 comments in $coursecontext1 and 2 comments in $coursecontext2,
 381          // and 3 comments by student12 in $coursecontext1 and $coursecontext2 combined.
 382          $this->assertEquals(
 383                  3,
 384                  $DB->count_records('comments', ['component' => 'block_comments', 'contextid' => $coursecontext1->id])
 385          );
 386          $this->assertEquals(
 387                  2,
 388                  $DB->count_records('comments', ['component' => 'block_comments', 'contextid' => $coursecontext2->id])
 389          );
 390          $this->assertEquals(
 391                  3,
 392                  $DB->count_records('comments', ['component' => 'block_comments', 'userid' => $this->student12->id])
 393          );
 394  
 395          $contextlist = new \core_privacy\local\request\approved_contextlist($this->student12, 'block_comments',
 396                  [$coursecontext1->id, $coursecontext2->id]);
 397          provider::delete_data_for_user($contextlist);
 398  
 399          // After deletion, the comments for the student12 should have been deleted.
 400          $this->assertEquals(
 401                  1,
 402                  $DB->count_records('comments', ['component' => 'block_comments', 'contextid' => $coursecontext1->id])
 403          );
 404          $this->assertEquals(
 405                  1,
 406                  $DB->count_records('comments', ['component' => 'block_comments', 'contextid' => $coursecontext2->id])
 407          );
 408          $this->assertEquals(
 409                  0,
 410                  $DB->count_records('comments', ['component' => 'block_comments', 'userid' => $this->student12->id])
 411          );
 412      }
 413  
 414      /**
 415       * Test for provider::delete_data_for_user() when there are also comments from other plugins.
 416       */
 417      public function test_delete_data_for_user_with_comments_from_other_plugins() {
 418          global $DB;
 419  
 420          $assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
 421          $instance = $assigngenerator->create_instance(['course' => $this->course1]);
 422          $cm = get_coursemodule_from_instance('assign', $instance->id);
 423          $assigncontext = \context_module::instance($cm->id);
 424          $assign = new \assign($assigncontext, $cm, $this->course1);
 425  
 426          // Add a comments block in the assignment page.
 427          $this->add_comments_block_in_context($assigncontext);
 428  
 429          $submission = $assign->get_user_submission($this->student1->id, true);
 430  
 431          $options = new \stdClass();
 432          $options->area = 'submission_comments';
 433          $options->course = $assign->get_course();
 434          $options->context = $assigncontext;
 435          $options->itemid = $submission->id;
 436          $options->component = 'assignsubmission_comments';
 437          $options->showcount = true;
 438          $options->displaycancel = true;
 439  
 440          $comment = new \comment($options);
 441          $comment->set_post_permission(true);
 442  
 443          $this->setUser($this->student1);
 444          $comment->add('Comment from student 1');
 445  
 446          $this->add_comment('New comment', $assigncontext);
 447          $this->add_comment('New comment', $assigncontext);
 448  
 449          // Before deletion, we should have 3 comments in $assigncontext.
 450          // one comment is for the assignment submission and 2 are for the comments block.
 451          $this->assertEquals(
 452                  3,
 453                  $DB->count_records('comments', ['contextid' => $assigncontext->id])
 454          );
 455  
 456          $contextlist = new \core_privacy\local\request\approved_contextlist($this->student1, 'block_comments',
 457                  [$assigncontext->id]);
 458          provider::delete_data_for_user($contextlist);
 459  
 460          // After deletion, the comments for the student1 in the comment block should have been deleted,
 461          // but the assignment submission comment should be left.
 462          $this->assertEquals(
 463                  1,
 464                  $DB->count_records('comments', ['contextid' => $assigncontext->id])
 465          );
 466          $this->assertEquals(
 467                  0,
 468                  $DB->count_records('comments', ['component' => 'block_comments', 'userid' => $this->student1->id])
 469          );
 470      }
 471  
 472      /**
 473       * Test that only users within a course context are fetched.
 474       * @group qtesttt
 475       */
 476      public function test_get_users_in_context() {
 477          $component = 'block_comments';
 478  
 479          $coursecontext1 = \context_course::instance($this->course1->id);
 480          $coursecontext2 = \context_course::instance($this->course2->id);
 481  
 482          $userlist1 = new \core_privacy\local\request\userlist($coursecontext1, $component);
 483          provider::get_users_in_context($userlist1);
 484          $this->assertCount(0, $userlist1);
 485  
 486          $userlist2 = new \core_privacy\local\request\userlist($coursecontext2, $component);
 487          provider::get_users_in_context($userlist2);
 488          $this->assertCount(0, $userlist2);
 489  
 490          $this->setUser($this->student12);
 491          $this->add_comment('New comment', $coursecontext1);
 492          $this->add_comment('New comment', $coursecontext2);
 493          $this->setUser($this->student1);
 494          $this->add_comment('New comment', $coursecontext1);
 495  
 496          // The list of users should contain user12 and user1.
 497          provider::get_users_in_context($userlist1);
 498          $this->assertCount(2, $userlist1);
 499          $this->assertTrue(in_array($this->student1->id, $userlist1->get_userids()));
 500          $this->assertTrue(in_array($this->student12->id, $userlist1->get_userids()));
 501  
 502          // The list of users should contain user12.
 503          provider::get_users_in_context($userlist2);
 504          $this->assertCount(1, $userlist2);
 505          $expected = [$this->student12->id];
 506          $actual = $userlist2->get_userids();
 507          $this->assertEquals($expected, $actual);
 508      }
 509  
 510      /**
 511       * Test that data for users in approved userlist is deleted.
 512       */
 513      public function test_delete_data_for_users() {
 514          $component = 'block_comments';
 515  
 516          $coursecontext1 = \context_course::instance($this->course1->id);
 517          $coursecontext2 = \context_course::instance($this->course2->id);
 518  
 519          $this->setUser($this->student12);
 520          $this->add_comment('New comment', $coursecontext1);
 521          $this->add_comment('New comment', $coursecontext2);
 522          $this->setUser($this->student1);
 523          $this->add_comment('New comment', $coursecontext1);
 524  
 525          $userlist1 = new \core_privacy\local\request\userlist($coursecontext1, $component);
 526          provider::get_users_in_context($userlist1);
 527          $this->assertCount(2, $userlist1);
 528  
 529          $userlist2 = new \core_privacy\local\request\userlist($coursecontext2, $component);
 530          provider::get_users_in_context($userlist2);
 531          $this->assertCount(1, $userlist2);
 532  
 533          // Convert $userlist1 into an approved_contextlist.
 534          $approvedlist1 = new approved_userlist($coursecontext1, $component, $userlist1->get_userids());
 535          // Delete using delete_data_for_user.
 536          provider::delete_data_for_users($approvedlist1);
 537  
 538          // Re-fetch users in coursecontext1.
 539          $userlist1 = new \core_privacy\local\request\userlist($coursecontext1, $component);
 540          provider::get_users_in_context($userlist1);
 541          // The user data in coursecontext1 should be deleted.
 542          $this->assertCount(0, $userlist1);
 543  
 544          // Re-fetch users in coursecontext2.
 545          $userlist2 = new \core_privacy\local\request\userlist($coursecontext2, $component);
 546          provider::get_users_in_context($userlist2);
 547          // The user data in coursecontext2 should be still present.
 548          $this->assertCount(1, $userlist2);
 549      }
 550  
 551      /**
 552       * Test for provider::delete_data_for_user() when there are also comments from other plugins.
 553       */
 554      public function test_delete_data_for_users_with_comments_from_other_plugins() {
 555          $component = 'block_comments';
 556  
 557          $assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
 558          $instance = $assigngenerator->create_instance(['course' => $this->course1]);
 559          $cm = get_coursemodule_from_instance('assign', $instance->id);
 560          $assigncontext = \context_module::instance($cm->id);
 561          $assign = new \assign($assigncontext, $cm, $this->course1);
 562  
 563          // Add a comments block in the assignment page.
 564          $this->add_comments_block_in_context($assigncontext);
 565  
 566          $submission = $assign->get_user_submission($this->student1->id, true);
 567  
 568          $options = new \stdClass();
 569          $options->area = 'submission_comments';
 570          $options->course = $assign->get_course();
 571          $options->context = $assigncontext;
 572          $options->itemid = $submission->id;
 573          $options->component = 'assignsubmission_comments';
 574          $options->showcount = true;
 575          $options->displaycancel = true;
 576  
 577          $comment = new \comment($options);
 578          $comment->set_post_permission(true);
 579  
 580          $this->setUser($this->student1);
 581          $comment->add('Comment from student 1');
 582  
 583          $this->add_comment('New comment', $assigncontext);
 584          $this->add_comment('New comment', $assigncontext);
 585  
 586          $userlist1 = new \core_privacy\local\request\userlist($assigncontext, $component);
 587          provider::get_users_in_context($userlist1);
 588          $this->assertCount(1, $userlist1);
 589  
 590          // Convert $userlist1 into an approved_contextlist.
 591          $approvedlist = new approved_userlist($assigncontext, $component, $userlist1->get_userids());
 592          // Delete using delete_data_for_user.
 593          provider::delete_data_for_users($approvedlist);
 594  
 595          // Re-fetch users in assigncontext.
 596          $userlist1 = new \core_privacy\local\request\userlist($assigncontext, $component);
 597          provider::get_users_in_context($userlist1);
 598          // The user data in assigncontext should be deleted.
 599          $this->assertCount(0, $userlist1);
 600      }
 601  }