Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   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   * Privacy tests for core_comment.
  18   *
  19   * @package    core_comment
  20   * @category   test
  21   * @copyright  2018 Adrian Greeve <adrian@moodle.com>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  namespace core_comment\privacy;
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  global $CFG;
  28  
  29  require_once($CFG->dirroot . '/comment/locallib.php');
  30  require_once($CFG->dirroot . '/comment/lib.php');
  31  
  32  use core_privacy\local\request\approved_userlist;
  33  use core_privacy\tests\provider_testcase;
  34  use core_privacy\tests\request\approved_contextlist;
  35  
  36  /**
  37   * Unit tests for comment/classes/privacy/policy
  38   *
  39   * @copyright  2018 Adrian Greeve <adrian@moodle.com>
  40   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   */
  42  class provider_test extends provider_testcase {
  43  
  44      protected function setUp(): void {
  45          $this->resetAfterTest();
  46      }
  47  
  48      /**
  49       * Check the exporting of comments for a user id in a context.
  50       */
  51      public function test_export_comments() {
  52          $course = $this->getDataGenerator()->create_course();
  53          $context = \context_course::instance($course->id);
  54  
  55          $comment = $this->get_comment_object($context, $course);
  56  
  57          $user1 = $this->getDataGenerator()->create_user();
  58          $user2 = $this->getDataGenerator()->create_user();
  59  
  60          // Add comments.
  61          $comments = [];
  62          $firstcomment = 'This is the first comment';
  63          $this->setUser($user1);
  64          $comment->add($firstcomment);
  65          $comments[$user1->id] = $firstcomment;
  66  
  67          $secondcomment = 'From the second user';
  68          $this->setUser($user2);
  69          $comment->add($secondcomment);
  70          $comments[$user2->id] = $secondcomment;
  71  
  72          // Retrieve comments only for user1.
  73          $this->setUser($user1);
  74          $writer = \core_privacy\local\request\writer::with_context($context);
  75          provider::export_comments($context, 'block_comments', 'page_comments', 0, []);
  76  
  77          $data = $writer->get_data([get_string('commentsubcontext', 'core_comment')]);
  78          $exportedcomments = $data->comments;
  79  
  80          // There is only one comment made by this user.
  81          $this->assertCount(1, $exportedcomments);
  82          $comment = reset($exportedcomments);
  83          $this->assertEquals($comments[$user1->id], format_string($comment->content, FORMAT_PLAIN));
  84  
  85          // Retrieve comments from any user.
  86          provider::export_comments($context, 'block_comments', 'page_comments', 0, [], false);
  87  
  88          $data = $writer->get_data([get_string('commentsubcontext', 'core_comment')]);
  89          $exportedcomments = $data->comments;
  90  
  91          // The whole conversation is two comments.
  92          $this->assertCount(2, $exportedcomments);
  93          foreach ($exportedcomments as $comment) {
  94              $this->assertEquals($comments[$comment->userid], format_string($comment->content, FORMAT_PLAIN));
  95          }
  96      }
  97  
  98      /**
  99       * Tests the deletion of all comments in a context.
 100       */
 101      public function test_delete_comments_for_all_users() {
 102          global $DB;
 103  
 104          $course1 = $this->getDataGenerator()->create_course();
 105          $course2 = $this->getDataGenerator()->create_course();
 106  
 107          $coursecontext1 = \context_course::instance($course1->id);
 108          $coursecontext2 = \context_course::instance($course2->id);
 109  
 110          $user1 = $this->getDataGenerator()->create_user();
 111          $user2 = $this->getDataGenerator()->create_user();
 112  
 113          $comment1 = $this->get_comment_object($coursecontext1, $course1);
 114          $comment2 = $this->get_comment_object($coursecontext2, $course2);
 115  
 116          $this->setUser($user1);
 117          $comment1->add('First comment for user 1 on comment 1');
 118          $comment2->add('First comment for user 1 on comment 2');
 119          $this->setUser($user2);
 120          $comment1->add('First comment for user 2 on comment 1');
 121          $comment2->add('First comment for user 2 on comment 2');
 122  
 123          // Because of the way things are set up with validation, creating an entry with the same context in a different component
 124          // or comment area is a huge pain. We're just going to jam entries into the table instead.
 125          $record = (object) [
 126              'contextid' => $coursecontext1->id,
 127              'component' => 'block_comments',
 128              'commentarea' => 'other_comments',
 129              'itemid' => 2,
 130              'content' => 'Comment user 1 different comment area',
 131              'format' => 0,
 132              'userid' => $user1->id,
 133              'timecreated' => time()
 134          ];
 135          $DB->insert_record('comments', $record);
 136          $record = (object) [
 137              'contextid' => $coursecontext1->id,
 138              'component' => 'tool_dataprivacy',
 139              'commentarea' => 'page_comments',
 140              'itemid' => 2,
 141              'content' => 'Comment user 1 different component',
 142              'format' => 0,
 143              'userid' => $user1->id,
 144              'timecreated' => time()
 145          ];
 146          $DB->insert_record('comments', $record);
 147  
 148          // Delete only for the first context. All records in the comments table for this context should be removed.
 149          provider::delete_comments_for_all_users($coursecontext1, 'block_comments', 'page_comments', 0);
 150          // No records left here.
 151          $this->assertCount(0, $comment1->get_comments());
 152          // All of the records are left intact here.
 153          $this->assertCount(2, $comment2->get_comments());
 154          // Check the other comment area.
 155          $result = $DB->get_records('comments', ['commentarea' => 'other_comments']);
 156          $this->assertCount(1, $result);
 157          $data = array_shift($result);
 158          $this->assertEquals('other_comments', $data->commentarea);
 159          // Check the different component, same commentarea.
 160          $result = $DB->get_records('comments', ['component' => 'tool_dataprivacy']);
 161          $this->assertCount(1, $result);
 162          $data = array_shift($result);
 163          $this->assertEquals('tool_dataprivacy', $data->component);
 164      }
 165  
 166      /**
 167       * Tests the deletion of all comments in a context.
 168       */
 169      public function test_delete_comments_for_all_users_select() {
 170          global $DB;
 171  
 172          $course1 = $this->getDataGenerator()->create_course();
 173          $course2 = $this->getDataGenerator()->create_course();
 174  
 175          $coursecontext1 = \context_course::instance($course1->id);
 176          $coursecontext2 = \context_course::instance($course2->id);
 177  
 178          $user1 = $this->getDataGenerator()->create_user();
 179          $user2 = $this->getDataGenerator()->create_user();
 180  
 181          $comment1 = $this->get_comment_object($coursecontext1, $course1);
 182          $comment2 = $this->get_comment_object($coursecontext2, $course2);
 183  
 184          $this->setUser($user1);
 185          $comment1->add('First comment for user 1 on comment 1');
 186          $comment2->add('First comment for user 1 on comment 2');
 187          $this->setUser($user2);
 188          $comment1->add('First comment for user 2 on comment 1');
 189          $comment2->add('First comment for user 2 on comment 2');
 190  
 191          // Because of the way things are set up with validation, creating an entry with the same context in a different component
 192          // or comment area is a huge pain. We're just going to jam entries into the table instead.
 193          $record = (object) [
 194              'contextid' => $coursecontext1->id,
 195              'component' => 'block_comments',
 196              'commentarea' => 'other_comments',
 197              'itemid' => 2,
 198              'content' => 'Comment user 1 different comment area',
 199              'format' => 0,
 200              'userid' => $user1->id,
 201              'timecreated' => time()
 202          ];
 203          $DB->insert_record('comments', $record);
 204          $record = (object) [
 205              'contextid' => $coursecontext1->id,
 206              'component' => 'tool_dataprivacy',
 207              'commentarea' => 'page_comments',
 208              'itemid' => 2,
 209              'content' => 'Comment user 1 different component',
 210              'format' => 0,
 211              'userid' => $user1->id,
 212              'timecreated' => time()
 213          ];
 214          $DB->insert_record('comments', $record);
 215  
 216          // Delete only for the first context. All records in the comments table for this context should be removed.
 217          list($sql, $params) = $DB->get_in_or_equal([0, 1, 2, 3], SQL_PARAMS_NAMED);
 218          provider::delete_comments_for_all_users_select($coursecontext1,
 219              'block_comments', 'page_comments', $sql, $params);
 220          // No records left here.
 221          $this->assertCount(0, $comment1->get_comments());
 222          // All of the records are left intact here.
 223          $this->assertCount(2, $comment2->get_comments());
 224          // Check the other comment area.
 225          $result = $DB->get_records('comments', ['commentarea' => 'other_comments']);
 226          $this->assertCount(1, $result);
 227          $data = array_shift($result);
 228          $this->assertEquals('other_comments', $data->commentarea);
 229          // Check the different component, same commentarea.
 230          $result = $DB->get_records('comments', ['component' => 'tool_dataprivacy']);
 231          $this->assertCount(1, $result);
 232          $data = array_shift($result);
 233          $this->assertEquals('tool_dataprivacy', $data->component);
 234      }
 235  
 236      /**
 237       * Tests deletion of comments for a specified user and contexts.
 238       */
 239      public function test_delete_comments_for_user() {
 240          global $DB;
 241  
 242          $course1 = $this->getDataGenerator()->create_course();
 243          $course2 = $this->getDataGenerator()->create_course();
 244          $course3 = $this->getDataGenerator()->create_course();
 245  
 246          $coursecontext1 = \context_course::instance($course1->id);
 247          $coursecontext2 = \context_course::instance($course2->id);
 248          $coursecontext3 = \context_course::instance($course3->id);
 249  
 250          $user1 = $this->getDataGenerator()->create_user();
 251          $user2 = $this->getDataGenerator()->create_user();
 252  
 253          $comment1 = $this->get_comment_object($coursecontext1, $course1);
 254          $comment2 = $this->get_comment_object($coursecontext2, $course2);
 255          $comment3 = $this->get_comment_object($coursecontext3, $course3);
 256  
 257          $this->setUser($user1);
 258          $comment1->add('First comment for user 1');
 259          $comment2->add('User 1 comment in second comment');
 260  
 261          $this->setUser($user2);
 262          $comment2->add('User two replied in comment two');
 263          $comment3->add('Comment three for user 2.');
 264  
 265          // Because of the way things are set up with validation, creating an entry with the same context in a different component
 266          // or comment area is a huge pain. We're just going to jam entries into the table instead.
 267          $record = (object) [
 268              'contextid' => $coursecontext1->id,
 269              'component' => 'block_comments',
 270              'commentarea' => 'other_comments',
 271              'itemid' => 2,
 272              'content' => 'Comment user 1 different comment area',
 273              'format' => 0,
 274              'userid' => $user1->id,
 275              'timecreated' => time()
 276          ];
 277          $DB->insert_record('comments', $record);
 278          $record = (object) [
 279              'contextid' => $coursecontext1->id,
 280              'component' => 'tool_dataprivacy',
 281              'commentarea' => 'page_comments',
 282              'itemid' => 2,
 283              'content' => 'Comment user 1 different component',
 284              'format' => 0,
 285              'userid' => $user1->id,
 286              'timecreated' => time()
 287          ];
 288          $DB->insert_record('comments', $record);
 289  
 290          // Delete the comments for user 1.
 291          $approvedcontextlist = new approved_contextlist($user1, 'block_comments',
 292                  [$coursecontext1->id, $coursecontext2->id]);
 293          provider::delete_comments_for_user($approvedcontextlist, 'block_comments', 'page_comments', 0);
 294  
 295          // No comments left in comments 1 as only user 1 commented there.
 296          $this->assertCount(0, $comment1->get_comments());
 297          // Only user 2 comments left in comments 2.
 298          $comment2comments = $comment2->get_comments();
 299          $this->assertCount(1, $comment2comments);
 300          $data = array_shift($comment2comments);
 301          $this->assertEquals($user2->id, $data->userid);
 302          // Nothing changed here as user 1 did not leave a comment.
 303          $comment3comments = $comment3->get_comments();
 304          $this->assertCount(1, $comment3comments);
 305          $data = array_shift($comment3comments);
 306          $this->assertEquals($user2->id, $data->userid);
 307          // Check the other comment area.
 308          $result = $DB->get_records('comments', ['commentarea' => 'other_comments']);
 309          $this->assertCount(1, $result);
 310          $data = array_shift($result);
 311          $this->assertEquals('other_comments', $data->commentarea);
 312          // Check the different component, same commentarea.
 313          $result = $DB->get_records('comments', ['component' => 'tool_dataprivacy']);
 314          $this->assertCount(1, $result);
 315          $data = array_shift($result);
 316          $this->assertEquals('tool_dataprivacy', $data->component);
 317      }
 318  
 319      /**
 320       * Tests deletion of comments for a specified userlist and context.
 321       */
 322      public function test_delete_comments_for_users() {
 323          global $DB;
 324  
 325          $course1 = $this->getDataGenerator()->create_course();
 326          $course2 = $this->getDataGenerator()->create_course();
 327          $course3 = $this->getDataGenerator()->create_course();
 328  
 329          $coursecontext1 = \context_course::instance($course1->id);
 330          $coursecontext2 = \context_course::instance($course2->id);
 331          $coursecontext3 = \context_course::instance($course3->id);
 332  
 333          $user1 = $this->getDataGenerator()->create_user();
 334          $user2 = $this->getDataGenerator()->create_user();
 335          $user3 = $this->getDataGenerator()->create_user();
 336  
 337          $comment1 = $this->get_comment_object($coursecontext1, $course1);
 338          $comment2 = $this->get_comment_object($coursecontext2, $course2);
 339          $comment3 = $this->get_comment_object($coursecontext3, $course3);
 340  
 341          $this->setUser($user1);
 342          $comment1->add('First comment for user 1');
 343          $comment2->add('User 1 comment in second comment');
 344  
 345          $this->setUser($user2);
 346          $comment2->add('User two replied in comment two');
 347  
 348          $this->setUser($user3);
 349          $comment2->add('User 3 also writing on comment 2, but will not be deleted');
 350          $comment3->add('Only user 3 commenting in comment 3.');
 351  
 352          // Because of the way things are set up with validation, creating an entry with the same context in a different component
 353          // or comment area is a huge pain. We're just going to jam entries into the table instead.
 354          $record = (object) [
 355              'contextid' => $coursecontext1->id,
 356              'component' => 'block_comments',
 357              'commentarea' => 'other_comments',
 358              'itemid' => 2,
 359              'content' => 'Comment user 1 different comment area',
 360              'format' => 0,
 361              'userid' => $user1->id,
 362              'timecreated' => time()
 363          ];
 364          $DB->insert_record('comments', $record);
 365          $record = (object) [
 366              'contextid' => $coursecontext1->id,
 367              'component' => 'tool_dataprivacy',
 368              'commentarea' => 'page_comments',
 369              'itemid' => 2,
 370              'content' => 'Comment user 1 different component',
 371              'format' => 0,
 372              'userid' => $user1->id,
 373              'timecreated' => time()
 374          ];
 375          $DB->insert_record('comments', $record);
 376  
 377          // Delete the comments for users 1 and 2 in all 3 contexts.
 378          $approvedusers = [$user1->id, $user2->id];
 379  
 380          $approveduserlist = new approved_userlist($coursecontext1, 'block_comments', $approvedusers);
 381          provider::delete_comments_for_users($approveduserlist, 'block_comments', 'page_comments');
 382  
 383          $approveduserlist = new approved_userlist($coursecontext2, 'block_comments', $approvedusers);
 384          provider::delete_comments_for_users($approveduserlist, 'block_comments', 'page_comments');
 385  
 386          $approveduserlist = new approved_userlist($coursecontext3, 'block_comments', $approvedusers);
 387          provider::delete_comments_for_users($approveduserlist, 'block_comments', 'page_comments');
 388  
 389          // No comments left in comments 1 as only user 1 commented there.
 390          $this->assertCount(0, $comment1->get_comments());
 391  
 392          // Only user 3's comment left in comments 2 as user 1 and 2 were approved for deletion.
 393          $comment2comments = $comment2->get_comments();
 394          $this->assertCount(1, $comment2comments);
 395          $comment2comment = array_shift($comment2comments);
 396          $this->assertEquals($user3->id, $comment2comment->userid);
 397  
 398          // Nothing changed here as user 1 and 2 did not leave a comment.
 399          $comment3comments = $comment3->get_comments();
 400          $this->assertCount(1, $comment3comments);
 401          $data = array_shift($comment3comments);
 402          $this->assertEquals($user3->id, $data->userid);
 403  
 404          // Check the other comment area.
 405          $result = $DB->get_records('comments', ['commentarea' => 'other_comments']);
 406          $this->assertCount(1, $result);
 407          $data = array_shift($result);
 408          $this->assertEquals('other_comments', $data->commentarea);
 409  
 410          // Check the different component, same commentarea.
 411          $result = $DB->get_records('comments', ['component' => 'tool_dataprivacy']);
 412          $this->assertCount(1, $result);
 413          $data = array_shift($result);
 414          $this->assertEquals('tool_dataprivacy', $data->component);
 415      }
 416  
 417      /**
 418       * Creates a comment object
 419       *
 420       * @param  context $context A context object.
 421       * @param  stdClass $course A course object.
 422       * @return comment The comment object.
 423       */
 424      protected function get_comment_object($context, $course) {
 425          // Comment on course page.
 426          $args = new \stdClass;
 427          $args->context = $context;
 428          $args->course = $course;
 429          $args->area = 'page_comments';
 430          $args->itemid = 0;
 431          $args->component = 'block_comments';
 432          $comment = new \comment($args);
 433          $comment->set_post_permission(true);
 434          return $comment;
 435      }
 436  }