Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

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