Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

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