Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 311 and 402] [Versions 400 and 402] [Versions 401 and 402]

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