Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 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 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [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  /**
  18   * Unit tests for lib.php
  19   *
  20   * @package    mod_data
  21   * @category   phpunit
  22   * @copyright  2013 Adrian Greeve
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  global $CFG;
  29  require_once($CFG->dirroot . '/mod/data/lib.php');
  30  
  31  /**
  32   * Unit tests for lib.php
  33   *
  34   * @package    mod_data
  35   * @copyright  2013 Adrian Greeve
  36   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  37   */
  38  class mod_data_lib_testcase extends advanced_testcase {
  39  
  40      /**
  41       * @var moodle_database
  42       */
  43      protected $DB = null;
  44  
  45      /**
  46       * Tear Down to reset DB.
  47       */
  48      public function tearDown(): void {
  49          global $DB;
  50  
  51          if (isset($this->DB)) {
  52              $DB = $this->DB;
  53              $this->DB = null;
  54          }
  55      }
  56  
  57      /**
  58       * Confirms that completionentries is working
  59       * Sets it to 1, confirms that
  60       * it is not complete. Inserts a record and
  61       * confirms that it is complete.
  62       */
  63      public function test_data_completion() {
  64          global $DB, $CFG;
  65          $this->resetAfterTest();
  66          $this->setAdminUser();
  67          $CFG->enablecompletion = 1;
  68          $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
  69          $record = new stdClass();
  70          $record->course = $course->id;
  71          $record->name = "Mod data completion test";
  72          $record->intro = "Some intro of some sort";
  73          $record->completionentries = "1";
  74          /* completion=2 means Show activity commplete when condition is met and completionentries means 1 record is
  75           * required for the activity to be considered complete
  76           */
  77          $module = $this->getDataGenerator()->create_module('data', $record, array('completion' => 2, 'completionentries' => 1));
  78  
  79          $cm = get_coursemodule_from_instance('data', $module->id, $course->id);
  80          $completion = new completion_info($course);
  81          $completiondata = $completion->get_data($cm, true, 0);
  82          /* Confirm it is not complete as there are no entries */
  83          $this->assertNotEquals(1, $completiondata->completionstate);
  84  
  85          $field = data_get_field_new('text', $module);
  86          $fielddetail = new stdClass();
  87          $fielddetail->d = $module->id;
  88          $fielddetail->mode = 'add';
  89          $fielddetail->type = 'text';
  90          $fielddetail->sesskey = sesskey();
  91          $fielddetail->name = 'Name';
  92          $fielddetail->description = 'Some name';
  93  
  94          $field->define_field($fielddetail);
  95          $field->insert_field();
  96          $recordid = data_add_record($module);
  97  
  98          $datacontent = array();
  99          $datacontent['fieldid'] = $field->field->id;
 100          $datacontent['recordid'] = $recordid;
 101          $datacontent['content'] = 'Asterix';
 102          $contentid = $DB->insert_record('data_content', $datacontent);
 103  
 104          $cm = get_coursemodule_from_instance('data', $module->id, $course->id);
 105          $completion = new completion_info($course);
 106          $completiondata = $completion->get_data($cm);
 107          /* Confirm it is complete because it has 1 entry */
 108          $this->assertEquals(1, $completiondata->completionstate);
 109      }
 110  
 111      public function test_data_delete_record() {
 112          global $DB;
 113  
 114          $this->resetAfterTest();
 115  
 116          // Create a record for deleting.
 117          $this->setAdminUser();
 118          $course = $this->getDataGenerator()->create_course();
 119          $record = new stdClass();
 120          $record->course = $course->id;
 121          $record->name = "Mod data delete test";
 122          $record->intro = "Some intro of some sort";
 123  
 124          $module = $this->getDataGenerator()->create_module('data', $record);
 125  
 126          $field = data_get_field_new('text', $module);
 127  
 128          $fielddetail = new stdClass();
 129          $fielddetail->d = $module->id;
 130          $fielddetail->mode = 'add';
 131          $fielddetail->type = 'text';
 132          $fielddetail->sesskey = sesskey();
 133          $fielddetail->name = 'Name';
 134          $fielddetail->description = 'Some name';
 135  
 136          $field->define_field($fielddetail);
 137          $field->insert_field();
 138          $recordid = data_add_record($module);
 139  
 140          $datacontent = array();
 141          $datacontent['fieldid'] = $field->field->id;
 142          $datacontent['recordid'] = $recordid;
 143          $datacontent['content'] = 'Asterix';
 144  
 145          $contentid = $DB->insert_record('data_content', $datacontent);
 146          $cm = get_coursemodule_from_instance('data', $module->id, $course->id);
 147  
 148          // Check to make sure that we have a database record.
 149          $data = $DB->get_records('data', array('id' => $module->id));
 150          $this->assertEquals(1, count($data));
 151  
 152          $datacontent = $DB->get_records('data_content', array('id' => $contentid));
 153          $this->assertEquals(1, count($datacontent));
 154  
 155          $datafields = $DB->get_records('data_fields', array('id' => $field->field->id));
 156          $this->assertEquals(1, count($datafields));
 157  
 158          $datarecords = $DB->get_records('data_records', array('id' => $recordid));
 159          $this->assertEquals(1, count($datarecords));
 160  
 161          // Test to see if a failed delete returns false.
 162          $result = data_delete_record(8798, $module, $course->id, $cm->id);
 163          $this->assertFalse($result);
 164  
 165          // Delete the record.
 166          $result = data_delete_record($recordid, $module, $course->id, $cm->id);
 167  
 168          // Check that all of the record is gone.
 169          $datacontent = $DB->get_records('data_content', array('id' => $contentid));
 170          $this->assertEquals(0, count($datacontent));
 171  
 172          $datarecords = $DB->get_records('data_records', array('id' => $recordid));
 173          $this->assertEquals(0, count($datarecords));
 174  
 175          // Make sure the function returns true on a successful deletion.
 176          $this->assertTrue($result);
 177      }
 178  
 179      /**
 180       * Test comment_created event.
 181       */
 182      public function test_data_comment_created_event() {
 183          global $CFG, $DB;
 184          require_once($CFG->dirroot . '/comment/lib.php');
 185  
 186          $this->resetAfterTest();
 187  
 188          // Create a record for deleting.
 189          $this->setAdminUser();
 190          $course = $this->getDataGenerator()->create_course();
 191          $record = new stdClass();
 192          $record->course = $course->id;
 193          $record->name = "Mod data delete test";
 194          $record->intro = "Some intro of some sort";
 195          $record->comments = 1;
 196  
 197          $module = $this->getDataGenerator()->create_module('data', $record);
 198          $field = data_get_field_new('text', $module);
 199  
 200          $fielddetail = new stdClass();
 201          $fielddetail->name = 'Name';
 202          $fielddetail->description = 'Some name';
 203  
 204          $field->define_field($fielddetail);
 205          $field->insert_field();
 206          $recordid = data_add_record($module);
 207  
 208          $datacontent = array();
 209          $datacontent['fieldid'] = $field->field->id;
 210          $datacontent['recordid'] = $recordid;
 211          $datacontent['content'] = 'Asterix';
 212  
 213          $contentid = $DB->insert_record('data_content', $datacontent);
 214          $cm = get_coursemodule_from_instance('data', $module->id, $course->id);
 215  
 216          $context = context_module::instance($module->cmid);
 217          $cmt = new stdClass();
 218          $cmt->context = $context;
 219          $cmt->course = $course;
 220          $cmt->cm = $cm;
 221          $cmt->area = 'database_entry';
 222          $cmt->itemid = $recordid;
 223          $cmt->showcount = true;
 224          $cmt->component = 'mod_data';
 225          $comment = new comment($cmt);
 226  
 227          // Triggering and capturing the event.
 228          $sink = $this->redirectEvents();
 229          $comment->add('New comment');
 230          $events = $sink->get_events();
 231          $this->assertCount(1, $events);
 232          $event = reset($events);
 233  
 234          // Checking that the event contains the expected values.
 235          $this->assertInstanceOf('\mod_data\event\comment_created', $event);
 236          $this->assertEquals($context, $event->get_context());
 237          $url = new moodle_url('/mod/data/view.php', array('id' => $cm->id));
 238          $this->assertEquals($url, $event->get_url());
 239          $this->assertEventContextNotUsed($event);
 240      }
 241  
 242      /**
 243       * Test comment_deleted event.
 244       */
 245      public function test_data_comment_deleted_event() {
 246          global $CFG, $DB;
 247          require_once($CFG->dirroot . '/comment/lib.php');
 248  
 249          $this->resetAfterTest();
 250  
 251          // Create a record for deleting.
 252          $this->setAdminUser();
 253          $course = $this->getDataGenerator()->create_course();
 254          $record = new stdClass();
 255          $record->course = $course->id;
 256          $record->name = "Mod data delete test";
 257          $record->intro = "Some intro of some sort";
 258          $record->comments = 1;
 259  
 260          $module = $this->getDataGenerator()->create_module('data', $record);
 261          $field = data_get_field_new('text', $module);
 262  
 263          $fielddetail = new stdClass();
 264          $fielddetail->name = 'Name';
 265          $fielddetail->description = 'Some name';
 266  
 267          $field->define_field($fielddetail);
 268          $field->insert_field();
 269          $recordid = data_add_record($module);
 270  
 271          $datacontent = array();
 272          $datacontent['fieldid'] = $field->field->id;
 273          $datacontent['recordid'] = $recordid;
 274          $datacontent['content'] = 'Asterix';
 275  
 276          $contentid = $DB->insert_record('data_content', $datacontent);
 277          $cm = get_coursemodule_from_instance('data', $module->id, $course->id);
 278  
 279          $context = context_module::instance($module->cmid);
 280          $cmt = new stdClass();
 281          $cmt->context = $context;
 282          $cmt->course = $course;
 283          $cmt->cm = $cm;
 284          $cmt->area = 'database_entry';
 285          $cmt->itemid = $recordid;
 286          $cmt->showcount = true;
 287          $cmt->component = 'mod_data';
 288          $comment = new comment($cmt);
 289          $newcomment = $comment->add('New comment 1');
 290  
 291          // Triggering and capturing the event.
 292          $sink = $this->redirectEvents();
 293          $comment->delete($newcomment->id);
 294          $events = $sink->get_events();
 295          $this->assertCount(1, $events);
 296          $event = reset($events);
 297  
 298          // Checking that the event contains the expected values.
 299          $this->assertInstanceOf('\mod_data\event\comment_deleted', $event);
 300          $this->assertEquals($context, $event->get_context());
 301          $url = new moodle_url('/mod/data/view.php', array('id' => $module->cmid));
 302          $this->assertEquals($url, $event->get_url());
 303          $this->assertEventContextNotUsed($event);
 304      }
 305  
 306      /**
 307       * Checks that data_user_can_manage_entry will return true if the user
 308       * has the mod/data:manageentries capability.
 309       */
 310      public function test_data_user_can_manage_entry_return_true_with_capability() {
 311  
 312          $this->resetAfterTest();
 313          $testdata = $this->create_user_test_data();
 314  
 315          $user = $testdata['user'];
 316          $course = $testdata['course'];
 317          $roleid = $testdata['roleid'];
 318          $context = $testdata['context'];
 319          $record = $testdata['record'];
 320          $data = new stdClass();
 321  
 322          $this->setUser($user);
 323  
 324          assign_capability('mod/data:manageentries', CAP_ALLOW, $roleid, $context);
 325  
 326          $this->assertTrue(data_user_can_manage_entry($record, $data, $context),
 327              'data_user_can_manage_entry() returns true if the user has mod/data:manageentries capability');
 328      }
 329  
 330      /**
 331       * Checks that data_user_can_manage_entry will return false if the data
 332       * is set to readonly.
 333       */
 334      public function test_data_user_can_manage_entry_return_false_readonly() {
 335  
 336          $this->resetAfterTest();
 337          $testdata = $this->create_user_test_data();
 338  
 339          $user = $testdata['user'];
 340          $course = $testdata['course'];
 341          $roleid = $testdata['roleid'];
 342          $context = $testdata['context'];
 343          $record = $testdata['record'];
 344  
 345          $this->setUser($user);
 346  
 347          // Need to make sure they don't have this capability in order to fall back to
 348          // the other checks.
 349          assign_capability('mod/data:manageentries', CAP_PROHIBIT, $roleid, $context);
 350  
 351          // Causes readonly mode to be enabled.
 352          $data = new stdClass();
 353          $now = time();
 354          // Add a small margin around the periods to prevent errors with slow tests.
 355          $data->timeviewfrom = $now - 1;
 356          $data->timeviewto = $now + 5;
 357  
 358          $this->assertFalse(data_user_can_manage_entry($record, $data, $context),
 359              'data_user_can_manage_entry() returns false if the data is read only');
 360      }
 361  
 362      /**
 363       * Checks that data_user_can_manage_entry will return false if the record
 364       * can't be found in the database.
 365       */
 366      public function test_data_user_can_manage_entry_return_false_no_record() {
 367  
 368          $this->resetAfterTest();
 369          $testdata = $this->create_user_test_data();
 370  
 371          $user = $testdata['user'];
 372          $course = $testdata['course'];
 373          $roleid = $testdata['roleid'];
 374          $context = $testdata['context'];
 375          $record = $testdata['record'];
 376          $data = new stdClass();
 377          // Causes readonly mode to be disabled.
 378          $now = time();
 379          $data->timeviewfrom = $now + 100;
 380          $data->timeviewto = $now - 100;
 381  
 382          $this->setUser($user);
 383  
 384          // Need to make sure they don't have this capability in order to fall back to
 385          // the other checks.
 386          assign_capability('mod/data:manageentries', CAP_PROHIBIT, $roleid, $context);
 387  
 388          // Pass record id instead of object to force DB lookup.
 389          $this->assertFalse(data_user_can_manage_entry(1, $data, $context),
 390              'data_user_can_manage_entry() returns false if the record cannot be found');
 391      }
 392  
 393      /**
 394       * Checks that data_user_can_manage_entry will return false if the record
 395       * isn't owned by the user.
 396       */
 397      public function test_data_user_can_manage_entry_return_false_not_owned_record() {
 398  
 399          $this->resetAfterTest();
 400          $testdata = $this->create_user_test_data();
 401  
 402          $user = $testdata['user'];
 403          $course = $testdata['course'];
 404          $roleid = $testdata['roleid'];
 405          $context = $testdata['context'];
 406          $record = $testdata['record'];
 407          $data = new stdClass();
 408          // Causes readonly mode to be disabled.
 409          $now = time();
 410          $data->timeviewfrom = $now + 100;
 411          $data->timeviewto = $now - 100;
 412          // Make sure the record isn't owned by this user.
 413          $record->userid = $user->id + 1;
 414  
 415          $this->setUser($user);
 416  
 417          // Need to make sure they don't have this capability in order to fall back to
 418          // the other checks.
 419          assign_capability('mod/data:manageentries', CAP_PROHIBIT, $roleid, $context);
 420  
 421          $this->assertFalse(data_user_can_manage_entry($record, $data, $context),
 422              'data_user_can_manage_entry() returns false if the record isnt owned by the user');
 423      }
 424  
 425      /**
 426       * Checks that data_user_can_manage_entry will return true if the data
 427       * doesn't require approval.
 428       */
 429      public function test_data_user_can_manage_entry_return_true_data_no_approval() {
 430  
 431          $this->resetAfterTest();
 432          $testdata = $this->create_user_test_data();
 433  
 434          $user = $testdata['user'];
 435          $course = $testdata['course'];
 436          $roleid = $testdata['roleid'];
 437          $context = $testdata['context'];
 438          $record = $testdata['record'];
 439          $data = new stdClass();
 440          // Causes readonly mode to be disabled.
 441          $now = time();
 442          $data->timeviewfrom = $now + 100;
 443          $data->timeviewto = $now - 100;
 444          // The record doesn't need approval.
 445          $data->approval = false;
 446          // Make sure the record is owned by this user.
 447          $record->userid = $user->id;
 448  
 449          $this->setUser($user);
 450  
 451          // Need to make sure they don't have this capability in order to fall back to
 452          // the other checks.
 453          assign_capability('mod/data:manageentries', CAP_PROHIBIT, $roleid, $context);
 454  
 455          $this->assertTrue(data_user_can_manage_entry($record, $data, $context),
 456              'data_user_can_manage_entry() returns true if the record doesnt require approval');
 457      }
 458  
 459      /**
 460       * Checks that data_user_can_manage_entry will return true if the record
 461       * isn't yet approved.
 462       */
 463      public function test_data_user_can_manage_entry_return_true_record_unapproved() {
 464  
 465          $this->resetAfterTest();
 466          $testdata = $this->create_user_test_data();
 467  
 468          $user = $testdata['user'];
 469          $course = $testdata['course'];
 470          $roleid = $testdata['roleid'];
 471          $context = $testdata['context'];
 472          $record = $testdata['record'];
 473          $data = new stdClass();
 474          // Causes readonly mode to be disabled.
 475          $now = time();
 476          $data->timeviewfrom = $now + 100;
 477          $data->timeviewto = $now - 100;
 478          // The record needs approval.
 479          $data->approval = true;
 480          // Make sure the record is owned by this user.
 481          $record->userid = $user->id;
 482          // The record hasn't yet been approved.
 483          $record->approved = false;
 484  
 485          $this->setUser($user);
 486  
 487          // Need to make sure they don't have this capability in order to fall back to
 488          // the other checks.
 489          assign_capability('mod/data:manageentries', CAP_PROHIBIT, $roleid, $context);
 490  
 491          $this->assertTrue(data_user_can_manage_entry($record, $data, $context),
 492              'data_user_can_manage_entry() returns true if the record is not yet approved');
 493      }
 494  
 495      /**
 496       * Checks that data_user_can_manage_entry will return the 'manageapproved'
 497       * value if the record has already been approved.
 498       */
 499      public function test_data_user_can_manage_entry_return_manageapproved() {
 500  
 501          $this->resetAfterTest();
 502          $testdata = $this->create_user_test_data();
 503  
 504          $user = $testdata['user'];
 505          $course = $testdata['course'];
 506          $roleid = $testdata['roleid'];
 507          $context = $testdata['context'];
 508          $record = $testdata['record'];
 509          $data = new stdClass();
 510          // Causes readonly mode to be disabled.
 511          $now = time();
 512          $data->timeviewfrom = $now + 100;
 513          $data->timeviewto = $now - 100;
 514          // The record needs approval.
 515          $data->approval = true;
 516          // Can the user managed approved records?
 517          $data->manageapproved = false;
 518          // Make sure the record is owned by this user.
 519          $record->userid = $user->id;
 520          // The record has been approved.
 521          $record->approved = true;
 522  
 523          $this->setUser($user);
 524  
 525          // Need to make sure they don't have this capability in order to fall back to
 526          // the other checks.
 527          assign_capability('mod/data:manageentries', CAP_PROHIBIT, $roleid, $context);
 528  
 529          $canmanageentry = data_user_can_manage_entry($record, $data, $context);
 530  
 531          // Make sure the result of the check is what ever the manageapproved setting
 532          // is set to.
 533          $this->assertEquals($data->manageapproved, $canmanageentry,
 534              'data_user_can_manage_entry() returns the manageapproved setting on approved records');
 535      }
 536  
 537      /**
 538       * Helper method to create a set of test data for data_user_can_manage tests
 539       *
 540       * @return array contains user, course, roleid, module, context and record
 541       */
 542      private function create_user_test_data() {
 543          $user = $this->getDataGenerator()->create_user();
 544          $course = $this->getDataGenerator()->create_course();
 545          $roleid = $this->getDataGenerator()->create_role();
 546          $record = new stdClass();
 547          $record->name = "test name";
 548          $record->intro = "test intro";
 549          $record->comments = 1;
 550          $record->course = $course->id;
 551          $record->userid = $user->id;
 552  
 553          $module = $this->getDataGenerator()->create_module('data', $record);
 554          $cm = get_coursemodule_from_instance('data', $module->id, $course->id);
 555          $context = context_module::instance($module->cmid);
 556  
 557          $this->getDataGenerator()->role_assign($roleid, $user->id, $context->id);
 558  
 559          return array(
 560              'user' => $user,
 561              'course' => $course,
 562              'roleid' => $roleid,
 563              'module' => $module,
 564              'context' => $context,
 565              'record' => $record
 566          );
 567      }
 568  
 569      /**
 570       * Tests for mod_data_rating_can_see_item_ratings().
 571       *
 572       * @throws coding_exception
 573       * @throws rating_exception
 574       */
 575      public function test_mod_data_rating_can_see_item_ratings() {
 576          global $DB;
 577  
 578          $this->resetAfterTest();
 579  
 580          // Setup test data.
 581          $course = new stdClass();
 582          $course->groupmode = SEPARATEGROUPS;
 583          $course->groupmodeforce = true;
 584          $course = $this->getDataGenerator()->create_course($course);
 585          $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id));
 586          $cm = get_coursemodule_from_instance('data', $data->id);
 587          $context = context_module::instance($cm->id);
 588  
 589          // Create users.
 590          $user1 = $this->getDataGenerator()->create_user();
 591          $user2 = $this->getDataGenerator()->create_user();
 592          $user3 = $this->getDataGenerator()->create_user();
 593          $user4 = $this->getDataGenerator()->create_user();
 594  
 595          // Groups and stuff.
 596          $role = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST);
 597          $this->getDataGenerator()->enrol_user($user1->id, $course->id, $role->id);
 598          $this->getDataGenerator()->enrol_user($user2->id, $course->id, $role->id);
 599          $this->getDataGenerator()->enrol_user($user3->id, $course->id, $role->id);
 600          $this->getDataGenerator()->enrol_user($user4->id, $course->id, $role->id);
 601  
 602          $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
 603          $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
 604          groups_add_member($group1, $user1);
 605          groups_add_member($group1, $user2);
 606          groups_add_member($group2, $user3);
 607          groups_add_member($group2, $user4);
 608  
 609          // Add data.
 610          $field = data_get_field_new('text', $data);
 611  
 612          $fielddetail = new stdClass();
 613          $fielddetail->name = 'Name';
 614          $fielddetail->description = 'Some name';
 615  
 616          $field->define_field($fielddetail);
 617          $field->insert_field();
 618  
 619          // Add a record with a group id of zero (all participants).
 620          $recordid1 = data_add_record($data, 0);
 621  
 622          $datacontent = array();
 623          $datacontent['fieldid'] = $field->field->id;
 624          $datacontent['recordid'] = $recordid1;
 625          $datacontent['content'] = 'Obelix';
 626          $DB->insert_record('data_content', $datacontent);
 627  
 628          $recordid = data_add_record($data, $group1->id);
 629  
 630          $datacontent = array();
 631          $datacontent['fieldid'] = $field->field->id;
 632          $datacontent['recordid'] = $recordid;
 633          $datacontent['content'] = 'Asterix';
 634          $DB->insert_record('data_content', $datacontent);
 635  
 636          // Now try to access it as various users.
 637          unassign_capability('moodle/site:accessallgroups', $role->id);
 638          // Eveyone should have access to the record with the group id of zero.
 639          $params1 = array('contextid' => 2,
 640                          'component' => 'mod_data',
 641                          'ratingarea' => 'entry',
 642                          'itemid' => $recordid1,
 643                          'scaleid' => 2);
 644  
 645          $params = array('contextid' => 2,
 646                          'component' => 'mod_data',
 647                          'ratingarea' => 'entry',
 648                          'itemid' => $recordid,
 649                          'scaleid' => 2);
 650  
 651          $this->setUser($user1);
 652          $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
 653          $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
 654          $this->setUser($user2);
 655          $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
 656          $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
 657          $this->setUser($user3);
 658          $this->assertFalse(mod_data_rating_can_see_item_ratings($params));
 659          $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
 660          $this->setUser($user4);
 661          $this->assertFalse(mod_data_rating_can_see_item_ratings($params));
 662          $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
 663  
 664          // Now try with accessallgroups cap and make sure everything is visible.
 665          assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $role->id, $context->id);
 666          $this->setUser($user1);
 667          $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
 668          $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
 669          $this->setUser($user2);
 670          $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
 671          $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
 672          $this->setUser($user3);
 673          $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
 674          $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
 675          $this->setUser($user4);
 676          $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
 677          $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
 678  
 679          // Change group mode and verify visibility.
 680          $course->groupmode = VISIBLEGROUPS;
 681          $DB->update_record('course', $course);
 682          unassign_capability('moodle/site:accessallgroups', $role->id);
 683          $this->setUser($user1);
 684          $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
 685          $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
 686          $this->setUser($user2);
 687          $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
 688          $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
 689          $this->setUser($user3);
 690          $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
 691          $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
 692          $this->setUser($user4);
 693          $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
 694          $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
 695  
 696      }
 697  
 698      /**
 699       * Tests for mod_data_refresh_events.
 700       */
 701      public function test_data_refresh_events() {
 702          global $DB;
 703          $this->resetAfterTest();
 704          $this->setAdminUser();
 705  
 706          $timeopen = time();
 707          $timeclose = time() + 86400;
 708  
 709          $course = $this->getDataGenerator()->create_course();
 710          $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
 711          $params['course'] = $course->id;
 712          $params['timeavailablefrom'] = $timeopen;
 713          $params['timeavailableto'] = $timeclose;
 714          $data = $generator->create_instance($params);
 715  
 716          // Normal case, with existing course.
 717          $this->assertTrue(data_refresh_events($course->id));
 718          $eventparams = array('modulename' => 'data', 'instance' => $data->id, 'eventtype' => 'open');
 719          $openevent = $DB->get_record('event', $eventparams, '*', MUST_EXIST);
 720          $this->assertEquals($openevent->timestart, $timeopen);
 721  
 722          $eventparams = array('modulename' => 'data', 'instance' => $data->id, 'eventtype' => 'close');
 723          $closeevent = $DB->get_record('event', $eventparams, '*', MUST_EXIST);
 724          $this->assertEquals($closeevent->timestart, $timeclose);
 725          // In case the course ID is passed as a numeric string.
 726          $this->assertTrue(data_refresh_events('' . $course->id));
 727          // Course ID not provided.
 728          $this->assertTrue(data_refresh_events());
 729          $eventparams = array('modulename' => 'data');
 730          $events = $DB->get_records('event', $eventparams);
 731          foreach ($events as $event) {
 732              if ($event->modulename === 'data' && $event->instance === $data->id && $event->eventtype === 'open') {
 733                  $this->assertEquals($event->timestart, $timeopen);
 734              }
 735              if ($event->modulename === 'data' && $event->instance === $data->id && $event->eventtype === 'close') {
 736                  $this->assertEquals($event->timestart, $timeclose);
 737              }
 738          }
 739      }
 740  
 741      /**
 742       * Data provider for tests of data_get_config.
 743       *
 744       * @return array
 745       */
 746      public function data_get_config_provider() {
 747          $initialdata = (object) [
 748              'template_foo' => true,
 749              'template_bar' => false,
 750              'template_baz' => null,
 751          ];
 752  
 753          $database = (object) [
 754              'config' => json_encode($initialdata),
 755          ];
 756  
 757          return [
 758              'Return full dataset (no key/default)' => [
 759                  [$database],
 760                  $initialdata,
 761              ],
 762              'Return full dataset (no default)' => [
 763                  [$database, null],
 764                  $initialdata,
 765              ],
 766              'Return full dataset' => [
 767                  [$database, null, null],
 768                  $initialdata,
 769              ],
 770              'Return requested key only, value true, no default' => [
 771                  [$database, 'template_foo'],
 772                  true,
 773              ],
 774              'Return requested key only, value false, no default' => [
 775                  [$database, 'template_bar'],
 776                  false,
 777              ],
 778              'Return requested key only, value null, no default' => [
 779                  [$database, 'template_baz'],
 780                  null,
 781              ],
 782              'Return unknown key, value null, no default' => [
 783                  [$database, 'template_bum'],
 784                  null,
 785              ],
 786              'Return requested key only, value true, default null' => [
 787                  [$database, 'template_foo', null],
 788                  true,
 789              ],
 790              'Return requested key only, value false, default null' => [
 791                  [$database, 'template_bar', null],
 792                  false,
 793              ],
 794              'Return requested key only, value null, default null' => [
 795                  [$database, 'template_baz', null],
 796                  null,
 797              ],
 798              'Return unknown key, value null, default null' => [
 799                  [$database, 'template_bum', null],
 800                  null,
 801              ],
 802              'Return requested key only, value true, default 42' => [
 803                  [$database, 'template_foo', 42],
 804                  true,
 805              ],
 806              'Return requested key only, value false, default 42' => [
 807                  [$database, 'template_bar', 42],
 808                  false,
 809              ],
 810              'Return requested key only, value null, default 42' => [
 811                  [$database, 'template_baz', 42],
 812                  null,
 813              ],
 814              'Return unknown key, value null, default 42' => [
 815                  [$database, 'template_bum', 42],
 816                  42,
 817              ],
 818          ];
 819      }
 820  
 821      /**
 822       * Tests for data_get_config.
 823       *
 824       * @dataProvider    data_get_config_provider
 825       * @param   array   $funcargs       The args to pass to data_get_config
 826       * @param   mixed   $expectation    The expected value
 827       */
 828      public function test_data_get_config($funcargs, $expectation) {
 829          $this->assertEquals($expectation, call_user_func_array('data_get_config', $funcargs));
 830      }
 831  
 832      /**
 833       * Data provider for tests of data_set_config.
 834       *
 835       * @return array
 836       */
 837      public function data_set_config_provider() {
 838          $basevalue = (object) ['id' => rand(1, 1000)];
 839          $config = [
 840              'template_foo'  => true,
 841              'template_bar'  => false,
 842          ];
 843  
 844          $withvalues = clone $basevalue;
 845          $withvalues->config = json_encode((object) $config);
 846  
 847          return [
 848              'Empty config, New value' => [
 849                  $basevalue,
 850                  'etc',
 851                  'newvalue',
 852                  true,
 853                  json_encode((object) ['etc' => 'newvalue'])
 854              ],
 855              'Has config, New value' => [
 856                  clone $withvalues,
 857                  'etc',
 858                  'newvalue',
 859                  true,
 860                  json_encode((object) array_merge($config, ['etc' => 'newvalue']))
 861              ],
 862              'Has config, Update value, string' => [
 863                  clone $withvalues,
 864                  'template_foo',
 865                  'newvalue',
 866                  true,
 867                  json_encode((object) array_merge($config, ['template_foo' => 'newvalue']))
 868              ],
 869              'Has config, Update value, true' => [
 870                  clone $withvalues,
 871                  'template_bar',
 872                  true,
 873                  true,
 874                  json_encode((object) array_merge($config, ['template_bar' => true]))
 875              ],
 876              'Has config, Update value, false' => [
 877                  clone $withvalues,
 878                  'template_foo',
 879                  false,
 880                  true,
 881                  json_encode((object) array_merge($config, ['template_foo' => false]))
 882              ],
 883              'Has config, Update value, null' => [
 884                  clone $withvalues,
 885                  'template_foo',
 886                  null,
 887                  true,
 888                  json_encode((object) array_merge($config, ['template_foo' => null]))
 889              ],
 890              'Has config, No update, value true' => [
 891                  clone $withvalues,
 892                  'template_foo',
 893                  true,
 894                  false,
 895                  $withvalues->config,
 896              ],
 897          ];
 898      }
 899  
 900      /**
 901       * Tests for data_set_config.
 902       *
 903       * @dataProvider    data_set_config_provider
 904       * @param   object  $database       The example row for the entry
 905       * @param   string  $key            The config key to set
 906       * @param   mixed   $value          The value of the key
 907       * @param   bool    $expectupdate   Whether we expected an update
 908       * @param   mixed   $newconfigvalue The expected value
 909       */
 910      public function test_data_set_config($database, $key, $value, $expectupdate, $newconfigvalue) {
 911          global $DB;
 912  
 913          // Mock the database.
 914          // Note: Use the actual test class here rather than the abstract because are testing concrete methods.
 915          $this->DB = $DB;
 916          $DB = $this->getMockBuilder(get_class($DB))
 917              ->setMethods(['set_field'])
 918              ->getMock();
 919  
 920          $DB->expects($this->exactly((int) $expectupdate))
 921              ->method('set_field')
 922              ->with(
 923                  'data',
 924                  'config',
 925                  $newconfigvalue,
 926                  ['id' => $database->id]
 927              );
 928  
 929          // Perform the update.
 930          data_set_config($database, $key, $value);
 931  
 932          // Ensure that the value was updated by reference in $database.
 933          $config = json_decode($database->config);
 934          $this->assertEquals($value, $config->$key);
 935      }
 936  
 937      /**
 938       * Test data_view
 939       * @return void
 940       */
 941      public function test_data_view() {
 942          global $CFG;
 943  
 944          $CFG->enablecompletion = 1;
 945          $this->resetAfterTest();
 946  
 947          $this->setAdminUser();
 948          // Setup test data.
 949          $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
 950          $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id),
 951                                                              array('completion' => 2, 'completionview' => 1));
 952          $context = context_module::instance($data->cmid);
 953          $cm = get_coursemodule_from_instance('data', $data->id);
 954  
 955          // Trigger and capture the event.
 956          $sink = $this->redirectEvents();
 957  
 958          data_view($data, $course, $cm, $context);
 959  
 960          $events = $sink->get_events();
 961          // 2 additional events thanks to completion.
 962          $this->assertCount(3, $events);
 963          $event = array_shift($events);
 964  
 965          // Checking that the event contains the expected values.
 966          $this->assertInstanceOf('\mod_data\event\course_module_viewed', $event);
 967          $this->assertEquals($context, $event->get_context());
 968          $moodleurl = new \moodle_url('/mod/data/view.php', array('id' => $cm->id));
 969          $this->assertEquals($moodleurl, $event->get_url());
 970          $this->assertEventContextNotUsed($event);
 971          $this->assertNotEmpty($event->get_name());
 972  
 973          // Check completion status.
 974          $completion = new completion_info($course);
 975          $completiondata = $completion->get_data($cm);
 976          $this->assertEquals(1, $completiondata->completionstate);
 977      }
 978  
 979      public function test_mod_data_get_tagged_records() {
 980          $this->resetAfterTest();
 981          $this->setAdminUser();
 982  
 983          // Setup test data.
 984          $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
 985          $course1 = $this->getDataGenerator()->create_course();
 986  
 987          $fieldrecord = new StdClass();
 988          $fieldrecord->name = 'field-1';
 989          $fieldrecord->type = 'text';
 990  
 991          $data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id, 'approval' => true));
 992          $field1 = $datagenerator->create_field($fieldrecord, $data1);
 993  
 994          $datagenerator->create_entry($data1, [$field1->field->id => 'value11'], 0, ['Cats', 'Dogs']);
 995          $datagenerator->create_entry($data1, [$field1->field->id => 'value12'], 0, ['Cats', 'mice']);
 996          $datagenerator->create_entry($data1, [$field1->field->id => 'value13'], 0, ['Cats']);
 997          $datagenerator->create_entry($data1, [$field1->field->id => 'value14'], 0);
 998  
 999          $tag = core_tag_tag::get_by_name(0, 'Cats');
1000  
1001          // Admin can see everything.
1002          $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1003          $this->assertStringContainsString('value11', $res->content);
1004          $this->assertStringContainsString('value12', $res->content);
1005          $this->assertStringContainsString('value13', $res->content);
1006          $this->assertStringNotContainsString('value14', $res->content);
1007      }
1008  
1009      public function test_mod_data_get_tagged_records_approval() {
1010          global $DB;
1011  
1012          $this->resetAfterTest();
1013          $this->setAdminUser();
1014  
1015          // Setup test data.
1016          $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
1017          $course2 = $this->getDataGenerator()->create_course();
1018          $course1 = $this->getDataGenerator()->create_course();
1019  
1020          $fieldrecord = new StdClass();
1021          $fieldrecord->name = 'field-1';
1022          $fieldrecord->type = 'text';
1023  
1024          $data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id));
1025          $field1 = $datagenerator->create_field($fieldrecord, $data1);
1026          $data2 = $this->getDataGenerator()->create_module('data', array('course' => $course2->id, 'approval' => true));
1027          $field2 = $datagenerator->create_field($fieldrecord, $data2);
1028  
1029          $record11 = $datagenerator->create_entry($data1, [$field1->field->id => 'value11'], 0, ['Cats', 'Dogs']);
1030          $record21 = $datagenerator->create_entry($data2, [$field2->field->id => 'value21'], 0, ['Cats'], ['approved' => false]);
1031          $tag = core_tag_tag::get_by_name(0, 'Cats');
1032  
1033          // Admin can see everything.
1034          $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1035          $this->assertStringContainsString('value11', $res->content);
1036          $this->assertStringContainsString('value21', $res->content);
1037          $this->assertEmpty($res->prevpageurl);
1038          $this->assertEmpty($res->nextpageurl);
1039  
1040          // Create and enrol a user.
1041          $student = self::getDataGenerator()->create_user();
1042          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1043          $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
1044          $this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual');
1045          $this->setUser($student);
1046  
1047          // User can search data records inside a course.
1048          core_tag_index_builder::reset_caches();
1049          $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1050  
1051          $this->assertStringContainsString('value11', $res->content);
1052          $this->assertStringNotContainsString('value21', $res->content);
1053  
1054          $recordtoupdate = new stdClass();
1055          $recordtoupdate->id = $record21;
1056          $recordtoupdate->approved = true;
1057          $DB->update_record('data_records', $recordtoupdate);
1058  
1059          core_tag_index_builder::reset_caches();
1060          $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1061  
1062          $this->assertStringContainsString('value11', $res->content);
1063          $this->assertStringContainsString('value21', $res->content);
1064      }
1065  
1066      public function test_mod_data_get_tagged_records_time() {
1067          global $DB;
1068  
1069          $this->resetAfterTest();
1070          $this->setAdminUser();
1071  
1072          // Setup test data.
1073          $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
1074          $course2 = $this->getDataGenerator()->create_course();
1075          $course1 = $this->getDataGenerator()->create_course();
1076  
1077          $fieldrecord = new StdClass();
1078          $fieldrecord->name = 'field-1';
1079          $fieldrecord->type = 'text';
1080  
1081          $timefrom = time() - YEARSECS;
1082          $timeto = time() - WEEKSECS;
1083  
1084          $data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id, 'approval' => true));
1085          $field1 = $datagenerator->create_field($fieldrecord, $data1);
1086          $data2 = $this->getDataGenerator()->create_module('data', array('course' => $course2->id,
1087                                                                          'timeviewfrom' => $timefrom,
1088                                                                          'timeviewto'   => $timeto));
1089          $field2 = $datagenerator->create_field($fieldrecord, $data2);
1090          $record11 = $datagenerator->create_entry($data1, [$field1->field->id => 'value11'], 0, ['Cats', 'Dogs']);
1091          $record21 = $datagenerator->create_entry($data2, [$field2->field->id => 'value21'], 0, ['Cats']);
1092          $tag = core_tag_tag::get_by_name(0, 'Cats');
1093  
1094          // Admin can see everything.
1095          $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1096          $this->assertStringContainsString('value11', $res->content);
1097          $this->assertStringContainsString('value21', $res->content);
1098          $this->assertEmpty($res->prevpageurl);
1099          $this->assertEmpty($res->nextpageurl);
1100  
1101          // Create and enrol a user.
1102          $student = self::getDataGenerator()->create_user();
1103          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1104          $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
1105          $this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual');
1106          $this->setUser($student);
1107  
1108          // User can search data records inside a course.
1109          core_tag_index_builder::reset_caches();
1110          $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1111  
1112          $this->assertStringContainsString('value11', $res->content);
1113          $this->assertStringNotContainsString('value21', $res->content);
1114  
1115          $data2->timeviewto = time() + YEARSECS;
1116          $DB->update_record('data', $data2);
1117  
1118          core_tag_index_builder::reset_caches();
1119          $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1120  
1121          $this->assertStringContainsString('value11', $res->content);
1122          $this->assertStringContainsString('value21', $res->content);
1123      }
1124  
1125      public function test_mod_data_get_tagged_records_course_enrolment() {
1126          global $DB;
1127  
1128          $this->resetAfterTest();
1129          $this->setAdminUser();
1130  
1131          // Setup test data.
1132          $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
1133          $course2 = $this->getDataGenerator()->create_course();
1134          $course1 = $this->getDataGenerator()->create_course();
1135  
1136          $fieldrecord = new StdClass();
1137          $fieldrecord->name = 'field-1';
1138          $fieldrecord->type = 'text';
1139  
1140          $data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id, 'approval' => true));
1141          $field1 = $datagenerator->create_field($fieldrecord, $data1);
1142          $data2 = $this->getDataGenerator()->create_module('data', array('course' => $course2->id));
1143          $field2 = $datagenerator->create_field($fieldrecord, $data2);
1144  
1145          $record11 = $datagenerator->create_entry($data1, [$field1->field->id => 'value11'], 0, ['Cats', 'Dogs']);
1146          $record21 = $datagenerator->create_entry($data2, [$field2->field->id => 'value21'], 0, ['Cats']);
1147          $tag = core_tag_tag::get_by_name(0, 'Cats');
1148  
1149          // Admin can see everything.
1150          $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1151          $this->assertStringContainsString('value11', $res->content);
1152          $this->assertStringContainsString('value21', $res->content);
1153          $this->assertEmpty($res->prevpageurl);
1154          $this->assertEmpty($res->nextpageurl);
1155  
1156          // Create and enrol a user.
1157          $student = self::getDataGenerator()->create_user();
1158          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1159          $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
1160          $this->setUser($student);
1161          core_tag_index_builder::reset_caches();
1162  
1163          // User can search data records inside a course.
1164          $coursecontext = context_course::instance($course1->id);
1165          $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1166  
1167          $this->assertStringContainsString('value11', $res->content);
1168          $this->assertStringNotContainsString('value21', $res->content);
1169  
1170          $this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual');
1171  
1172          core_tag_index_builder::reset_caches();
1173          $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1174  
1175          $this->assertStringContainsString('value11', $res->content);
1176          $this->assertStringContainsString('value21', $res->content);
1177      }
1178  
1179      public function test_mod_data_get_tagged_records_course_groups() {
1180          global $DB;
1181  
1182          $this->resetAfterTest();
1183          $this->setAdminUser();
1184  
1185          // Setup test data.
1186          $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
1187          $course2 = $this->getDataGenerator()->create_course();
1188          $course1 = $this->getDataGenerator()->create_course();
1189  
1190          $groupa = $this->getDataGenerator()->create_group(array('courseid' => $course2->id, 'name' => 'groupA'));
1191          $groupb = $this->getDataGenerator()->create_group(array('courseid' => $course2->id, 'name' => 'groupB'));
1192  
1193          $fieldrecord = new StdClass();
1194          $fieldrecord->name = 'field-1';
1195          $fieldrecord->type = 'text';
1196  
1197          $data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id, 'approval' => true));
1198          $field1 = $datagenerator->create_field($fieldrecord, $data1);
1199          $data2 = $this->getDataGenerator()->create_module('data', array('course' => $course2->id));
1200          $field2 = $datagenerator->create_field($fieldrecord, $data2);
1201          set_coursemodule_groupmode($data2->cmid, SEPARATEGROUPS);
1202  
1203          $record11 = $datagenerator->create_entry($data1, [$field1->field->id => 'value11'],
1204                  0, ['Cats', 'Dogs']);
1205          $record21 = $datagenerator->create_entry($data2, [$field2->field->id => 'value21'],
1206                  $groupa->id, ['Cats']);
1207          $record22 = $datagenerator->create_entry($data2, [$field2->field->id => 'value22'],
1208                  $groupb->id, ['Cats']);
1209          $tag = core_tag_tag::get_by_name(0, 'Cats');
1210  
1211          // Admin can see everything.
1212          $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1213          $this->assertStringContainsString('value11', $res->content);
1214          $this->assertStringContainsString('value21', $res->content);
1215          $this->assertStringContainsString('value22', $res->content);
1216          $this->assertEmpty($res->prevpageurl);
1217          $this->assertEmpty($res->nextpageurl);
1218  
1219          // Create and enrol a user.
1220          $student = self::getDataGenerator()->create_user();
1221          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1222          $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
1223          $this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual');
1224          groups_add_member($groupa, $student);
1225          $this->setUser($student);
1226          core_tag_index_builder::reset_caches();
1227  
1228          // User can search data records inside a course.
1229          $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1230  
1231          $this->assertStringContainsString('value11', $res->content);
1232          $this->assertStringContainsString('value21', $res->content);
1233          $this->assertStringNotContainsString('value22', $res->content);
1234  
1235          groups_add_member($groupb, $student);
1236          core_tag_index_builder::reset_caches();
1237          $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1238  
1239          $this->assertStringContainsString('value11', $res->content);
1240          $this->assertStringContainsString('value21', $res->content);
1241          $this->assertStringContainsString('value22', $res->content);
1242      }
1243  
1244      /**
1245       * Test check_updates_since callback.
1246       */
1247      public function test_check_updates_since() {
1248          global $DB;
1249          $this->resetAfterTest();
1250          $this->setAdminUser();
1251          $course = $this->getDataGenerator()->create_course();
1252          // Create user.
1253          $student = self::getDataGenerator()->create_user();
1254          // User enrolment.
1255          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1256          $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual');
1257          $this->setCurrentTimeStart();
1258          $record = array(
1259              'course' => $course->id,
1260          );
1261          $data = $this->getDataGenerator()->create_module('data', $record);
1262          $cm = get_coursemodule_from_instance('data', $data->id, $course->id);
1263          $cm = cm_info::create($cm);
1264          $this->setUser($student);
1265  
1266          // Check that upon creation, the updates are only about the new configuration created.
1267          $onehourago = time() - HOURSECS;
1268          $updates = data_check_updates_since($cm, $onehourago);
1269          foreach ($updates as $el => $val) {
1270              if ($el == 'configuration') {
1271                  $this->assertTrue($val->updated);
1272                  $this->assertTimeCurrent($val->timeupdated);
1273              } else {
1274                  $this->assertFalse($val->updated);
1275              }
1276          }
1277  
1278          // Add a couple of entries.
1279          $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
1280          $fieldtypes = array('checkbox', 'date');
1281  
1282          $count = 1;
1283          // Creating test Fields with default parameter values.
1284          foreach ($fieldtypes as $fieldtype) {
1285              // Creating variables dynamically.
1286              $fieldname = 'field-' . $count;
1287              $record = new StdClass();
1288              $record->name = $fieldname;
1289              $record->type = $fieldtype;
1290              $record->required = 1;
1291  
1292              ${$fieldname} = $datagenerator->create_field($record, $data);
1293              $count++;
1294          }
1295  
1296          $fields = $DB->get_records('data_fields', array('dataid' => $data->id), 'id');
1297  
1298          $contents = array();
1299          $contents[] = array('opt1', 'opt2', 'opt3', 'opt4');
1300          $contents[] = '01-01-2037'; // It should be lower than 2038, to avoid failing on 32-bit windows.
1301          $count = 0;
1302          $fieldcontents = array();
1303          foreach ($fields as $fieldrecord) {
1304              $fieldcontents[$fieldrecord->id] = $contents[$count++];
1305          }
1306  
1307          $datarecor1did = $datagenerator->create_entry($data, $fieldcontents);
1308          $datarecor2did = $datagenerator->create_entry($data, $fieldcontents);
1309          $records = $DB->get_records('data_records', array('dataid' => $data->id));
1310          $this->assertCount(2, $records);
1311          // Check we received the entries updated.
1312          $updates = data_check_updates_since($cm, $onehourago);
1313          $this->assertTrue($updates->entries->updated);
1314          $this->assertEqualsCanonicalizing([$datarecor1did, $datarecor2did], $updates->entries->itemids);
1315      }
1316  
1317      public function test_data_core_calendar_provide_event_action_in_hidden_section() {
1318          global $CFG;
1319  
1320          $this->resetAfterTest();
1321  
1322          $this->setAdminUser();
1323  
1324          // Create a course.
1325          $course = $this->getDataGenerator()->create_course();
1326  
1327          // Create a student.
1328          $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1329  
1330          // Create a database activity.
1331          $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id,
1332                  'timeavailablefrom' => time() - DAYSECS, 'timeavailableto' => time() + DAYSECS));
1333  
1334          // Create a calendar event.
1335          $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1336  
1337          // Set sections 0 as hidden.
1338          set_section_visible($course->id, 0, 0);
1339  
1340          // Now, log out.
1341          $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
1342          $this->setUser();
1343  
1344          // Create an action factory.
1345          $factory = new \core_calendar\action_factory();
1346  
1347          // Decorate action event for the student.
1348          $actionevent = mod_data_core_calendar_provide_event_action($event, $factory, $student->id);
1349  
1350          // Confirm the event is not shown at all.
1351          $this->assertNull($actionevent);
1352      }
1353  
1354      public function test_data_core_calendar_provide_event_action_for_non_user() {
1355          global $CFG;
1356  
1357          $this->resetAfterTest();
1358  
1359          $this->setAdminUser();
1360  
1361          // Create a course.
1362          $course = $this->getDataGenerator()->create_course();
1363  
1364          // Create a database activity.
1365          $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id,
1366              'timeavailablefrom' => time() - DAYSECS, 'timeavailableto' => time() + DAYSECS));
1367  
1368          // Create a calendar event.
1369          $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1370  
1371          // Now, log out.
1372          $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
1373          $this->setUser();
1374  
1375          // Create an action factory.
1376          $factory = new \core_calendar\action_factory();
1377  
1378          // Decorate action event.
1379          $actionevent = mod_data_core_calendar_provide_event_action($event, $factory);
1380  
1381          // Confirm the event is not shown at all.
1382          $this->assertNull($actionevent);
1383      }
1384  
1385      public function test_data_core_calendar_provide_event_action_open() {
1386          $this->resetAfterTest();
1387  
1388          $this->setAdminUser();
1389  
1390          // Create a course.
1391          $course = $this->getDataGenerator()->create_course();
1392  
1393          // Create a database activity.
1394          $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id,
1395              'timeavailablefrom' => time() - DAYSECS, 'timeavailableto' => time() + DAYSECS));
1396  
1397          // Create a calendar event.
1398          $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1399  
1400          // Create an action factory.
1401          $factory = new \core_calendar\action_factory();
1402  
1403          // Decorate action event.
1404          $actionevent = mod_data_core_calendar_provide_event_action($event, $factory);
1405  
1406          // Confirm the event was decorated.
1407          $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
1408          $this->assertEquals(get_string('add', 'data'), $actionevent->get_name());
1409          $this->assertInstanceOf('moodle_url', $actionevent->get_url());
1410          $this->assertEquals(1, $actionevent->get_item_count());
1411          $this->assertTrue($actionevent->is_actionable());
1412      }
1413  
1414      public function test_data_core_calendar_provide_event_action_open_for_user() {
1415          global $CFG;
1416  
1417          $this->resetAfterTest();
1418  
1419          $this->setAdminUser();
1420  
1421          // Create a course.
1422          $course = $this->getDataGenerator()->create_course();
1423  
1424          // Create a student.
1425          $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1426  
1427          // Create a database activity.
1428          $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id,
1429              'timeavailablefrom' => time() - DAYSECS, 'timeavailableto' => time() + DAYSECS));
1430  
1431          // Create a calendar event.
1432          $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1433  
1434          // Now log out.
1435          $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
1436          $this->setUser();
1437  
1438          // Create an action factory.
1439          $factory = new \core_calendar\action_factory();
1440  
1441          // Decorate action event for the student.
1442          $actionevent = mod_data_core_calendar_provide_event_action($event, $factory, $student->id);
1443  
1444          // Confirm the event was decorated.
1445          $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
1446          $this->assertEquals(get_string('add', 'data'), $actionevent->get_name());
1447          $this->assertInstanceOf('moodle_url', $actionevent->get_url());
1448          $this->assertEquals(1, $actionevent->get_item_count());
1449          $this->assertTrue($actionevent->is_actionable());
1450      }
1451  
1452      public function test_data_core_calendar_provide_event_action_closed() {
1453          $this->resetAfterTest();
1454  
1455          $this->setAdminUser();
1456  
1457          // Create a course.
1458          $course = $this->getDataGenerator()->create_course();
1459  
1460          // Create a database activity.
1461          $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id,
1462              'timeavailableto' => time() - DAYSECS));
1463  
1464          // Create a calendar event.
1465          $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1466  
1467          // Create an action factory.
1468          $factory = new \core_calendar\action_factory();
1469  
1470          // Decorate action event.
1471          $actionevent = mod_data_core_calendar_provide_event_action($event, $factory);
1472  
1473          // No event on the dashboard if module is closed.
1474          $this->assertNull($actionevent);
1475      }
1476  
1477      public function test_data_core_calendar_provide_event_action_closed_for_user() {
1478          $this->resetAfterTest();
1479  
1480          $this->setAdminUser();
1481  
1482          // Create a course.
1483          $course = $this->getDataGenerator()->create_course();
1484  
1485          // Create a student.
1486          $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1487  
1488          // Create a database activity.
1489          $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id,
1490              'timeavailableto' => time() - DAYSECS));
1491  
1492          // Create a calendar event.
1493          $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1494  
1495          // Now log out.
1496          $this->setUser();
1497  
1498          // Create an action factory.
1499          $factory = new \core_calendar\action_factory();
1500  
1501          // Decorate action event for the student.
1502          $actionevent = mod_data_core_calendar_provide_event_action($event, $factory, $student->id);
1503  
1504          // No event on the dashboard if module is closed.
1505          $this->assertNull($actionevent);
1506      }
1507  
1508      public function test_data_core_calendar_provide_event_action_open_in_future() {
1509          $this->resetAfterTest();
1510  
1511          $this->setAdminUser();
1512  
1513          // Create a course.
1514          $course = $this->getDataGenerator()->create_course();
1515  
1516          // Create a database activity.
1517          $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id,
1518              'timeavailablefrom' => time() + DAYSECS));
1519  
1520          // Create a calendar event.
1521          $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1522  
1523          // Create an action factory.
1524          $factory = new \core_calendar\action_factory();
1525  
1526          // Decorate action event.
1527          $actionevent = mod_data_core_calendar_provide_event_action($event, $factory);
1528  
1529          // Confirm the event was decorated.
1530          $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
1531          $this->assertEquals(get_string('add', 'data'), $actionevent->get_name());
1532          $this->assertInstanceOf('moodle_url', $actionevent->get_url());
1533          $this->assertEquals(1, $actionevent->get_item_count());
1534          $this->assertFalse($actionevent->is_actionable());
1535      }
1536  
1537      public function test_data_core_calendar_provide_event_action_open_in_future_for_user() {
1538          global $CFG;
1539  
1540          $this->resetAfterTest();
1541  
1542          $this->setAdminUser();
1543  
1544          // Create a course.
1545          $course = $this->getDataGenerator()->create_course();
1546  
1547          // Create a student.
1548          $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1549  
1550          // Create a database activity.
1551          $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id,
1552              'timeavailablefrom' => time() + DAYSECS));
1553  
1554          // Create a calendar event.
1555          $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1556  
1557          // Now log out.
1558          $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
1559          $this->setUser();
1560  
1561          // Create an action factory.
1562          $factory = new \core_calendar\action_factory();
1563  
1564          // Decorate action event for the student.
1565          $actionevent = mod_data_core_calendar_provide_event_action($event, $factory, $student->id);
1566  
1567          // Confirm the event was decorated.
1568          $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
1569          $this->assertEquals(get_string('add', 'data'), $actionevent->get_name());
1570          $this->assertInstanceOf('moodle_url', $actionevent->get_url());
1571          $this->assertEquals(1, $actionevent->get_item_count());
1572          $this->assertFalse($actionevent->is_actionable());
1573      }
1574  
1575      public function test_data_core_calendar_provide_event_action_no_time_specified() {
1576          $this->resetAfterTest();
1577  
1578          $this->setAdminUser();
1579  
1580          // Create a course.
1581          $course = $this->getDataGenerator()->create_course();
1582  
1583          // Create a database activity.
1584          $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id));
1585  
1586          // Create a calendar event.
1587          $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1588  
1589          // Create an action factory.
1590          $factory = new \core_calendar\action_factory();
1591  
1592          // Decorate action event.
1593          $actionevent = mod_data_core_calendar_provide_event_action($event, $factory);
1594  
1595          // Confirm the event was decorated.
1596          $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
1597          $this->assertEquals(get_string('add', 'data'), $actionevent->get_name());
1598          $this->assertInstanceOf('moodle_url', $actionevent->get_url());
1599          $this->assertEquals(1, $actionevent->get_item_count());
1600          $this->assertTrue($actionevent->is_actionable());
1601      }
1602  
1603      public function test_data_core_calendar_provide_event_action_no_time_specified_for_user() {
1604          global $CFG;
1605  
1606          $this->resetAfterTest();
1607  
1608          $this->setAdminUser();
1609  
1610          // Create a course.
1611          $course = $this->getDataGenerator()->create_course();
1612  
1613          // Create a student.
1614          $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1615  
1616          // Create a database activity.
1617          $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id));
1618  
1619          // Create a calendar event.
1620          $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1621  
1622          // Now log out.
1623          $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
1624          $this->setUser();
1625  
1626          // Create an action factory.
1627          $factory = new \core_calendar\action_factory();
1628  
1629          // Decorate action event for the student.
1630          $actionevent = mod_data_core_calendar_provide_event_action($event, $factory, $student->id);
1631  
1632          // Confirm the event was decorated.
1633          $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
1634          $this->assertEquals(get_string('add', 'data'), $actionevent->get_name());
1635          $this->assertInstanceOf('moodle_url', $actionevent->get_url());
1636          $this->assertEquals(1, $actionevent->get_item_count());
1637          $this->assertTrue($actionevent->is_actionable());
1638      }
1639  
1640      /**
1641       * Creates an action event.
1642       *
1643       * @param int $courseid
1644       * @param int $instanceid The data id.
1645       * @param string $eventtype The event type. eg. DATA_EVENT_TYPE_OPEN.
1646       * @param int|null $timestart The start timestamp for the event
1647       * @return bool|calendar_event
1648       */
1649      private function create_action_event($courseid, $instanceid, $eventtype, $timestart = null) {
1650          $event = new stdClass();
1651          $event->name = 'Calendar event';
1652          $event->modulename  = 'data';
1653          $event->courseid = $courseid;
1654          $event->instance = $instanceid;
1655          $event->type = CALENDAR_EVENT_TYPE_ACTION;
1656          $event->eventtype = $eventtype;
1657          if ($timestart) {
1658              $event->timestart = $timestart;
1659          } else {
1660              $event->timestart = time();
1661          }
1662  
1663          return calendar_event::create($event);
1664      }
1665  
1666      /**
1667       * Test the callback responsible for returning the completion rule descriptions.
1668       * This function should work given either an instance of the module (cm_info), such as when checking the active rules,
1669       * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type.
1670       */
1671      public function test_mod_data_completion_get_active_rule_descriptions() {
1672          $this->resetAfterTest();
1673          $this->setAdminUser();
1674  
1675          // Two activities, both with automatic completion. One has the 'completionentries' rule, one doesn't.
1676          $course = $this->getDataGenerator()->create_course(['enablecompletion' => 2]);
1677          $data1 = $this->getDataGenerator()->create_module('data', [
1678              'course' => $course->id,
1679              'completion' => 2,
1680              'completionentries' => 3
1681          ]);
1682          $data2 = $this->getDataGenerator()->create_module('data', [
1683              'course' => $course->id,
1684              'completion' => 2,
1685              'completionentries' => 0
1686          ]);
1687          $cm1 = cm_info::create(get_coursemodule_from_instance('data', $data1->id));
1688          $cm2 = cm_info::create(get_coursemodule_from_instance('data', $data2->id));
1689  
1690          // Data for the stdClass input type.
1691          // This type of input would occur when checking the default completion rules for an activity type, where we don't have
1692          // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info.
1693          $moddefaults = new stdClass();
1694          $moddefaults->customdata = ['customcompletionrules' => ['completionentries' => 3]];
1695          $moddefaults->completion = 2;
1696  
1697          $activeruledescriptions = [get_string('completionentriesdesc', 'data', 3)];
1698          $this->assertEquals(mod_data_get_completion_active_rule_descriptions($cm1), $activeruledescriptions);
1699          $this->assertEquals(mod_data_get_completion_active_rule_descriptions($cm2), []);
1700          $this->assertEquals(mod_data_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions);
1701          $this->assertEquals(mod_data_get_completion_active_rule_descriptions(new stdClass()), []);
1702      }
1703  
1704      /**
1705       * An unknown event type should not change the data instance.
1706       */
1707      public function test_mod_data_core_calendar_event_timestart_updated_unknown_event() {
1708          global $CFG, $DB;
1709          require_once($CFG->dirroot . "/calendar/lib.php");
1710  
1711          $this->resetAfterTest(true);
1712          $this->setAdminUser();
1713          $generator = $this->getDataGenerator();
1714          $course = $generator->create_course();
1715          $datagenerator = $generator->get_plugin_generator('mod_data');
1716          $timeopen = time();
1717          $timeclose = $timeopen + DAYSECS;
1718          $data = $datagenerator->create_instance(['course' => $course->id]);
1719          $data->timeavailablefrom = $timeopen;
1720          $data->timeavailableto = $timeclose;
1721          $DB->update_record('data', $data);
1722  
1723          // Create a valid event.
1724          $event = new \calendar_event([
1725              'name' => 'Test event',
1726              'description' => '',
1727              'format' => 1,
1728              'courseid' => $course->id,
1729              'groupid' => 0,
1730              'userid' => 2,
1731              'modulename' => 'data',
1732              'instance' => $data->id,
1733              'eventtype' => DATA_EVENT_TYPE_OPEN . "SOMETHING ELSE",
1734              'timestart' => 1,
1735              'timeduration' => 86400,
1736              'visible' => 1
1737          ]);
1738  
1739          mod_data_core_calendar_event_timestart_updated($event, $data);
1740          $data = $DB->get_record('data', ['id' => $data->id]);
1741          $this->assertEquals($timeopen, $data->timeavailablefrom);
1742          $this->assertEquals($timeclose, $data->timeavailableto);
1743      }
1744  
1745      /**
1746       * A DATA_EVENT_TYPE_OPEN event should update the timeavailablefrom property of the data activity.
1747       */
1748      public function test_mod_data_core_calendar_event_timestart_updated_open_event() {
1749          global $CFG, $DB;
1750          require_once($CFG->dirroot . "/calendar/lib.php");
1751  
1752          $this->resetAfterTest(true);
1753          $this->setAdminUser();
1754          $generator = $this->getDataGenerator();
1755          $course = $generator->create_course();
1756          $datagenerator = $generator->get_plugin_generator('mod_data');
1757          $timeopen = time();
1758          $timeclose = $timeopen + DAYSECS;
1759          $timemodified = 1;
1760          $newtimeopen = $timeopen - DAYSECS;
1761          $data = $datagenerator->create_instance(['course' => $course->id]);
1762          $data->timeavailablefrom = $timeopen;
1763          $data->timeavailableto = $timeclose;
1764          $data->timemodified = $timemodified;
1765          $DB->update_record('data', $data);
1766  
1767          // Create a valid event.
1768          $event = new \calendar_event([
1769              'name' => 'Test event',
1770              'description' => '',
1771              'format' => 1,
1772              'courseid' => $course->id,
1773              'groupid' => 0,
1774              'userid' => 2,
1775              'modulename' => 'data',
1776              'instance' => $data->id,
1777              'eventtype' => DATA_EVENT_TYPE_OPEN,
1778              'timestart' => $newtimeopen,
1779              'timeduration' => 86400,
1780              'visible' => 1
1781          ]);
1782  
1783          // Trigger and capture the event when adding a contact.
1784          $sink = $this->redirectEvents();
1785          mod_data_core_calendar_event_timestart_updated($event, $data);
1786          $triggeredevents = $sink->get_events();
1787          $moduleupdatedevents = array_filter($triggeredevents, function($e) {
1788              return is_a($e, 'core\event\course_module_updated');
1789          });
1790          $data = $DB->get_record('data', ['id' => $data->id]);
1791  
1792          // Ensure the timeavailablefrom property matches the event timestart.
1793          $this->assertEquals($newtimeopen, $data->timeavailablefrom);
1794          // Ensure the timeavailableto isn't changed.
1795          $this->assertEquals($timeclose, $data->timeavailableto);
1796          // Ensure the timemodified property has been changed.
1797          $this->assertNotEquals($timemodified, $data->timemodified);
1798          // Confirm that a module updated event is fired when the module is changed.
1799          $this->assertNotEmpty($moduleupdatedevents);
1800      }
1801  
1802      /**
1803       * A DATA_EVENT_TYPE_CLOSE event should update the timeavailableto property of the data activity.
1804       */
1805      public function test_mod_data_core_calendar_event_timestart_updated_close_event() {
1806          global $CFG, $DB;
1807          require_once($CFG->dirroot . "/calendar/lib.php");
1808  
1809          $this->resetAfterTest(true);
1810          $this->setAdminUser();
1811          $generator = $this->getDataGenerator();
1812          $course = $generator->create_course();
1813          $datagenerator = $generator->get_plugin_generator('mod_data');
1814          $timeopen = time();
1815          $timeclose = $timeopen + DAYSECS;
1816          $timemodified = 1;
1817          $newtimeclose = $timeclose + DAYSECS;
1818          $data = $datagenerator->create_instance(['course' => $course->id]);
1819          $data->timeavailablefrom = $timeopen;
1820          $data->timeavailableto = $timeclose;
1821          $data->timemodified = $timemodified;
1822          $DB->update_record('data', $data);
1823  
1824          // Create a valid event.
1825          $event = new \calendar_event([
1826              'name' => 'Test event',
1827              'description' => '',
1828              'format' => 1,
1829              'courseid' => $course->id,
1830              'groupid' => 0,
1831              'userid' => 2,
1832              'modulename' => 'data',
1833              'instance' => $data->id,
1834              'eventtype' => DATA_EVENT_TYPE_CLOSE,
1835              'timestart' => $newtimeclose,
1836              'timeduration' => 86400,
1837              'visible' => 1
1838          ]);
1839  
1840          // Trigger and capture the event when adding a contact.
1841          $sink = $this->redirectEvents();
1842          mod_data_core_calendar_event_timestart_updated($event, $data);
1843          $triggeredevents = $sink->get_events();
1844          $moduleupdatedevents = array_filter($triggeredevents, function($e) {
1845              return is_a($e, 'core\event\course_module_updated');
1846          });
1847          $data = $DB->get_record('data', ['id' => $data->id]);
1848  
1849          // Ensure the timeavailableto property matches the event timestart.
1850          $this->assertEquals($newtimeclose, $data->timeavailableto);
1851          // Ensure the timeavailablefrom isn't changed.
1852          $this->assertEquals($timeopen, $data->timeavailablefrom);
1853          // Ensure the timemodified property has been changed.
1854          $this->assertNotEquals($timemodified, $data->timemodified);
1855          // Confirm that a module updated event is fired when the module is changed.
1856          $this->assertNotEmpty($moduleupdatedevents);
1857      }
1858  
1859      /**
1860       * An unknown event type should not have any limits.
1861       */
1862      public function test_mod_data_core_calendar_get_valid_event_timestart_range_unknown_event() {
1863          global $CFG;
1864          require_once($CFG->dirroot . "/calendar/lib.php");
1865  
1866          $this->resetAfterTest(true);
1867          $this->setAdminUser();
1868          $generator = $this->getDataGenerator();
1869          $course = $generator->create_course();
1870          $timeopen = time();
1871          $timeclose = $timeopen + DAYSECS;
1872          $data = new \stdClass();
1873          $data->timeavailablefrom = $timeopen;
1874          $data->timeavailableto = $timeclose;
1875  
1876          // Create a valid event.
1877          $event = new \calendar_event([
1878              'name' => 'Test event',
1879              'description' => '',
1880              'format' => 1,
1881              'courseid' => $course->id,
1882              'groupid' => 0,
1883              'userid' => 2,
1884              'modulename' => 'data',
1885              'instance' => 1,
1886              'eventtype' => DATA_EVENT_TYPE_OPEN . "SOMETHING ELSE",
1887              'timestart' => 1,
1888              'timeduration' => 86400,
1889              'visible' => 1
1890          ]);
1891  
1892          list ($min, $max) = mod_data_core_calendar_get_valid_event_timestart_range($event, $data);
1893          $this->assertNull($min);
1894          $this->assertNull($max);
1895      }
1896  
1897      /**
1898       * The open event should be limited by the data's timeclose property, if it's set.
1899       */
1900      public function test_mod_data_core_calendar_get_valid_event_timestart_range_open_event() {
1901          global $CFG;
1902          require_once($CFG->dirroot . "/calendar/lib.php");
1903  
1904          $this->resetAfterTest(true);
1905          $this->setAdminUser();
1906          $generator = $this->getDataGenerator();
1907          $course = $generator->create_course();
1908          $timeopen = time();
1909          $timeclose = $timeopen + DAYSECS;
1910          $data = new \stdClass();
1911          $data->timeavailablefrom = $timeopen;
1912          $data->timeavailableto = $timeclose;
1913  
1914          // Create a valid event.
1915          $event = new \calendar_event([
1916              'name' => 'Test event',
1917              'description' => '',
1918              'format' => 1,
1919              'courseid' => $course->id,
1920              'groupid' => 0,
1921              'userid' => 2,
1922              'modulename' => 'data',
1923              'instance' => 1,
1924              'eventtype' => DATA_EVENT_TYPE_OPEN,
1925              'timestart' => 1,
1926              'timeduration' => 86400,
1927              'visible' => 1
1928          ]);
1929  
1930          // The max limit should be bounded by the timeclose value.
1931          list ($min, $max) = mod_data_core_calendar_get_valid_event_timestart_range($event, $data);
1932          $this->assertNull($min);
1933          $this->assertEquals($timeclose, $max[0]);
1934  
1935          // No timeclose value should result in no upper limit.
1936          $data->timeavailableto = 0;
1937          list ($min, $max) = mod_data_core_calendar_get_valid_event_timestart_range($event, $data);
1938          $this->assertNull($min);
1939          $this->assertNull($max);
1940      }
1941  
1942      /**
1943       * The close event should be limited by the data's timeavailablefrom property, if it's set.
1944       */
1945      public function test_mod_data_core_calendar_get_valid_event_timestart_range_close_event() {
1946          global $CFG;
1947  
1948          require_once($CFG->dirroot . "/calendar/lib.php");
1949  
1950          $this->resetAfterTest(true);
1951          $this->setAdminUser();
1952          $generator = $this->getDataGenerator();
1953          $course = $generator->create_course();
1954          $timeopen = time();
1955          $timeclose = $timeopen + DAYSECS;
1956          $data = new \stdClass();
1957          $data->timeavailablefrom = $timeopen;
1958          $data->timeavailableto = $timeclose;
1959  
1960          // Create a valid event.
1961          $event = new \calendar_event([
1962              'name' => 'Test event',
1963              'description' => '',
1964              'format' => 1,
1965              'courseid' => $course->id,
1966              'groupid' => 0,
1967              'userid' => 2,
1968              'modulename' => 'data',
1969              'instance' => 1,
1970              'eventtype' => DATA_EVENT_TYPE_CLOSE,
1971              'timestart' => 1,
1972              'timeduration' => 86400,
1973              'visible' => 1
1974          ]);
1975  
1976          // The max limit should be bounded by the timeclose value.
1977          list ($min, $max) = mod_data_core_calendar_get_valid_event_timestart_range($event, $data);
1978          $this->assertEquals($timeopen, $min[0]);
1979          $this->assertNull($max);
1980  
1981          // No timeavailableto value should result in no upper limit.
1982          $data->timeavailablefrom = 0;
1983          list ($min, $max) = mod_data_core_calendar_get_valid_event_timestart_range($event, $data);
1984          $this->assertNull($min);
1985          $this->assertNull($max);
1986      }
1987  
1988      /**
1989       * A user who does not have capabilities to add events to the calendar should be able to create an database.
1990       */
1991      public function test_creation_with_no_calendar_capabilities() {
1992          $this->resetAfterTest();
1993          $course = self::getDataGenerator()->create_course();
1994          $context = context_course::instance($course->id);
1995          $user = self::getDataGenerator()->create_and_enrol($course, 'editingteacher');
1996          $roleid = self::getDataGenerator()->create_role();
1997          self::getDataGenerator()->role_assign($roleid, $user->id, $context->id);
1998          assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true);
1999          $generator = self::getDataGenerator()->get_plugin_generator('mod_data');
2000          // Create an instance as a user without the calendar capabilities.
2001          $this->setUser($user);
2002          $time = time();
2003          $params = array(
2004              'course' => $course->id,
2005              'timeavailablefrom' => $time + 200,
2006              'timeavailableto' => $time + 2000,
2007              'timeviewfrom' => $time + 400,
2008              'timeviewto' => $time + 2000,
2009          );
2010          $generator->create_instance($params);
2011      }
2012  }