Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 310]

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