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  /**
  18   * Privacy provider tests.
  19   *
  20   * @package     mod_glossary
  21   * @copyright   2018 Simey Lameze <simey@moodle.com>
  22   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  namespace mod_glossary\privacy;
  25  
  26  use core_privacy\local\metadata\collection;
  27  use core_privacy\local\request\approved_userlist;
  28  use core_privacy\local\request\deletion_criteria;
  29  use mod_glossary\privacy\provider;
  30  
  31  defined('MOODLE_INTERNAL') || die();
  32  
  33  global $CFG;
  34  require_once($CFG->dirroot . '/comment/lib.php');
  35  require_once($CFG->dirroot . '/rating/lib.php');
  36  
  37  /**
  38   * Privacy provider tests class.
  39   *
  40   * @package    mod_glossary
  41   * @copyright 2018 Simey Lameze <simey@moodle.com>
  42   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  43   */
  44  class provider_test extends \core_privacy\tests\provider_testcase {
  45      /** @var stdClass The student object. */
  46      protected $student;
  47  
  48      /** @var stdClass The teacher object. */
  49      protected $teacher;
  50  
  51      /** @var stdClass The glossary object. */
  52      protected $glossary;
  53  
  54      /** @var stdClass The course object. */
  55      protected $course;
  56  
  57      /** @var stdClass The plugin generator object. */
  58      protected $plugingenerator;
  59  
  60      /**
  61       * {@inheritdoc}
  62       */
  63      protected function setUp(): void {
  64          $this->resetAfterTest();
  65  
  66          global $DB;
  67          $generator = $this->getDataGenerator();
  68          $course = $generator->create_course();
  69          $this->course = $course;
  70  
  71          $this->plugingenerator = $generator->get_plugin_generator('mod_glossary');
  72  
  73          // The glossary activity the user will answer.
  74          $glossary = $this->plugingenerator->create_instance(['course' => $course->id]);
  75          $this->glossary = $glossary;
  76  
  77          $cm = get_coursemodule_from_instance('glossary', $glossary->id);
  78          $context = \context_module::instance($cm->id);
  79  
  80          // Create a student which will add an entry to a glossary.
  81          $student = $generator->create_user();
  82          $generator->enrol_user($student->id,  $course->id, 'student');
  83          $this->student = $student;
  84  
  85          $teacher = $generator->create_user();
  86          $generator->enrol_user($teacher->id,  $course->id, 'editingteacher');
  87          $this->teacher = $teacher;
  88  
  89          $this->setUser($student->id);
  90          $ge1 = $this->plugingenerator->create_content($glossary, ['concept' => 'first', 'approved' => 1], ['one']);
  91  
  92          // Student create a comment on a glossary entry.
  93          $this->setUser($student);
  94          $comment = $this->get_comment_object($context, $ge1->id);
  95          $comment->add('Hello, it\'s me!');
  96  
  97          // Attach tags.
  98          \core_tag_tag::set_item_tags('mod_glossary', 'glossary_entries', $ge1->id, $context, ['Beer', 'Golf']);
  99      }
 100  
 101      /**
 102       * Test for provider::get_metadata().
 103       */
 104      public function test_get_metadata() {
 105          $collection = new collection('mod_glossary');
 106          $newcollection = provider::get_metadata($collection);
 107          $itemcollection = $newcollection->get_collection();
 108          $this->assertCount(5, $itemcollection);
 109  
 110          $table = reset($itemcollection);
 111          $this->assertEquals('glossary_entries', $table->get_name());
 112  
 113          $privacyfields = $table->get_privacy_fields();
 114          $this->assertArrayHasKey('glossaryid', $privacyfields);
 115          $this->assertArrayHasKey('concept', $privacyfields);
 116          $this->assertArrayHasKey('definition', $privacyfields);
 117          $this->assertArrayHasKey('attachment', $privacyfields);
 118          $this->assertArrayHasKey('userid', $privacyfields);
 119          $this->assertArrayHasKey('timemodified', $privacyfields);
 120  
 121          $this->assertEquals('privacy:metadata:glossary_entries', $table->get_summary());
 122      }
 123  
 124      /**
 125       * Test for provider::get_contexts_for_userid().
 126       */
 127      public function test_get_contexts_for_userid() {
 128          $cm = get_coursemodule_from_instance('glossary', $this->glossary->id);
 129  
 130          $contextlist = provider::get_contexts_for_userid($this->student->id);
 131          $this->assertCount(1, $contextlist);
 132          $contextforuser = $contextlist->current();
 133          $cmcontext = \context_module::instance($cm->id);
 134          $this->assertEquals($cmcontext->id, $contextforuser->id);
 135      }
 136  
 137      /**
 138       * Test for provider::get_users_in_context().
 139       */
 140      public function test_get_users_in_context() {
 141          $component = 'mod_glossary';
 142          $cm = get_coursemodule_from_instance('glossary', $this->glossary->id);
 143          $cmcontext = \context_module::instance($cm->id);
 144  
 145          $userlist = new \core_privacy\local\request\userlist($cmcontext, $component);
 146          provider::get_users_in_context($userlist);
 147  
 148          $this->assertCount(1, $userlist);
 149  
 150          $expected = [$this->student->id];
 151          $actual = $userlist->get_userids();
 152          sort($expected);
 153          sort($actual);
 154  
 155          $this->assertEquals($expected, $actual);
 156      }
 157  
 158      /**
 159       * Test for provider::export_user_data().
 160       */
 161      public function test_export_for_context() {
 162          $cm = get_coursemodule_from_instance('glossary', $this->glossary->id);
 163          $cmcontext = \context_module::instance($cm->id);
 164  
 165          // Export all of the data for the context.
 166          $writer = \core_privacy\local\request\writer::with_context($cmcontext);
 167          $contextlist = new \core_privacy\local\request\approved_contextlist($this->student, 'mod_glossary' , [$cmcontext->id]);
 168  
 169          \mod_glossary\privacy\provider::export_user_data($contextlist);
 170          $this->assertTrue($writer->has_any_data());
 171          $data = $writer->get_data([]);
 172  
 173          $this->assertEquals('Glossary 1', $data->name);
 174          $this->assertEquals('first', $data->entries[0]['concept']);
 175      }
 176  
 177      /**
 178       * Test for provider::delete_data_for_all_users_in_context().
 179       */
 180      public function test_delete_data_for_all_users_in_context() {
 181          global $DB;
 182  
 183          $generator = $this->getDataGenerator();
 184          $cm = get_coursemodule_from_instance('glossary', $this->glossary->id);
 185          $context = \context_module::instance($cm->id);
 186          // Create another student who will add an entry the glossary activity.
 187          $student2 = $generator->create_user();
 188          $generator->enrol_user($student2->id, $this->course->id, 'student');
 189  
 190          $this->setUser($student2);
 191          $ge3 = $this->plugingenerator->create_content($this->glossary, ['concept' => 'first', 'approved' => 1], ['three']);
 192          $comment = $this->get_comment_object($context, $ge3->id);
 193          $comment->add('User 2 comment');
 194  
 195          $this->plugingenerator->create_category($this->glossary, ['cat1'], [$ge3]);
 196          $count = $DB->count_records('glossary_entries_categories', ['entryid' => $ge3->id]);
 197          $this->assertEquals(1, $count);
 198          \core_tag_tag::set_item_tags('mod_glossary', 'glossary_entries', $ge3->id, $context, ['Pizza', 'Noodles']);
 199  
 200          // As a teacher, rate student 2 entry.
 201          $this->setUser($this->teacher);
 202          $rating = $this->get_rating_object($context, $ge3->id);
 203          $rating->update_rating(2);
 204  
 205          // Before deletion, we should have 2 entries.
 206          $count = $DB->count_records('glossary_entries', ['glossaryid' => $this->glossary->id]);
 207          $this->assertEquals(2, $count);
 208          $aliascount = $DB->count_records('glossary_alias');
 209          $this->assertEquals(2, $aliascount);
 210          // Delete data based on context.
 211          provider::delete_data_for_all_users_in_context($context);
 212  
 213          // After deletion, the glossary entries and aliases for that glossary activity should have been deleted.
 214          $count = $DB->count_records('glossary_entries', ['glossaryid' => $this->glossary->id]);
 215          $this->assertEquals(0, $count);
 216          $this->assertEquals(0, $DB->count_records('glossary_alias'));
 217          $count = $DB->count_records('glossary_entries_categories', ['entryid' => $ge3->id]);
 218          $this->assertEquals(0, $count);
 219          $tagcount = $DB->count_records('tag_instance', ['component' => 'mod_glossary', 'itemtype' => 'glossary_entries',
 220              'itemid' => $ge3->id]);
 221          $this->assertEquals(0, $tagcount);
 222  
 223          $commentcount = $DB->count_records('comments', ['component' => 'mod_glossary', 'commentarea' => 'glossary_entry',
 224              'itemid' => $ge3->id, 'userid' => $student2->id]);
 225          $this->assertEquals(0, $commentcount);
 226  
 227          $ratingcount = $DB->count_records('rating', ['component' => 'mod_glossary', 'ratingarea' => 'entry',
 228              'itemid' => $ge3->id]);
 229          $this->assertEquals(0, $ratingcount);
 230      }
 231  
 232      /**
 233       * Test for provider::delete_data_for_user().
 234       */
 235      public function test_delete_data_for_user() {
 236          global $DB;
 237          $generator = $this->getDataGenerator();
 238  
 239          // Create another student who will add an entry to the first glossary.
 240          $student2 = $generator->create_user();
 241          $generator->enrol_user($student2->id, $this->course->id, 'student');
 242  
 243          $cm1 = get_coursemodule_from_instance('glossary', $this->glossary->id);
 244          $glossary2 = $this->plugingenerator->create_instance(['course' => $this->course->id]);
 245          $cm2 = get_coursemodule_from_instance('glossary', $glossary2->id);
 246  
 247          $ge1 = $this->plugingenerator->create_content($this->glossary, ['concept' => 'first user glossary entry', 'approved' => 1]);
 248          $this->plugingenerator->create_content($glossary2, ['concept' => 'first user second glossary entry', 'approved' => 1]);
 249  
 250          $context1 = \context_module::instance($cm1->id);
 251          $context2 = \context_module::instance($cm2->id);
 252          \core_tag_tag::set_item_tags('mod_glossary', 'glossary_entries', $ge1->id, $context1, ['Parmi', 'Sushi']);
 253  
 254          $this->setUser($student2);
 255          $ge3 = $this->plugingenerator->create_content($this->glossary, ['concept' => 'second user glossary entry',
 256                  'approved' => 1], ['three']);
 257  
 258          $comment = $this->get_comment_object($context1, $ge3->id);
 259          $comment->add('User 2 comment');
 260  
 261          \core_tag_tag::set_item_tags('mod_glossary', 'glossary_entries', $ge3->id, $context1, ['Pizza', 'Noodles']);
 262  
 263          // As a teacher, rate student 2's entry.
 264          $this->setUser($this->teacher);
 265          $rating = $this->get_rating_object($context1, $ge3->id);
 266          $rating->update_rating(2);
 267  
 268          // Before deletion, we should have 3 entries, one rating and 2 tag instances.
 269          $count = $DB->count_records('glossary_entries', ['glossaryid' => $this->glossary->id]);
 270          $this->assertEquals(3, $count);
 271          $tagcount = $DB->count_records('tag_instance', ['component' => 'mod_glossary', 'itemtype' => 'glossary_entries',
 272              'itemid' => $ge3->id]);
 273          $this->assertEquals(2, $tagcount);
 274          $aliascount = $DB->count_records('glossary_alias', ['entryid' => $ge3->id]);
 275          $this->assertEquals(1, $aliascount);
 276          $ratingcount = $DB->count_records('rating', ['component' => 'mod_glossary', 'ratingarea' => 'entry',
 277              'itemid' => $ge3->id]);
 278          $this->assertEquals(1, $ratingcount);
 279  
 280          $contextlist = new \core_privacy\local\request\approved_contextlist($student2, 'glossary',
 281              [$context1->id, $context2->id]);
 282          provider::delete_data_for_user($contextlist);
 283  
 284          // After deletion, the glossary entry and tags for the second student should have been deleted.
 285          $count = $DB->count_records('glossary_entries', ['glossaryid' => $this->glossary->id, 'userid' => $student2->id]);
 286          $this->assertEquals(0, $count);
 287  
 288          $tagcount = $DB->count_records('tag_instance', ['component' => 'mod_glossary', 'itemtype' => 'glossary_entries',
 289                  'itemid' => $ge3->id]);
 290          $this->assertEquals(0, $tagcount);
 291  
 292          $commentcount = $DB->count_records('comments', ['component' => 'mod_glossary', 'commentarea' => 'glossary_entry',
 293                  'itemid' => $ge3->id, 'userid' => $student2->id]);
 294          $this->assertEquals(0, $commentcount);
 295          $aliascount = $DB->count_records('glossary_alias', ['entryid' => $ge3->id]);
 296          $this->assertEquals(0, $aliascount);
 297  
 298          // Student's 1 entries, comments and tags should not be removed.
 299          $count = $DB->count_records('glossary_entries', ['glossaryid' => $this->glossary->id,
 300                  'userid' => $this->student->id]);
 301          $this->assertEquals(2, $count);
 302  
 303          $tagcount = $DB->count_records('tag_instance', ['component' => 'mod_glossary', 'itemtype' => 'glossary_entries',
 304              'itemid' => $ge1->id]);
 305          $this->assertEquals(2, $tagcount);
 306  
 307          $commentcount = $DB->count_records('comments', ['component' => 'mod_glossary', 'commentarea' => 'glossary_entry',
 308               'userid' => $this->student->id]);
 309          $this->assertEquals(1, $commentcount);
 310  
 311          $ratingcount = $DB->count_records('rating', ['component' => 'mod_glossary', 'ratingarea' => 'entry',
 312              'itemid' => $ge3->id]);
 313          $this->assertEquals(0, $ratingcount);
 314      }
 315  
 316      /**
 317       * Test for provider::delete_data_for_users().
 318       */
 319      public function test_delete_data_for_users() {
 320          global $DB;
 321          $generator = $this->getDataGenerator();
 322  
 323          $student2 = $generator->create_user();
 324          $generator->enrol_user($student2->id, $this->course->id, 'student');
 325  
 326          $cm1 = get_coursemodule_from_instance('glossary', $this->glossary->id);
 327          $glossary2 = $this->plugingenerator->create_instance(['course' => $this->course->id]);
 328          $cm2 = get_coursemodule_from_instance('glossary', $glossary2->id);
 329  
 330          $ge1 = $this->plugingenerator->create_content($this->glossary, ['concept' => 'first user glossary entry', 'approved' => 1]);
 331          $ge2 = $this->plugingenerator->create_content($glossary2, ['concept' => 'first user second glossary entry',
 332                  'approved' => 1], ['two']);
 333  
 334          $context1 = \context_module::instance($cm1->id);
 335          $context2 = \context_module::instance($cm2->id);
 336          \core_tag_tag::set_item_tags('mod_glossary', 'glossary_entries', $ge1->id, $context1, ['Parmi', 'Sushi']);
 337  
 338          $this->setUser($student2);
 339          $ge3 = $this->plugingenerator->create_content($this->glossary, ['concept' => 'second user glossary entry',
 340                  'approved' => 1], ['three']);
 341  
 342          $comment = $this->get_comment_object($context1, $ge3->id);
 343          $comment->add('User 2 comment 1');
 344          $comment = $this->get_comment_object($context2, $ge2->id);
 345          $comment->add('User 2 comment 2');
 346  
 347          \core_tag_tag::set_item_tags('mod_glossary', 'glossary_entries', $ge3->id, $context1, ['Pizza', 'Noodles']);
 348          \core_tag_tag::set_item_tags('mod_glossary', 'glossary_entries', $ge2->id, $context2, ['Potato', 'Kumara']);
 349  
 350          // As a teacher, rate student 2's entry.
 351          $this->setUser($this->teacher);
 352          $rating = $this->get_rating_object($context1, $ge3->id);
 353          $rating->update_rating(2);
 354  
 355          // Check correct glossary 1 record counts before deletion.
 356          $count = $DB->count_records('glossary_entries', ['glossaryid' => $this->glossary->id]);
 357          // Note: There is an additional student entry from setUp().
 358          $this->assertEquals(3, $count);
 359  
 360          list($context1itemsql, $context1itemparams) = $DB->get_in_or_equal([$ge1->id, $ge3->id], SQL_PARAMS_NAMED);
 361          $geparams = [
 362              'component' => 'mod_glossary',
 363              'itemtype' => 'glossary_entries',
 364          ];
 365          $geparams += $context1itemparams;
 366          $wheresql = "component = :component AND itemtype = :itemtype AND itemid {$context1itemsql}";
 367  
 368          $tagcount = $DB->count_records_select('tag_instance', $wheresql, $geparams);
 369          $this->assertEquals(4, $tagcount);
 370  
 371          $aliascount = $DB->count_records_select('glossary_alias', "entryid {$context1itemsql}", $context1itemparams);
 372          $this->assertEquals(1, $aliascount);
 373  
 374          $commentparams = [
 375              'component' => 'mod_glossary',
 376              'commentarea' => 'glossary_entry',
 377          ];
 378          $commentparams += $context1itemparams;
 379          $commentwhere = "component = :component AND commentarea = :commentarea AND itemid {$context1itemsql}";
 380  
 381          $commentcount = $DB->count_records_select('comments', $commentwhere, $commentparams);
 382          $this->assertEquals(1, $commentcount);
 383  
 384          $ratingcount = $DB->count_records('rating', ['component' => 'mod_glossary', 'ratingarea' => 'entry',
 385              'itemid' => $ge3->id]);
 386          $this->assertEquals(1, $ratingcount);
 387  
 388          // Perform deletion within context 1 for both students.
 389          $approveduserlist = new approved_userlist($context1, 'mod_glossary',
 390                  [$this->student->id, $student2->id]);
 391          provider::delete_data_for_users($approveduserlist);
 392  
 393          // After deletion, all context 1 entries, tags and comment should be deleted.
 394          $count = $DB->count_records('glossary_entries', ['glossaryid' => $this->glossary->id]);
 395          $this->assertEquals(0, $count);
 396  
 397          $tagcount = $DB->count_records_select('tag_instance', $wheresql, $geparams);
 398          $this->assertEquals(0, $tagcount);
 399  
 400          $aliascount = $DB->count_records_select('glossary_alias', "entryid {$context1itemsql}", $context1itemparams);
 401          $this->assertEquals(0, $aliascount);
 402  
 403          $commentcount = $DB->count_records_select('comments', $commentwhere, $commentparams);
 404          $this->assertEquals(0, $commentcount);
 405  
 406          // Context 2 entries should remain intact.
 407          $count = $DB->count_records('glossary_entries', ['glossaryid' => $glossary2->id]);
 408          $this->assertEquals(1, $count);
 409  
 410          $tagcount = $DB->count_records('tag_instance', ['component' => 'mod_glossary', 'itemtype' => 'glossary_entries',
 411                  'itemid' => $ge2->id]);
 412          $this->assertEquals(2, $tagcount);
 413  
 414          $aliascount = $DB->count_records('glossary_alias', ['entryid' => $ge2->id]);
 415          $this->assertEquals(1, $aliascount);
 416  
 417          $commentcount = $DB->count_records('comments', ['component' => 'mod_glossary', 'commentarea' => 'glossary_entry',
 418               'itemid' => $ge2->id]);
 419          $this->assertEquals(1, $commentcount);
 420  
 421          $ratingcount = $DB->count_records('rating', ['component' => 'mod_glossary', 'ratingarea' => 'entry',
 422              'itemid' => $ge3->id]);
 423          $this->assertEquals(0, $ratingcount);
 424      }
 425  
 426      /**
 427       * Get the comment area for glossary module.
 428       *
 429       * @param context $context The context.
 430       * @param int $itemid The item ID.
 431       * @return comment
 432       */
 433      protected function get_comment_object(\context $context, $itemid) {
 434          $args = new \stdClass();
 435  
 436          $args->context = $context;
 437          $args->course = get_course(SITEID);
 438          $args->area = 'glossary_entry';
 439          $args->itemid = $itemid;
 440          $args->component = 'mod_glossary';
 441          $comment = new \comment($args);
 442          $comment->set_post_permission(true);
 443  
 444          return $comment;
 445      }
 446  
 447      /**
 448       * Get the rating area for glossary module.
 449       *
 450       * @param context $context The context.
 451       * @param int $itemid The item ID.
 452       * @return rating object
 453       */
 454      protected function get_rating_object(\context $context, $itemid) {
 455          global $USER;
 456  
 457          $ratingoptions = new \stdClass;
 458          $ratingoptions->context = $context;
 459          $ratingoptions->ratingarea = 'entry';
 460          $ratingoptions->component = 'mod_glossary';
 461          $ratingoptions->itemid  = $itemid;
 462          $ratingoptions->scaleid = 2;
 463          $ratingoptions->userid  = $USER->id;
 464          return new \rating($ratingoptions);
 465      }
 466  }