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.
   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 tests for core_tag.
  19   *
  20   * @package    core_comment
  21   * @category   test
  22   * @copyright  2018 Zig Tan <zig@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  global $CFG;
  28  
  29  require_once($CFG->dirroot . '/tag/lib.php');
  30  
  31  use \core_privacy\tests\provider_testcase;
  32  use \core_privacy\local\request\writer;
  33  use \core_tag\privacy\provider;
  34  use \core_privacy\local\request\approved_userlist;
  35  
  36  /**
  37   * Unit tests for tag/classes/privacy/policy
  38   *
  39   * @copyright  2018 Zig Tan <zig@moodle.com>
  40   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   */
  42  class core_tag_privacy_testcase extends provider_testcase {
  43  
  44      /**
  45       * Check the exporting of tags for a user id in a context.
  46       */
  47      public function test_export_tags() {
  48          global $DB;
  49  
  50          $this->resetAfterTest(true);
  51  
  52          // Create a user to perform tagging.
  53          $user = $this->getDataGenerator()->create_user();
  54          $this->setUser($user);
  55  
  56          // Create a course to tag.
  57          $course = $this->getDataGenerator()->create_course();
  58          $context = context_course::instance($course->id);
  59          $subcontext = [];
  60  
  61          // Create three dummy tags and tag instances.
  62          $dummytags = [ 'Tag 1', 'Tag 2', 'Tag 3' ];
  63          core_tag_tag::set_item_tags('core_course', 'course', $course->id, context_course::instance($course->id),
  64                                      $dummytags, $user->id);
  65  
  66          // Get the tag instances that should have been created.
  67          $taginstances = $DB->get_records('tag_instance', array('itemtype' => 'course', 'itemid' => $course->id));
  68          $this->assertCount(count($dummytags), $taginstances);
  69  
  70          // Check tag instances match the component and context.
  71          foreach ($taginstances as $taginstance) {
  72              $this->assertEquals('core_course', $taginstance->component);
  73              $this->assertEquals(context_course::instance($course->id)->id, $taginstance->contextid);
  74          }
  75  
  76          // Retrieve tags only for this user.
  77          provider::export_item_tags($user->id, $context, $subcontext, 'core_course', 'course', $course->id, true);
  78  
  79          $writer = writer::with_context($context);
  80          $this->assertTrue($writer->has_any_data());
  81  
  82          $exportedtags = $writer->get_related_data($subcontext, 'tags');
  83          $this->assertCount(count($dummytags), $exportedtags);
  84  
  85          // Check the exported tag's rawname is found in the initial dummy tags.
  86          foreach ($exportedtags as $exportedtag) {
  87              $this->assertContains($exportedtag, $dummytags);
  88          }
  89      }
  90  
  91      /**
  92       * Test method delete_item_tags().
  93       */
  94      public function test_delete_item_tags() {
  95          global $DB;
  96  
  97          $this->resetAfterTest(true);
  98  
  99          // Create a course to tag.
 100          $course1 = $this->getDataGenerator()->create_course();
 101          $context1 = context_course::instance($course1->id);
 102          $course2 = $this->getDataGenerator()->create_course();
 103          $context2 = context_course::instance($course2->id);
 104  
 105          // Tag courses.
 106          core_tag_tag::set_item_tags('core_course', 'course', $course1->id, $context1, ['Tag 1', 'Tag 2', 'Tag 3']);
 107          core_tag_tag::set_item_tags('core_course', 'course', $course2->id, $context2, ['Tag 1', 'Tag 2']);
 108  
 109          $expectedtagcount = $DB->count_records('tag_instance');
 110          // Delete tags for course1.
 111          core_tag\privacy\provider::delete_item_tags($context1, 'core_course', 'course');
 112          $expectedtagcount -= 3;
 113          $this->assertEquals($expectedtagcount, $DB->count_records('tag_instance'));
 114  
 115          // Delete tags for course2. Use wrong itemid.
 116          core_tag\privacy\provider::delete_item_tags($context2, 'core_course', 'course', $course1->id);
 117          $this->assertEquals($expectedtagcount, $DB->count_records('tag_instance'));
 118  
 119          // Use correct itemid.
 120          core_tag\privacy\provider::delete_item_tags($context2, 'core_course', 'course', $course2->id);
 121          $expectedtagcount -= 2;
 122          $this->assertEquals($expectedtagcount, $DB->count_records('tag_instance'));
 123      }
 124  
 125      /**
 126       * Test method delete_item_tags() with userid.
 127       */
 128      public function test_delete_item_tags_with_userid() {
 129          global $DB;
 130  
 131          $this->resetAfterTest(true);
 132          // Create a course to tag.
 133          $course = $this->getDataGenerator()->create_course();
 134          $context = context_course::instance($course->id);
 135  
 136          // Create a user to perform tagging.
 137          $user = $this->getDataGenerator()->create_user();
 138          $this->setUser($user);
 139  
 140          // Tag courses.
 141          core_tag_tag::set_item_tags('core_course', 'course', $course->id, $context, ['Tag 1', 'Tag 2'], $user->id);
 142          $expectedtagcount = $DB->count_records('tag_instance');
 143  
 144          // Delete tags for course. Use wrong userid.
 145          core_tag\privacy\provider::delete_item_tags($context, 'core_course', 'course', null, 1);
 146          $this->assertEquals($expectedtagcount, $DB->count_records('tag_instance'));
 147  
 148          $expectedtagcount -= 2;
 149          // Delete tags for course. Use correct userid.
 150          core_tag\privacy\provider::delete_item_tags($context, 'core_course', 'course', null, $user->id);
 151          $this->assertEquals($expectedtagcount, $DB->count_records('tag_instance'));
 152      }
 153  
 154      /**
 155       * Test method delete_item_tags_select().
 156       */
 157      public function test_delete_item_tags_select() {
 158          global $DB;
 159  
 160          $this->resetAfterTest(true);
 161  
 162          // Create a course to tag.
 163          $course1 = $this->getDataGenerator()->create_course();
 164          $context1 = context_course::instance($course1->id);
 165          $course2 = $this->getDataGenerator()->create_course();
 166          $context2 = context_course::instance($course2->id);
 167  
 168          // Tag courses.
 169          core_tag_tag::set_item_tags('core_course', 'course', $course1->id, $context1, ['Tag 1', 'Tag 2', 'Tag 3']);
 170          core_tag_tag::set_item_tags('core_course', 'course', $course2->id, $context2, ['Tag 1', 'Tag 2']);
 171  
 172          $expectedtagcount = $DB->count_records('tag_instance');
 173          // Delete tags for course1.
 174          list($sql, $params) = $DB->get_in_or_equal([$course1->id, $course2->id], SQL_PARAMS_NAMED);
 175          core_tag\privacy\provider::delete_item_tags_select($context1, 'core_course', 'course', $sql, $params);
 176          $expectedtagcount -= 3;
 177          $this->assertEquals($expectedtagcount, $DB->count_records('tag_instance'));
 178  
 179          // Delete tags for course2.
 180          core_tag\privacy\provider::delete_item_tags_select($context2, 'core_course', 'course', $sql, $params);
 181          $expectedtagcount -= 2;
 182          $this->assertEquals($expectedtagcount, $DB->count_records('tag_instance'));
 183      }
 184  
 185      protected function set_up_tags() {
 186          global $CFG;
 187          require_once($CFG->dirroot.'/user/editlib.php');
 188  
 189          $this->resetAfterTest(true);
 190  
 191          $user1 = $this->getDataGenerator()->create_user();
 192          $user2 = $this->getDataGenerator()->create_user();
 193  
 194          $this->setUser($user1);
 195          useredit_update_interests($user1, ['Birdwatching', 'Computers']);
 196  
 197          $this->setUser($user2);
 198          useredit_update_interests($user2, ['computers']);
 199  
 200          $this->setAdminUser();
 201  
 202          $tag = core_tag_tag::get_by_name(0, 'computers', '*');
 203          $tag->update(['description' => '<img src="@@PLUGINFILE@@/computer.jpg">']);
 204          get_file_storage()->create_file_from_string([
 205              'contextid' => context_system::instance()->id,
 206              'component' => 'tag',
 207              'filearea' => 'description',
 208              'itemid' => $tag->id,
 209              'filepath' => '/',
 210              'filename' => 'computer.jpg'
 211          ], "jpg:image");
 212  
 213          return [$user1, $user2];
 214      }
 215  
 216      public function test_export_item_tags() {
 217          list($user1, $user2) = $this->set_up_tags();
 218          $this->assertEquals([context_system::instance()->id],
 219              provider::get_contexts_for_userid($user1->id)->get_contextids());
 220          $this->assertEmpty(provider::get_contexts_for_userid($user2->id)->get_contextids());
 221      }
 222  
 223      public function test_delete_data_for_user() {
 224          global $DB;
 225          list($user1, $user2) = $this->set_up_tags();
 226          $context = context_system::instance();
 227          $this->assertEquals(2, $DB->count_records('tag', []));
 228          $this->assertEquals(0, $DB->count_records('tag', ['userid' => 0]));
 229          provider::delete_data_for_user(new \core_privacy\local\request\approved_contextlist($user2, 'core_tag', [$context->id]));
 230          $this->assertEquals(2, $DB->count_records('tag', []));
 231          $this->assertEquals(0, $DB->count_records('tag', ['userid' => 0]));
 232          provider::delete_data_for_user(new \core_privacy\local\request\approved_contextlist($user1, 'core_tag', [$context->id]));
 233          $this->assertEquals(2, $DB->count_records('tag', []));
 234          $this->assertEquals(2, $DB->count_records('tag', ['userid' => 0]));
 235      }
 236  
 237      public function test_delete_data_for_all_users_in_context() {
 238          global $DB;
 239          $course = $this->getDataGenerator()->create_course();
 240          list($user1, $user2) = $this->set_up_tags();
 241          $this->assertEquals(2, $DB->count_records('tag', []));
 242          $this->assertEquals(3, $DB->count_records('tag_instance', []));
 243          provider::delete_data_for_all_users_in_context(context_course::instance($course->id));
 244          $this->assertEquals(2, $DB->count_records('tag', []));
 245          $this->assertEquals(3, $DB->count_records('tag_instance', []));
 246          provider::delete_data_for_all_users_in_context(context_system::instance());
 247          $this->assertEquals(0, $DB->count_records('tag', []));
 248          $this->assertEquals(0, $DB->count_records('tag_instance', []));
 249      }
 250  
 251      public function test_export_data_for_user() {
 252          global $DB;
 253          list($user1, $user2) = $this->set_up_tags();
 254          $context = context_system::instance();
 255          provider::export_user_data(new \core_privacy\local\request\approved_contextlist($user2, 'core_tag', [$context->id]));
 256          $this->assertFalse(writer::with_context($context)->has_any_data());
 257  
 258          $tagids = array_values(array_map(function($tag) {
 259              return $tag->id;
 260          }, core_tag_tag::get_by_name_bulk(core_tag_collection::get_default(), ['Birdwatching', 'Computers'])));
 261  
 262          provider::export_user_data(new \core_privacy\local\request\approved_contextlist($user1, 'core_tag', [$context->id]));
 263          $writer = writer::with_context($context);
 264  
 265          $data = $writer->get_data(['Tags', $tagids[0]]);
 266          $files = $writer->get_files(['Tags', $tagids[0]]);
 267          $this->assertEquals('Birdwatching', $data->rawname);
 268          $this->assertEmpty($files);
 269  
 270          $data = $writer->get_data(['Tags', $tagids[1]]);
 271          $files = $writer->get_files(['Tags', $tagids[1]]);
 272          $this->assertEquals('Computers', $data->rawname);
 273          $this->assertEquals(['computer.jpg'], array_keys($files));
 274      }
 275  
 276      /**
 277       * Test that only users within a system context are fetched.
 278       */
 279      public function test_get_users_in_context() {
 280          $component = 'core_tag';
 281  
 282          $user1 = $this->set_up_tags()[0];
 283          $systemcontext = context_system::instance();
 284  
 285          $userlist1 = new \core_privacy\local\request\userlist($systemcontext, $component);
 286          provider::get_users_in_context($userlist1);
 287          $this->assertCount(1, $userlist1);
 288          $expected = [$user1->id];
 289          $actual = $userlist1->get_userids();
 290          $this->assertEquals($expected, $actual);
 291  
 292          // The list of users within the a context other than system context should be empty.
 293          $usercontext1 = context_user::instance($user1->id);
 294          $userlist2 = new \core_privacy\local\request\userlist($usercontext1, $component);
 295          provider::get_users_in_context($userlist2);
 296          $this->assertCount(0, $userlist2);
 297      }
 298  
 299      /**
 300       * Test that data for users in approved userlist is deleted.
 301       */
 302      public function test_delete_data_for_users() {
 303          $component = 'core_tag';
 304  
 305          list($user1, $user2) = $this->set_up_tags();
 306          $usercontext1 = context_user::instance($user1->id);
 307          $user3 = $this->getDataGenerator()->create_user();
 308          $systemcontext = context_system::instance();
 309  
 310          $this->setUser($user2);
 311          useredit_update_interests($user2, ['basketball']);
 312  
 313          $this->setUser($user3);
 314          useredit_update_interests($user3, ['soccer']);
 315  
 316          $userlist = new \core_privacy\local\request\userlist($systemcontext, $component);
 317          provider::get_users_in_context($userlist);
 318          $this->assertCount(3, $userlist);
 319          $this->assertTrue(in_array($user1->id, $userlist->get_userids()));
 320          $this->assertTrue(in_array($user2->id, $userlist->get_userids()));
 321          $this->assertTrue(in_array($user3->id, $userlist->get_userids()));
 322  
 323          // Data should not be deleted in contexts other than system context.
 324          // Convert $userlist into an approved_contextlist.
 325          $approvedlist = new approved_userlist($usercontext1, $component, $userlist->get_userids());
 326          // Delete using delete_data_for_user.
 327          provider::delete_data_for_users($approvedlist);
 328          // Re-fetch users in systemcontext.
 329          $userlist = new \core_privacy\local\request\userlist($systemcontext, $component);
 330          provider::get_users_in_context($userlist);
 331          // The user data in systemcontext should not be deleted.
 332          $this->assertCount(3, $userlist);
 333  
 334          // Add user1 and user2 into an approved_contextlist.
 335          $approvedlist = new approved_userlist($systemcontext, $component, [$user1->id, $user2->id]);
 336          // Delete using delete_data_for_user.
 337          provider::delete_data_for_users($approvedlist);
 338          // Re-fetch users in systemcontext.
 339          $userlist = new \core_privacy\local\request\userlist($systemcontext, $component);
 340          provider::get_users_in_context($userlist);
 341          // The approved user data in systemcontext should be deleted.
 342          // The user list should return user3.
 343          $this->assertCount(1, $userlist);
 344          $this->assertTrue(in_array($user3->id, $userlist->get_userids()));
 345      }
 346  }