Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Database module external functions tests
  19   *
  20   * @package    mod_data
  21   * @category   external
  22   * @copyright  2015 Juan Leyva <juan@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   * @since      Moodle 2.9
  25   */
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  global $CFG;
  30  
  31  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
  32  
  33  /**
  34   * Database module external functions tests
  35   *
  36   * @package    mod_data
  37   * @category   external
  38   * @copyright  2015 Juan Leyva <juan@moodle.com>
  39   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  40   * @since      Moodle 2.9
  41   */
  42  class mod_data_external_testcase extends externallib_advanced_testcase {
  43  
  44      /** @var stdClass Test module context. */
  45      protected $context;
  46  
  47      /** @var stdClass Test course.*/
  48      protected $course;
  49  
  50      /** @var stdClass Test course module. */
  51      protected $cm;
  52  
  53      /** @var  stdClass Test database activity. */
  54      protected $database;
  55  
  56      /** @var stdClass Test group 1. */
  57      protected $group1;
  58  
  59      /** @var stdClass Test group 2. */
  60      protected $group2;
  61  
  62      /** @var stdClass Test student 1. */
  63      protected $student1;
  64  
  65      /** @var stdClass Test student 2. */
  66      protected $student2;
  67  
  68      /** @var stdClass Test student 3. */
  69      protected $student3;
  70  
  71      /** @var stdClass Test student 4. */
  72      protected $student4;
  73  
  74      /** @var stdClass Student role. */
  75      protected $studentrole;
  76  
  77      /** @var stdClass Test teacher. */
  78      protected $teacher;
  79  
  80      /** @var stdClass Teacher role. */
  81      protected $teacherrole;
  82  
  83      /**
  84       * Set up for every test
  85       */
  86      public function setUp(): void {
  87          global $DB;
  88          $this->resetAfterTest();
  89          $this->setAdminUser();
  90  
  91          // Setup test data.
  92          $course = new stdClass();
  93          $course->groupmode = SEPARATEGROUPS;
  94          $course->groupmodeforce = true;
  95          $this->course = $this->getDataGenerator()->create_course($course);
  96          $this->database = $this->getDataGenerator()->create_module('data', array('course' => $this->course->id));
  97          $this->context = context_module::instance($this->database->cmid);
  98          $this->cm = get_coursemodule_from_instance('data', $this->database->id);
  99  
 100          // Create users.
 101          $this->student1 = self::getDataGenerator()->create_user(['firstname' => 'Olivia', 'lastname' => 'Smith']);
 102          $this->student2 = self::getDataGenerator()->create_user(['firstname' => 'Ezra', 'lastname' => 'Johnson']);
 103          $this->student3 = self::getDataGenerator()->create_user(['firstname' => 'Amelia', 'lastname' => 'Williams']);
 104          $this->teacher = self::getDataGenerator()->create_user(['firstname' => 'Asher', 'lastname' => 'Jones']);
 105  
 106          // Users enrolments.
 107          $this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
 108          $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
 109          $this->getDataGenerator()->enrol_user($this->student1->id, $this->course->id, $this->studentrole->id, 'manual');
 110          $this->getDataGenerator()->enrol_user($this->student2->id, $this->course->id, $this->studentrole->id, 'manual');
 111          $this->getDataGenerator()->enrol_user($this->student3->id, $this->course->id, $this->studentrole->id, 'manual');
 112          $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual');
 113  
 114          $this->group1 = $this->getDataGenerator()->create_group(array('courseid' => $this->course->id));
 115          $this->group2 = $this->getDataGenerator()->create_group(array('courseid' => $this->course->id));
 116          groups_add_member($this->group1, $this->student1);
 117          groups_add_member($this->group1, $this->student2);
 118          groups_add_member($this->group2, $this->student3);
 119      }
 120  
 121      /**
 122       * Test get databases by courses
 123       */
 124      public function test_mod_data_get_databases_by_courses() {
 125          global $DB, $CFG;
 126          require_once($CFG->libdir . '/externallib.php');
 127  
 128          $this->resetAfterTest(true);
 129  
 130          // Create users.
 131          $student = self::getDataGenerator()->create_user();
 132          $teacher = self::getDataGenerator()->create_user();
 133  
 134          // Set to the student user.
 135          self::setUser($student);
 136  
 137          // Create courses to add the modules.
 138          $course1 = self::getDataGenerator()->create_course();
 139          $course2 = self::getDataGenerator()->create_course();
 140  
 141          // First database.
 142          $record = new stdClass();
 143          $record->introformat = FORMAT_HTML;
 144          $record->course = $course1->id;
 145          // Set multilang text to check that is properly filtered to "en" only.
 146          $record->name = '<span lang="en" class="multilang">English</span><span lang="es" class="multilang">EspaƱol</span>';
 147          $record->intro = '<button>Test with HTML allowed.</button>';
 148          $database1 = self::getDataGenerator()->create_module('data', $record);
 149  
 150          // Second database.
 151          $record = new stdClass();
 152          $record->introformat = FORMAT_HTML;
 153          $record->course = $course2->id;
 154          $database2 = self::getDataGenerator()->create_module('data', $record);
 155  
 156          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 157          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
 158  
 159          // Users enrolments.
 160          $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
 161          $this->getDataGenerator()->enrol_user($teacher->id, $course1->id, $teacherrole->id, 'manual');
 162  
 163          // Execute real Moodle enrolment as we'll call unenrol() method on the instance later.
 164          $enrol = enrol_get_plugin('manual');
 165          $enrolinstances = enrol_get_instances($course2->id, true);
 166          foreach ($enrolinstances as $courseenrolinstance) {
 167              if ($courseenrolinstance->enrol == "manual") {
 168                  $instance2 = $courseenrolinstance;
 169                  break;
 170              }
 171          }
 172          $enrol->enrol_user($instance2, $student->id, $studentrole->id);
 173  
 174          // Enable multilang filter to on content and heading.
 175          filter_manager::reset_caches();
 176          filter_set_global_state('multilang', TEXTFILTER_ON);
 177          filter_set_applies_to_strings('multilang', true);
 178          // Set WS filtering.
 179          $wssettings = external_settings::get_instance();
 180          $wssettings->set_filter(true);
 181  
 182          // Create what we expect to be returned when querying the two courses.
 183          // First for the student user.
 184          $expectedfields = array('id', 'coursemodule', 'course', 'name', 'comments', 'timeavailablefrom',
 185                              'timeavailableto', 'timeviewfrom', 'timeviewto', 'requiredentries', 'requiredentriestoview',
 186                              'intro', 'introformat', 'introfiles', 'maxentries', 'rssarticles', 'singletemplate', 'listtemplate',
 187                              'listtemplateheader', 'listtemplatefooter', 'addtemplate', 'rsstemplate', 'rsstitletemplate',
 188                              'csstemplate', 'jstemplate', 'asearchtemplate', 'approval', 'defaultsort', 'defaultsortdir', 'manageapproved');
 189  
 190          // Add expected coursemodule.
 191          $database1->coursemodule = $database1->cmid;
 192          $database1->introfiles = [];
 193          $database2->coursemodule = $database2->cmid;
 194          $database2->introfiles = [];
 195  
 196          $expected1 = array();
 197          $expected2 = array();
 198          foreach ($expectedfields as $field) {
 199              if ($field == 'approval' or $field == 'manageapproved') {
 200                  $database1->{$field} = (bool) $database1->{$field};
 201                  $database2->{$field} = (bool) $database2->{$field};
 202              }
 203              $expected1[$field] = $database1->{$field};
 204              $expected2[$field] = $database2->{$field};
 205          }
 206          $expected1['name'] = 'English'; // Lang filtered expected.
 207          $expected1['comments'] = (bool) $expected1['comments'];
 208          $expected2['comments'] = (bool) $expected2['comments'];
 209  
 210          $expecteddatabases = array();
 211          $expecteddatabases[] = $expected2;
 212          $expecteddatabases[] = $expected1;
 213  
 214          // Call the external function passing course ids.
 215          $result = mod_data_external::get_databases_by_courses(array($course2->id, $course1->id));
 216          $result = external_api::clean_returnvalue(mod_data_external::get_databases_by_courses_returns(), $result);
 217          $this->assertEquals($expecteddatabases, $result['databases']);
 218  
 219          // Call the external function without passing course id.
 220          $result = mod_data_external::get_databases_by_courses();
 221          $result = external_api::clean_returnvalue(mod_data_external::get_databases_by_courses_returns(), $result);
 222          $this->assertEquals($expecteddatabases, $result['databases']);
 223  
 224          // Unenrol user from second course and alter expected databases.
 225          $enrol->unenrol_user($instance2, $student->id);
 226          array_shift($expecteddatabases);
 227  
 228          // Call the external function without passing course id.
 229          $result = mod_data_external::get_databases_by_courses();
 230          $result = external_api::clean_returnvalue(mod_data_external::get_databases_by_courses_returns(), $result);
 231          $this->assertEquals($expecteddatabases, $result['databases']);
 232  
 233          // Call for the second course we unenrolled the user from, expected warning.
 234          $result = mod_data_external::get_databases_by_courses(array($course2->id));
 235          $this->assertCount(1, $result['warnings']);
 236          $this->assertEquals('1', $result['warnings'][0]['warningcode']);
 237          $this->assertEquals($course2->id, $result['warnings'][0]['itemid']);
 238  
 239          // Now, try as a teacher for getting all the additional fields.
 240          self::setUser($teacher);
 241  
 242          $additionalfields = array('scale', 'assessed', 'assesstimestart', 'assesstimefinish', 'editany', 'notification', 'timemodified');
 243  
 244          foreach ($additionalfields as $field) {
 245              if ($field == 'editany') {
 246                  $database1->{$field} = (bool) $database1->{$field};
 247              }
 248              $expecteddatabases[0][$field] = $database1->{$field};
 249          }
 250          $result = mod_data_external::get_databases_by_courses();
 251          $result = external_api::clean_returnvalue(mod_data_external::get_databases_by_courses_returns(), $result);
 252          $this->assertEquals($expecteddatabases, $result['databases']);
 253  
 254          // Admin should get all the information.
 255          self::setAdminUser();
 256  
 257          $result = mod_data_external::get_databases_by_courses(array($course1->id));
 258          $result = external_api::clean_returnvalue(mod_data_external::get_databases_by_courses_returns(), $result);
 259          $this->assertEquals($expecteddatabases, $result['databases']);
 260      }
 261  
 262      /**
 263       * Test view_database invalid id.
 264       */
 265      public function test_view_database_invalid_id() {
 266  
 267          // Test invalid instance id.
 268          $this->expectException('moodle_exception');
 269          mod_data_external::view_database(0);
 270      }
 271  
 272      /**
 273       * Test view_database not enrolled user.
 274       */
 275      public function test_view_database_not_enrolled_user() {
 276  
 277          $usernotenrolled = self::getDataGenerator()->create_user();
 278          $this->setUser($usernotenrolled);
 279  
 280          $this->expectException('moodle_exception');
 281          mod_data_external::view_database(0);
 282      }
 283  
 284      /**
 285       * Test view_database no capabilities.
 286       */
 287      public function test_view_database_no_capabilities() {
 288          // Test user with no capabilities.
 289          // We need a explicit prohibit since this capability is allowed for students by default.
 290          assign_capability('mod/data:view', CAP_PROHIBIT, $this->studentrole->id, $this->context->id);
 291          accesslib_clear_all_caches_for_unit_testing();
 292  
 293          $this->expectException('moodle_exception');
 294          mod_data_external::view_database(0);
 295      }
 296  
 297      /**
 298       * Test view_database.
 299       */
 300      public function test_view_database() {
 301  
 302          // Test user with full capabilities.
 303          $this->setUser($this->student1);
 304  
 305          // Trigger and capture the event.
 306          $sink = $this->redirectEvents();
 307  
 308          $result = mod_data_external::view_database($this->database->id);
 309          $result = external_api::clean_returnvalue(mod_data_external::view_database_returns(), $result);
 310  
 311          $events = $sink->get_events();
 312          $this->assertCount(1, $events);
 313          $event = array_shift($events);
 314  
 315          // Checking that the event contains the expected values.
 316          $this->assertInstanceOf('\mod_data\event\course_module_viewed', $event);
 317          $this->assertEquals($this->context, $event->get_context());
 318          $moodledata = new \moodle_url('/mod/data/view.php', array('id' => $this->cm->id));
 319          $this->assertEquals($moodledata, $event->get_url());
 320          $this->assertEventContextNotUsed($event);
 321          $this->assertNotEmpty($event->get_name());
 322      }
 323  
 324      /**
 325       * Test get_data_access_information for student.
 326       */
 327      public function test_get_data_access_information_student() {
 328          global $DB;
 329          // Modify the database to add access restrictions.
 330          $this->database->timeavailablefrom = time() + DAYSECS;
 331          $this->database->requiredentries = 2;
 332          $this->database->requiredentriestoview = 2;
 333          $DB->update_record('data', $this->database);
 334  
 335          // Test user with full capabilities.
 336          $this->setUser($this->student1);
 337  
 338          $result = mod_data_external::get_data_access_information($this->database->id);
 339          $result = external_api::clean_returnvalue(mod_data_external::get_data_access_information_returns(), $result);
 340  
 341          $this->assertEquals($this->group1->id, $result['groupid']);
 342  
 343          $this->assertFalse($result['canmanageentries']);
 344          $this->assertFalse($result['canapprove']);
 345          $this->assertTrue($result['canaddentry']);  // It return true because it doen't check time restrictions.
 346          $this->assertFalse($result['timeavailable']);
 347          $this->assertFalse($result['inreadonlyperiod']);
 348          $this->assertEquals(0, $result['numentries']);
 349          $this->assertEquals($this->database->requiredentries, $result['entrieslefttoadd']);
 350          $this->assertEquals($this->database->requiredentriestoview, $result['entrieslefttoview']);
 351      }
 352  
 353      /**
 354       * Test get_data_access_information for teacher.
 355       */
 356      public function test_get_data_access_information_teacher() {
 357          global $DB;
 358          // Modify the database to add access restrictions.
 359          $this->database->timeavailablefrom = time() + DAYSECS;
 360          $this->database->requiredentries = 2;
 361          $this->database->requiredentriestoview = 2;
 362          $DB->update_record('data', $this->database);
 363  
 364          // Test user with full capabilities.
 365          $this->setUser($this->teacher);
 366  
 367          $result = mod_data_external::get_data_access_information($this->database->id);
 368          $result = external_api::clean_returnvalue(mod_data_external::get_data_access_information_returns(), $result);
 369  
 370          $this->assertEquals(0, $result['groupid']);
 371  
 372          $this->assertTrue($result['canmanageentries']);
 373          $this->assertTrue($result['canapprove']);
 374          $this->assertTrue($result['canaddentry']);  // It return true because it doen't check time restrictions.
 375          $this->assertTrue($result['timeavailable']);
 376          $this->assertFalse($result['inreadonlyperiod']);
 377          $this->assertEquals(0, $result['numentries']);
 378          $this->assertEquals(0, $result['entrieslefttoadd']);
 379          $this->assertEquals(0, $result['entrieslefttoview']);
 380      }
 381  
 382      /**
 383       * Test get_data_access_information with groups.
 384       */
 385      public function test_get_data_access_information_groups() {
 386          global $DB;
 387  
 388          $DB->set_field('course', 'groupmode', VISIBLEGROUPS, ['id' => $this->course->id]);
 389  
 390          // Check I can see my group.
 391          $this->setUser($this->student1);
 392  
 393          $result = mod_data_external::get_data_access_information($this->database->id);
 394          $result = external_api::clean_returnvalue(mod_data_external::get_data_access_information_returns(), $result);
 395  
 396          $this->assertEquals($this->group1->id, $result['groupid']); // My group is correctly found.
 397          $this->assertFalse($result['canmanageentries']);
 398          $this->assertFalse($result['canapprove']);
 399          $this->assertTrue($result['canaddentry']);  // I can entries in my groups.
 400          $this->assertTrue($result['timeavailable']);
 401          $this->assertFalse($result['inreadonlyperiod']);
 402          $this->assertEquals(0, $result['numentries']);
 403          $this->assertEquals(0, $result['entrieslefttoadd']);
 404          $this->assertEquals(0, $result['entrieslefttoview']);
 405  
 406          // Check the other course group in visible groups mode.
 407          $result = mod_data_external::get_data_access_information($this->database->id, $this->group2->id);
 408          $result = external_api::clean_returnvalue(mod_data_external::get_data_access_information_returns(), $result);
 409  
 410          $this->assertEquals($this->group2->id, $result['groupid']); // The group is correctly found.
 411          $this->assertFalse($result['canmanageentries']);
 412          $this->assertFalse($result['canapprove']);
 413          $this->assertFalse($result['canaddentry']);  // I cannot add entries in other groups.
 414          $this->assertTrue($result['timeavailable']);
 415          $this->assertFalse($result['inreadonlyperiod']);
 416          $this->assertEquals(0, $result['numentries']);
 417          $this->assertEquals(0, $result['entrieslefttoadd']);
 418          $this->assertEquals(0, $result['entrieslefttoview']);
 419      }
 420  
 421      /**
 422       * Helper method to populate the database with some entries.
 423       *
 424       * @return array the entry ids created
 425       */
 426      public function populate_database_with_entries() {
 427          global $DB;
 428  
 429          // Force approval.
 430          $DB->set_field('data', 'approval', 1, array('id' => $this->database->id));
 431          $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
 432          $fieldtypes = array('checkbox', 'date', 'menu', 'multimenu', 'number', 'radiobutton', 'text', 'textarea', 'url');
 433  
 434          $count = 1;
 435          // Creating test Fields with default parameter values.
 436          foreach ($fieldtypes as $fieldtype) {
 437              $fieldname = 'field-' . $count;
 438              $record = new StdClass();
 439              $record->name = $fieldname;
 440              $record->type = $fieldtype;
 441              $record->required = 1;
 442  
 443              $generator->create_field($record, $this->database);
 444              $count++;
 445          }
 446          // Get all the fields created.
 447          $fields = $DB->get_records('data_fields', array('dataid' => $this->database->id), 'id');
 448  
 449          // Populate with contents, creating a new entry.
 450          $contents = array();
 451          $contents[] = array('opt1', 'opt2', 'opt3', 'opt4');
 452          $contents[] = '01-01-2037'; // It should be lower than 2038, to avoid failing on 32-bit windows.
 453          $contents[] = 'menu1';
 454          $contents[] = array('multimenu1', 'multimenu2', 'multimenu3', 'multimenu4');
 455          $contents[] = '12345';
 456          $contents[] = 'radioopt1';
 457          $contents[] = 'text for testing';
 458          $contents[] = '<p>text area testing<br /></p>';
 459          $contents[] = array('example.url', 'sampleurl');
 460          $count = 0;
 461          $fieldcontents = array();
 462          foreach ($fields as $fieldrecord) {
 463              $fieldcontents[$fieldrecord->id] = $contents[$count++];
 464          }
 465  
 466          $this->setUser($this->student1);
 467          $entry11 = $generator->create_entry($this->database, $fieldcontents, $this->group1->id, ['Cats', 'Dogs']);
 468          $this->setUser($this->student2);
 469          $entry12 = $generator->create_entry($this->database, $fieldcontents, $this->group1->id, ['Cats']);
 470          $entry13 = $generator->create_entry($this->database, $fieldcontents, $this->group1->id);
 471          // Entry not in group.
 472          $entry14 = $generator->create_entry($this->database, $fieldcontents, 0);
 473  
 474          $this->setUser($this->student3);
 475          $entry21 = $generator->create_entry($this->database, $fieldcontents, $this->group2->id);
 476  
 477          // Approve all except $entry13.
 478          $DB->set_field('data_records', 'approved', 1, ['id' => $entry11]);
 479          $DB->set_field('data_records', 'approved', 1, ['id' => $entry12]);
 480          $DB->set_field('data_records', 'approved', 1, ['id' => $entry14]);
 481          $DB->set_field('data_records', 'approved', 1, ['id' => $entry21]);
 482  
 483          return [$entry11, $entry12, $entry13, $entry14, $entry21];
 484      }
 485  
 486      /**
 487       * Test get_entries
 488       */
 489      public function test_get_entries() {
 490          global $DB;
 491          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
 492  
 493          // First of all, expect to see only my group entries (not other users in other groups ones).
 494          // We may expect entries without group also.
 495          $this->setUser($this->student1);
 496          $result = mod_data_external::get_entries($this->database->id);
 497          $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
 498          $this->assertCount(0, $result['warnings']);
 499          $this->assertCount(3, $result['entries']);
 500          $this->assertEquals(3, $result['totalcount']);
 501          $this->assertEquals($entry11, $result['entries'][0]['id']);
 502          $this->assertCount(2, $result['entries'][0]['tags']);
 503          $this->assertEquals($this->student1->id, $result['entries'][0]['userid']);
 504          $this->assertEquals($this->group1->id, $result['entries'][0]['groupid']);
 505          $this->assertEquals($this->database->id, $result['entries'][0]['dataid']);
 506          $this->assertEquals($entry12, $result['entries'][1]['id']);
 507          $this->assertCount(1, $result['entries'][1]['tags']);
 508          $this->assertEquals('Cats', $result['entries'][1]['tags'][0]['rawname']);
 509          $this->assertEquals($this->student2->id, $result['entries'][1]['userid']);
 510          $this->assertEquals($this->group1->id, $result['entries'][1]['groupid']);
 511          $this->assertEquals($this->database->id, $result['entries'][1]['dataid']);
 512          $this->assertEquals($entry14, $result['entries'][2]['id']);
 513          $this->assertEquals($this->student2->id, $result['entries'][2]['userid']);
 514          $this->assertEquals(0, $result['entries'][2]['groupid']);
 515          $this->assertEquals($this->database->id, $result['entries'][2]['dataid']);
 516          // Other user in same group.
 517          $this->setUser($this->student2);
 518          $result = mod_data_external::get_entries($this->database->id);
 519          $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
 520          $this->assertCount(0, $result['warnings']);
 521          $this->assertCount(4, $result['entries']);  // I can see my entry not approved yet.
 522          $this->assertEquals(4, $result['totalcount']);
 523  
 524          // Now try with the user in the second group that must see only two entries (his group entry and the one without group).
 525          $this->setUser($this->student3);
 526          $result = mod_data_external::get_entries($this->database->id);
 527          $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
 528          $this->assertCount(0, $result['warnings']);
 529          $this->assertCount(2, $result['entries']);
 530          $this->assertEquals(2, $result['totalcount']);
 531          $this->assertEquals($entry14, $result['entries'][0]['id']);
 532          $this->assertEquals($this->student2->id, $result['entries'][0]['userid']);
 533          $this->assertEquals(0, $result['entries'][0]['groupid']);
 534          $this->assertEquals($this->database->id, $result['entries'][0]['dataid']);
 535          $this->assertEquals($entry21, $result['entries'][1]['id']);
 536          $this->assertEquals($this->student3->id, $result['entries'][1]['userid']);
 537          $this->assertEquals($this->group2->id, $result['entries'][1]['groupid']);
 538          $this->assertEquals($this->database->id, $result['entries'][1]['dataid']);
 539  
 540          // Now, as teacher we should see all (we have permissions to view all groups).
 541          $this->setUser($this->teacher);
 542          $result = mod_data_external::get_entries($this->database->id);
 543          $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
 544          $this->assertCount(0, $result['warnings']);
 545          $this->assertCount(5, $result['entries']);  // I can see the not approved one.
 546          $this->assertEquals(5, $result['totalcount']);
 547  
 548          $entries = $DB->get_records('data_records', array('dataid' => $this->database->id), 'id');
 549          $this->assertCount(5, $entries);
 550          $count = 0;
 551          foreach ($entries as $entry) {
 552              $this->assertEquals($entry->id, $result['entries'][$count]['id']);
 553              $count++;
 554          }
 555  
 556          // Basic test passing the parameter (instead having to calculate it).
 557          $this->setUser($this->student1);
 558          $result = mod_data_external::get_entries($this->database->id, $this->group1->id);
 559          $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
 560          $this->assertCount(0, $result['warnings']);
 561          $this->assertCount(3, $result['entries']);
 562          $this->assertEquals(3, $result['totalcount']);
 563  
 564          // Test ordering (reverse).
 565          $this->setUser($this->student1);
 566          $result = mod_data_external::get_entries($this->database->id, $this->group1->id, false, null, 'DESC');
 567          $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
 568          $this->assertCount(0, $result['warnings']);
 569          $this->assertCount(3, $result['entries']);
 570          $this->assertEquals(3, $result['totalcount']);
 571          $this->assertEquals($entry14, $result['entries'][0]['id']);
 572  
 573          // Test pagination.
 574          $this->setUser($this->student1);
 575          $result = mod_data_external::get_entries($this->database->id, $this->group1->id, false, null, null, 0, 1);
 576          $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
 577          $this->assertCount(0, $result['warnings']);
 578          $this->assertCount(1, $result['entries']);
 579          $this->assertEquals(3, $result['totalcount']);
 580          $this->assertEquals($entry11, $result['entries'][0]['id']);
 581  
 582          $result = mod_data_external::get_entries($this->database->id, $this->group1->id, false, null, null, 1, 1);
 583          $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
 584          $this->assertCount(0, $result['warnings']);
 585          $this->assertCount(1, $result['entries']);
 586          $this->assertEquals(3, $result['totalcount']);
 587          $this->assertEquals($entry12, $result['entries'][0]['id']);
 588  
 589          // Now test the return contents.
 590          data_generate_default_template($this->database, 'listtemplate', 0, false, true); // Generate a default list template.
 591          $result = mod_data_external::get_entries($this->database->id, $this->group1->id, true, null, null, 0, 2);
 592          $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
 593          $this->assertCount(0, $result['warnings']);
 594          $this->assertCount(2, $result['entries']);
 595          $this->assertEquals(3, $result['totalcount']);
 596          $this->assertCount(9, $result['entries'][0]['contents']);
 597          $this->assertCount(9, $result['entries'][1]['contents']);
 598          // Search for some content.
 599          $this->assertTrue(strpos($result['listviewcontents'], 'opt1') !== false);
 600          $this->assertTrue(strpos($result['listviewcontents'], 'January') !== false);
 601          $this->assertTrue(strpos($result['listviewcontents'], 'menu1') !== false);
 602          $this->assertTrue(strpos($result['listviewcontents'], 'text for testing') !== false);
 603          $this->assertTrue(strpos($result['listviewcontents'], 'sampleurl') !== false);
 604      }
 605  
 606      /**
 607       * Test get_entry_visible_groups.
 608       */
 609      public function test_get_entry_visible_groups() {
 610          global $DB;
 611  
 612          $DB->set_field('course', 'groupmode', VISIBLEGROUPS, ['id' => $this->course->id]);
 613          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
 614  
 615          // Check I can see my approved group entries.
 616          $this->setUser($this->student1);
 617          $result = mod_data_external::get_entry($entry11);
 618          $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
 619          $this->assertCount(0, $result['warnings']);
 620          $this->assertEquals($entry11, $result['entry']['id']);
 621          $this->assertTrue($result['entry']['approved']);
 622          $this->assertTrue($result['entry']['canmanageentry']); // Is mine, I can manage it.
 623  
 624          // Entry from other group.
 625          $result = mod_data_external::get_entry($entry21);
 626          $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
 627          $this->assertCount(0, $result['warnings']);
 628          $this->assertEquals($entry21, $result['entry']['id']);
 629      }
 630  
 631      /**
 632       * Test get_entry_separated_groups.
 633       */
 634      public function test_get_entry_separated_groups() {
 635          global $DB;
 636          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
 637  
 638          // Check I can see my approved group entries.
 639          $this->setUser($this->student1);
 640          $result = mod_data_external::get_entry($entry11);
 641          $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
 642          $this->assertCount(0, $result['warnings']);
 643          $this->assertEquals($entry11, $result['entry']['id']);
 644          $this->assertTrue($result['entry']['approved']);
 645          $this->assertTrue($result['entry']['canmanageentry']); // Is mine, I can manage it.
 646  
 647          // Retrieve contents.
 648          data_generate_default_template($this->database, 'singletemplate', 0, false, true);
 649          $result = mod_data_external::get_entry($entry11, true);
 650          $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
 651          $this->assertCount(0, $result['warnings']);
 652          $this->assertCount(9, $result['entry']['contents']);
 653          $this->assertTrue(strpos($result['entryviewcontents'], 'opt1') !== false);
 654          $this->assertTrue(strpos($result['entryviewcontents'], 'January') !== false);
 655          $this->assertTrue(strpos($result['entryviewcontents'], 'menu1') !== false);
 656          $this->assertTrue(strpos($result['entryviewcontents'], 'text for testing') !== false);
 657          $this->assertTrue(strpos($result['entryviewcontents'], 'sampleurl') !== false);
 658  
 659          // This is in my group but I'm not the author.
 660          $result = mod_data_external::get_entry($entry12);
 661          $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
 662          $this->assertCount(0, $result['warnings']);
 663          $this->assertEquals($entry12, $result['entry']['id']);
 664          $this->assertTrue($result['entry']['approved']);
 665          $this->assertFalse($result['entry']['canmanageentry']); // Not mine.
 666  
 667          $this->setUser($this->student3);
 668          $result = mod_data_external::get_entry($entry21);
 669          $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
 670          $this->assertCount(0, $result['warnings']);
 671          $this->assertEquals($entry21, $result['entry']['id']);
 672          $this->assertTrue($result['entry']['approved']);
 673          $this->assertTrue($result['entry']['canmanageentry']); // Is mine, I can manage it.
 674  
 675          // As teacher I should be able to see all the entries.
 676          $this->setUser($this->teacher);
 677          $result = mod_data_external::get_entry($entry11);
 678          $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
 679          $this->assertEquals($entry11, $result['entry']['id']);
 680  
 681          $result = mod_data_external::get_entry($entry12);
 682          $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
 683          $this->assertEquals($entry12, $result['entry']['id']);
 684          // This is the not approved one.
 685          $result = mod_data_external::get_entry($entry13);
 686          $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
 687          $this->assertEquals($entry13, $result['entry']['id']);
 688  
 689          $result = mod_data_external::get_entry($entry21);
 690          $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
 691          $this->assertEquals($entry21, $result['entry']['id']);
 692  
 693          // Now, try to get an entry not approved yet.
 694          $this->setUser($this->student1);
 695          $this->expectException('moodle_exception');
 696          $result = mod_data_external::get_entry($entry13);
 697      }
 698  
 699      /**
 700       * Test get_entry from other group in separated groups.
 701       */
 702      public function test_get_entry_other_group_separated_groups() {
 703          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
 704  
 705          // We should not be able to view other gropu entries (in separated groups).
 706          $this->setUser($this->student1);
 707          $this->expectException('moodle_exception');
 708          $result = mod_data_external::get_entry($entry21);
 709      }
 710  
 711      /**
 712       * Test get_fields.
 713       */
 714      public function test_get_fields() {
 715          global $DB;
 716          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
 717  
 718          $this->setUser($this->student1);
 719          $result = mod_data_external::get_fields($this->database->id);
 720          $result = external_api::clean_returnvalue(mod_data_external::get_fields_returns(), $result);
 721  
 722          // Basically compare we retrieve all the fields and the correct values.
 723          $fields = $DB->get_records('data_fields', array('dataid' => $this->database->id), 'id');
 724          foreach ($result['fields'] as $field) {
 725              $this->assertEquals($field, (array) $fields[$field['id']]);
 726          }
 727      }
 728  
 729      /**
 730       * Test get_fields_database_without_fields.
 731       */
 732      public function test_get_fields_database_without_fields() {
 733  
 734          $this->setUser($this->student1);
 735          $result = mod_data_external::get_fields($this->database->id);
 736          $result = external_api::clean_returnvalue(mod_data_external::get_fields_returns(), $result);
 737  
 738          $this->assertEmpty($result['fields']);
 739      }
 740  
 741      /**
 742       * Test search_entries.
 743       */
 744      public function test_search_entries() {
 745          global $DB;
 746          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
 747  
 748          $this->setUser($this->student1);
 749          // Empty search, it should return all the visible entries.
 750          $result = mod_data_external::search_entries($this->database->id, 0, false);
 751          $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
 752          $this->assertCount(3, $result['entries']);
 753          $this->assertEquals(3, $result['totalcount']);
 754  
 755          // Search for something that does not exists.
 756          $result = mod_data_external::search_entries($this->database->id, 0, false, 'abc');
 757          $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
 758          $this->assertCount(0, $result['entries']);
 759          $this->assertEquals(0, $result['totalcount']);
 760  
 761          // Search by text matching all the entries.
 762          $result = mod_data_external::search_entries($this->database->id, 0, false, 'text');
 763          $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
 764          $this->assertCount(3, $result['entries']);
 765          $this->assertEquals(3, $result['totalcount']);
 766          $this->assertEquals(3, $result['maxcount']);
 767  
 768          // Now as the other student I should receive my not approved entry. Apply ordering here.
 769          $this->setUser($this->student2);
 770          $result = mod_data_external::search_entries($this->database->id, 0, false, 'text', [], DATA_APPROVED, 'ASC');
 771          $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
 772          $this->assertCount(4, $result['entries']);
 773          $this->assertEquals(4, $result['totalcount']);
 774          $this->assertEquals(4, $result['maxcount']);
 775          // The not approved one should be the first.
 776          $this->assertEquals($entry13, $result['entries'][0]['id']);
 777  
 778          // Now as the other group student.
 779          $this->setUser($this->student3);
 780          $result = mod_data_external::search_entries($this->database->id, 0, false, 'text');
 781          $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
 782          $this->assertCount(2, $result['entries']);
 783          $this->assertEquals(2, $result['totalcount']);
 784          $this->assertEquals(2, $result['maxcount']);
 785          $this->assertEquals($this->student2->id, $result['entries'][0]['userid']);
 786          $this->assertEquals($this->student3->id, $result['entries'][1]['userid']);
 787  
 788          // Same normal text search as teacher.
 789          $this->setUser($this->teacher);
 790          $result = mod_data_external::search_entries($this->database->id, 0, false, 'text');
 791          $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
 792          $this->assertCount(5, $result['entries']);  // I can see all groups and non approved.
 793          $this->assertEquals(5, $result['totalcount']);
 794          $this->assertEquals(5, $result['maxcount']);
 795  
 796          // Pagination.
 797          $this->setUser($this->teacher);
 798          $result = mod_data_external::search_entries($this->database->id, 0, false, 'text', [], DATA_TIMEADDED, 'ASC', 0, 2);
 799          $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
 800          $this->assertCount(2, $result['entries']);  // Only 2 per page.
 801          $this->assertEquals(5, $result['totalcount']);
 802          $this->assertEquals(5, $result['maxcount']);
 803  
 804          // Now advanced search or not dinamic fields (user firstname for example).
 805          $this->setUser($this->student1);
 806          $advsearch = [
 807              ['name' => 'fn', 'value' => json_encode($this->student2->firstname)]
 808          ];
 809          $result = mod_data_external::search_entries($this->database->id, 0, false, '', $advsearch);
 810          $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
 811          $this->assertCount(2, $result['entries']);
 812          $this->assertEquals(2, $result['totalcount']);
 813          $this->assertEquals(3, $result['maxcount']);
 814          $this->assertEquals($this->student2->id, $result['entries'][0]['userid']);  // I only found mine!
 815  
 816          // Advanced search for fields.
 817          $field = $DB->get_record('data_fields', array('type' => 'url'));
 818          $advsearch = [
 819              ['name' => 'f_' . $field->id , 'value' => 'sampleurl']
 820          ];
 821          $result = mod_data_external::search_entries($this->database->id, 0, false, '', $advsearch);
 822          $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
 823          $this->assertCount(3, $result['entries']);  // Found two entries matching this.
 824          $this->assertEquals(3, $result['totalcount']);
 825          $this->assertEquals(3, $result['maxcount']);
 826  
 827          // Combined search.
 828          $field2 = $DB->get_record('data_fields', array('type' => 'number'));
 829          $advsearch = [
 830              ['name' => 'f_' . $field->id , 'value' => 'sampleurl'],
 831              ['name' => 'f_' . $field2->id , 'value' => '12345'],
 832              ['name' => 'ln', 'value' => json_encode($this->student2->lastname)]
 833          ];
 834          $result = mod_data_external::search_entries($this->database->id, 0, false, '', $advsearch);
 835          $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
 836          $this->assertCount(2, $result['entries']);  // Only one matching everything.
 837          $this->assertEquals(2, $result['totalcount']);
 838          $this->assertEquals(3, $result['maxcount']);
 839  
 840          // Combined search (no results).
 841          $field2 = $DB->get_record('data_fields', array('type' => 'number'));
 842          $advsearch = [
 843              ['name' => 'f_' . $field->id , 'value' => 'sampleurl'],
 844              ['name' => 'f_' . $field2->id , 'value' => '98780333'], // Non existent number.
 845          ];
 846          $result = mod_data_external::search_entries($this->database->id, 0, false, '', $advsearch);
 847          $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
 848          $this->assertCount(0, $result['entries']);  // Only one matching everything.
 849          $this->assertEquals(0, $result['totalcount']);
 850          $this->assertEquals(3, $result['maxcount']);
 851      }
 852  
 853      /**
 854       * Test approve_entry.
 855       */
 856      public function test_approve_entry() {
 857          global $DB;
 858          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
 859  
 860          $this->setUser($this->teacher);
 861          $this->assertEquals(0, $DB->get_field('data_records', 'approved', array('id' => $entry13)));
 862          $result = mod_data_external::approve_entry($entry13);
 863          $result = external_api::clean_returnvalue(mod_data_external::approve_entry_returns(), $result);
 864          $this->assertEquals(1, $DB->get_field('data_records', 'approved', array('id' => $entry13)));
 865      }
 866  
 867      /**
 868       * Test unapprove_entry.
 869       */
 870      public function test_unapprove_entry() {
 871          global $DB;
 872          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
 873  
 874          $this->setUser($this->teacher);
 875          $this->assertEquals(1, $DB->get_field('data_records', 'approved', array('id' => $entry11)));
 876          $result = mod_data_external::approve_entry($entry11, false);
 877          $result = external_api::clean_returnvalue(mod_data_external::approve_entry_returns(), $result);
 878          $this->assertEquals(0, $DB->get_field('data_records', 'approved', array('id' => $entry11)));
 879      }
 880  
 881      /**
 882       * Test approve_entry missing permissions.
 883       */
 884      public function test_approve_entry_missing_permissions() {
 885          global $DB;
 886          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
 887  
 888          $this->setUser($this->student1);
 889          $this->expectException('moodle_exception');
 890          mod_data_external::approve_entry($entry13);
 891      }
 892  
 893      /**
 894       * Test delete_entry as teacher. Check I can delete any entry.
 895       */
 896      public function test_delete_entry_as_teacher() {
 897          global $DB;
 898          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
 899  
 900          $this->setUser($this->teacher);
 901          $result = mod_data_external::delete_entry($entry11);
 902          $result = external_api::clean_returnvalue(mod_data_external::delete_entry_returns(), $result);
 903          $this->assertEquals(0, $DB->count_records('data_records', array('id' => $entry11)));
 904  
 905          // Entry in other group.
 906          $result = mod_data_external::delete_entry($entry21);
 907          $result = external_api::clean_returnvalue(mod_data_external::delete_entry_returns(), $result);
 908          $this->assertEquals(0, $DB->count_records('data_records', array('id' => $entry21)));
 909      }
 910  
 911      /**
 912       * Test delete_entry as student. Check I can delete my own entries.
 913       */
 914      public function test_delete_entry_as_student() {
 915          global $DB;
 916          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
 917  
 918          $this->setUser($this->student1);
 919          $result = mod_data_external::delete_entry($entry11);
 920          $result = external_api::clean_returnvalue(mod_data_external::delete_entry_returns(), $result);
 921          $this->assertEquals(0, $DB->count_records('data_records', array('id' => $entry11)));
 922      }
 923  
 924      /**
 925       * Test delete_entry as student in read only mode period. Check I cannot delete my own entries in that period.
 926       */
 927      public function test_delete_entry_as_student_in_read_only_period() {
 928          global $DB;
 929          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
 930          // Set a time period.
 931          $this->database->timeviewfrom = time() - HOURSECS;
 932          $this->database->timeviewto = time() + HOURSECS;
 933          $DB->update_record('data', $this->database);
 934  
 935          $this->setUser($this->student1);
 936          $this->expectException('moodle_exception');
 937          mod_data_external::delete_entry($entry11);
 938      }
 939  
 940      /**
 941       * Test delete_entry with an user missing permissions.
 942       */
 943      public function test_delete_entry_missing_permissions() {
 944          global $DB;
 945          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
 946  
 947          $this->setUser($this->student1);
 948          $this->expectException('moodle_exception');
 949          mod_data_external::delete_entry($entry21);
 950      }
 951  
 952      /**
 953       * Test add_entry.
 954       */
 955      public function test_add_entry() {
 956          global $DB;
 957          // First create the record structure and add some entries.
 958          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
 959  
 960          $this->setUser($this->student1);
 961          $newentrydata = [];
 962          $fields = $DB->get_records('data_fields', array('dataid' => $this->database->id), 'id');
 963          // Prepare the new entry data.
 964          foreach ($fields as $field) {
 965              $subfield = $value = '';
 966  
 967              switch ($field->type) {
 968                  case 'checkbox':
 969                      $value = ['opt1', 'opt2'];
 970                      break;
 971                  case 'date':
 972                      // Add two extra.
 973                      $newentrydata[] = [
 974                          'fieldid' => $field->id,
 975                          'subfield' => 'day',
 976                          'value' => json_encode('5')
 977                      ];
 978                      $newentrydata[] = [
 979                          'fieldid' => $field->id,
 980                          'subfield' => 'month',
 981                          'value' => json_encode('1')
 982                      ];
 983                      $subfield = 'year';
 984                      $value = '1981';
 985                      break;
 986                  case 'menu':
 987                      $value = 'menu1';
 988                      break;
 989                  case 'multimenu':
 990                      $value = ['multimenu1', 'multimenu4'];
 991                      break;
 992                  case 'number':
 993                      $value = 6;
 994                      break;
 995                  case 'radiobutton':
 996                      $value = 'radioopt1';
 997                      break;
 998                  case 'text':
 999                      $value = 'some text';
1000                      break;
1001                  case 'textarea':
1002                      $newentrydata[] = [
1003                          'fieldid' => $field->id,
1004                          'subfield' => 'content1',
1005                          'value' => json_encode(FORMAT_MOODLE)
1006                      ];
1007                      $newentrydata[] = [
1008                          'fieldid' => $field->id,
1009                          'subfield' => 'itemid',
1010                          'value' => json_encode(0)
1011                      ];
1012                      $value = 'more text';
1013                      break;
1014                  case 'url':
1015                      $value = 'https://moodle.org';
1016                      $subfield = 0;
1017                      break;
1018              }
1019  
1020              $newentrydata[] = [
1021                  'fieldid' => $field->id,
1022                  'subfield' => $subfield,
1023                  'value' => json_encode($value)
1024              ];
1025          }
1026          $result = mod_data_external::add_entry($this->database->id, 0, $newentrydata);
1027          $result = external_api::clean_returnvalue(mod_data_external::add_entry_returns(), $result);
1028  
1029          $newentryid = $result['newentryid'];
1030          $result = mod_data_external::get_entry($newentryid, true);
1031          $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
1032          $this->assertEquals($this->student1->id, $result['entry']['userid']);
1033          $this->assertCount(9, $result['entry']['contents']);
1034          foreach ($result['entry']['contents'] as $content) {
1035              $field = $fields[$content['fieldid']];
1036              // Stored content same that the one retrieved by WS.
1037              $dbcontent = $DB->get_record('data_content', array('fieldid' => $field->id, 'recordid' => $newentryid));
1038              $this->assertEquals($dbcontent->content, $content['content']);
1039  
1040              // Now double check everything stored is correct.
1041              if ($field->type == 'checkbox') {
1042                  $this->assertEquals('opt1##opt2', $content['content']);
1043                  continue;
1044              }
1045              if ($field->type == 'date') {
1046                  $this->assertEquals(347500800, $content['content']); // Date in gregorian format.
1047                  continue;
1048              }
1049              if ($field->type == 'menu') {
1050                  $this->assertEquals('menu1', $content['content']);
1051                  continue;
1052              }
1053              if ($field->type == 'multimenu') {
1054                  $this->assertEquals('multimenu1##multimenu4', $content['content']);
1055                  continue;
1056              }
1057              if ($field->type == 'number') {
1058                  $this->assertEquals(6, $content['content']);
1059                  continue;
1060              }
1061              if ($field->type == 'radiobutton') {
1062                  $this->assertEquals('radioopt1', $content['content']);
1063                  continue;
1064              }
1065              if ($field->type == 'text') {
1066                  $this->assertEquals('some text', $content['content']);
1067                  continue;
1068              }
1069              if ($field->type == 'textarea') {
1070                  $this->assertEquals('more text', $content['content']);
1071                  $this->assertEquals(FORMAT_MOODLE, $content['content1']);
1072                  continue;
1073              }
1074              if ($field->type == 'url') {
1075                  $this->assertEquals('https://moodle.org', $content['content']);
1076                  continue;
1077              }
1078              $this->assertEquals('multimenu1##multimenu4', $content['content']);
1079          }
1080  
1081          // Now, try to add another entry but removing some required data.
1082          unset($newentrydata[0]);
1083          $result = mod_data_external::add_entry($this->database->id, 0, $newentrydata);
1084          $result = external_api::clean_returnvalue(mod_data_external::add_entry_returns(), $result);
1085          $this->assertEquals(0, $result['newentryid']);
1086          $this->assertCount(0, $result['generalnotifications']);
1087          $this->assertCount(1, $result['fieldnotifications']);
1088          $this->assertEquals('field-1', $result['fieldnotifications'][0]['fieldname']);
1089          $this->assertEquals(get_string('errormustsupplyvalue', 'data'), $result['fieldnotifications'][0]['notification']);
1090      }
1091  
1092      /**
1093       * Test add_entry empty_form.
1094       */
1095      public function test_add_entry_empty_form() {
1096          $result = mod_data_external::add_entry($this->database->id, 0, []);
1097          $result = external_api::clean_returnvalue(mod_data_external::add_entry_returns(), $result);
1098          $this->assertEquals(0, $result['newentryid']);
1099          $this->assertCount(1, $result['generalnotifications']);
1100          $this->assertCount(0, $result['fieldnotifications']);
1101          $this->assertEquals(get_string('emptyaddform', 'data'), $result['generalnotifications'][0]);
1102      }
1103  
1104      /**
1105       * Test add_entry read_only_period.
1106       */
1107      public function test_add_entry_read_only_period() {
1108          global $DB;
1109          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
1110          // Set a time period.
1111          $this->database->timeviewfrom = time() - HOURSECS;
1112          $this->database->timeviewto = time() + HOURSECS;
1113          $DB->update_record('data', $this->database);
1114  
1115          $this->setUser($this->student1);
1116          $this->expectExceptionMessage(get_string('noaccess', 'data'));
1117          $this->expectException('moodle_exception');
1118          mod_data_external::add_entry($this->database->id, 0, []);
1119      }
1120  
1121      /**
1122       * Test add_entry max_num_entries.
1123       */
1124      public function test_add_entry_max_num_entries() {
1125          global $DB;
1126          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
1127          // Set a time period.
1128          $this->database->maxentries = 1;
1129          $DB->update_record('data', $this->database);
1130  
1131          $this->setUser($this->student1);
1132          $this->expectExceptionMessage(get_string('noaccess', 'data'));
1133          $this->expectException('moodle_exception');
1134          mod_data_external::add_entry($this->database->id, 0, []);
1135      }
1136  
1137      /**
1138       * Test add_entry invalid group.
1139       */
1140      public function test_add_entry_invalid_group() {
1141          $this->setUser($this->student1);
1142          $this->expectExceptionMessage(get_string('noaccess', 'data'));
1143          $this->expectException('moodle_exception');
1144          mod_data_external::add_entry($this->database->id, $this->group2->id, []);
1145      }
1146  
1147      /**
1148       * Test update_entry.
1149       */
1150      public function test_update_entry() {
1151          global $DB;
1152          // First create the record structure and add some entries.
1153          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
1154  
1155          $this->setUser($this->student1);
1156          $newentrydata = [];
1157          $fields = $DB->get_records('data_fields', array('dataid' => $this->database->id), 'id');
1158          // Prepare the new entry data.
1159          foreach ($fields as $field) {
1160              $subfield = $value = '';
1161  
1162              switch ($field->type) {
1163                  case 'checkbox':
1164                      $value = ['opt1', 'opt2'];
1165                      break;
1166                  case 'date':
1167                      // Add two extra.
1168                      $newentrydata[] = [
1169                          'fieldid' => $field->id,
1170                          'subfield' => 'day',
1171                          'value' => json_encode('5')
1172                      ];
1173                      $newentrydata[] = [
1174                          'fieldid' => $field->id,
1175                          'subfield' => 'month',
1176                          'value' => json_encode('1')
1177                      ];
1178                      $subfield = 'year';
1179                      $value = '1981';
1180                      break;
1181                  case 'menu':
1182                      $value = 'menu1';
1183                      break;
1184                  case 'multimenu':
1185                      $value = ['multimenu1', 'multimenu4'];
1186                      break;
1187                  case 'number':
1188                      $value = 6;
1189                      break;
1190                  case 'radiobutton':
1191                      $value = 'radioopt2';
1192                      break;
1193                  case 'text':
1194                      $value = 'some text';
1195                      break;
1196                  case 'textarea':
1197                      $newentrydata[] = [
1198                          'fieldid' => $field->id,
1199                          'subfield' => 'content1',
1200                          'value' => json_encode(FORMAT_MOODLE)
1201                      ];
1202                      $newentrydata[] = [
1203                          'fieldid' => $field->id,
1204                          'subfield' => 'itemid',
1205                          'value' => json_encode(0)
1206                      ];
1207                      $value = 'more text';
1208                      break;
1209                  case 'url':
1210                      $value = 'https://moodle.org';
1211                      $subfield = 0;
1212                      break;
1213              }
1214  
1215              $newentrydata[] = [
1216                  'fieldid' => $field->id,
1217                  'subfield' => $subfield,
1218                  'value' => json_encode($value)
1219              ];
1220          }
1221          $result = mod_data_external::update_entry($entry11, $newentrydata);
1222          $result = external_api::clean_returnvalue(mod_data_external::update_entry_returns(), $result);
1223          $this->assertTrue($result['updated']);
1224          $this->assertCount(0, $result['generalnotifications']);
1225          $this->assertCount(0, $result['fieldnotifications']);
1226  
1227          $result = mod_data_external::get_entry($entry11, true);
1228          $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
1229          $this->assertEquals($this->student1->id, $result['entry']['userid']);
1230          $this->assertCount(9, $result['entry']['contents']);
1231          foreach ($result['entry']['contents'] as $content) {
1232              $field = $fields[$content['fieldid']];
1233              // Stored content same that the one retrieved by WS.
1234              $dbcontent = $DB->get_record('data_content', array('fieldid' => $field->id, 'recordid' => $entry11));
1235              $this->assertEquals($dbcontent->content, $content['content']);
1236  
1237              // Now double check everything stored is correct.
1238              if ($field->type == 'checkbox') {
1239                  $this->assertEquals('opt1##opt2', $content['content']);
1240                  continue;
1241              }
1242              if ($field->type == 'date') {
1243                  $this->assertEquals(347500800, $content['content']); // Date in gregorian format.
1244                  continue;
1245              }
1246              if ($field->type == 'menu') {
1247                  $this->assertEquals('menu1', $content['content']);
1248                  continue;
1249              }
1250              if ($field->type == 'multimenu') {
1251                  $this->assertEquals('multimenu1##multimenu4', $content['content']);
1252                  continue;
1253              }
1254              if ($field->type == 'number') {
1255                  $this->assertEquals(6, $content['content']);
1256                  continue;
1257              }
1258              if ($field->type == 'radiobutton') {
1259                  $this->assertEquals('radioopt2', $content['content']);
1260                  continue;
1261              }
1262              if ($field->type == 'text') {
1263                  $this->assertEquals('some text', $content['content']);
1264                  continue;
1265              }
1266              if ($field->type == 'textarea') {
1267                  $this->assertEquals('more text', $content['content']);
1268                  $this->assertEquals(FORMAT_MOODLE, $content['content1']);
1269                  continue;
1270              }
1271              if ($field->type == 'url') {
1272                  $this->assertEquals('https://moodle.org', $content['content']);
1273                  continue;
1274              }
1275              $this->assertEquals('multimenu1##multimenu4', $content['content']);
1276          }
1277  
1278          // Now, try to update the entry but removing some required data.
1279          unset($newentrydata[0]);
1280          $result = mod_data_external::update_entry($entry11, $newentrydata);
1281          $result = external_api::clean_returnvalue(mod_data_external::update_entry_returns(), $result);
1282          $this->assertFalse($result['updated']);
1283          $this->assertCount(0, $result['generalnotifications']);
1284          $this->assertCount(1, $result['fieldnotifications']);
1285          $this->assertEquals('field-1', $result['fieldnotifications'][0]['fieldname']);
1286          $this->assertEquals(get_string('errormustsupplyvalue', 'data'), $result['fieldnotifications'][0]['notification']);
1287      }
1288  
1289      /**
1290       * Test update_entry sending empty data.
1291       */
1292      public function test_update_entry_empty_data() {
1293          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
1294  
1295          $this->setUser($this->student1);
1296          $result = mod_data_external::update_entry($entry11, []);
1297          $result = external_api::clean_returnvalue(mod_data_external::update_entry_returns(), $result);
1298          $this->assertFalse($result['updated']);
1299          $this->assertCount(1, $result['generalnotifications']);
1300          $this->assertCount(9, $result['fieldnotifications']);
1301          $this->assertEquals(get_string('emptyaddform', 'data'), $result['generalnotifications'][0]);
1302      }
1303  
1304      /**
1305       * Test update_entry in read only period.
1306       */
1307      public function test_update_entry_read_only_period() {
1308          global $DB;
1309          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
1310          // Set a time period.
1311          $this->database->timeviewfrom = time() - HOURSECS;
1312          $this->database->timeviewto = time() + HOURSECS;
1313          $DB->update_record('data', $this->database);
1314  
1315          $this->setUser($this->student1);
1316          $this->expectExceptionMessage(get_string('noaccess', 'data'));
1317          $this->expectException('moodle_exception');
1318          mod_data_external::update_entry($entry11, []);
1319      }
1320  
1321      /**
1322       * Test update_entry other_user.
1323       */
1324      public function test_update_entry_other_user() {
1325          // Try to update other user entry.
1326          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
1327          $this->setUser($this->student2);
1328          $this->expectExceptionMessage(get_string('noaccess', 'data'));
1329          $this->expectException('moodle_exception');
1330          mod_data_external::update_entry($entry11, []);
1331      }
1332  
1333      /**
1334       * Test get_entry_rating_information.
1335       */
1336      public function test_get_entry_rating_information() {
1337          global $DB, $CFG;
1338          require_once($CFG->dirroot . '/rating/lib.php');
1339  
1340          $DB->set_field('data', 'assessed', RATING_AGGREGATE_SUM, array('id' => $this->database->id));
1341          $DB->set_field('data', 'scale', 100, array('id' => $this->database->id));
1342          list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
1343  
1344          $user1 = self::getDataGenerator()->create_user();
1345          $user2 = self::getDataGenerator()->create_user();
1346          $this->getDataGenerator()->enrol_user($user1->id, $this->course->id, $this->studentrole->id, 'manual');
1347          $this->getDataGenerator()->enrol_user($user2->id, $this->course->id, $this->studentrole->id, 'manual');
1348  
1349          // Rate the entry as user1.
1350          $rating1 = new stdClass();
1351          $rating1->contextid = $this->context->id;
1352          $rating1->component = 'mod_data';
1353          $rating1->ratingarea = 'entry';
1354          $rating1->itemid = $entry11;
1355          $rating1->rating = 50;
1356          $rating1->scaleid = 100;
1357          $rating1->userid = $user1->id;
1358          $rating1->timecreated = time();
1359          $rating1->timemodified = time();
1360          $rating1->id = $DB->insert_record('rating', $rating1);
1361  
1362          // Rate the entry as user2.
1363          $rating2 = new stdClass();
1364          $rating2->contextid = $this->context->id;
1365          $rating2->component = 'mod_data';
1366          $rating2->ratingarea = 'entry';
1367          $rating2->itemid = $entry11;
1368          $rating2->rating = 100;
1369          $rating2->scaleid = 100;
1370          $rating2->userid = $user2->id;
1371          $rating2->timecreated = time() + 1;
1372          $rating2->timemodified = time() + 1;
1373          $rating2->id = $DB->insert_record('rating', $rating2);
1374  
1375          // As student, retrieve ratings information.
1376          $this->setUser($this->student2);
1377          $result = mod_data_external::get_entry($entry11);
1378          $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
1379          $this->assertCount(1, $result['ratinginfo']['ratings']);
1380          $this->assertFalse($result['ratinginfo']['ratings'][0]['canviewaggregate']);
1381          $this->assertFalse($result['ratinginfo']['canviewall']);
1382          $this->assertFalse($result['ratinginfo']['ratings'][0]['canrate']);
1383          $this->assertTrue(!isset($result['ratinginfo']['ratings'][0]['count']));
1384  
1385          // Now, as teacher, I should see the info correctly.
1386          $this->setUser($this->teacher);
1387          $result = mod_data_external::get_entry($entry11);
1388          $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
1389          $this->assertCount(1, $result['ratinginfo']['ratings']);
1390          $this->assertTrue($result['ratinginfo']['ratings'][0]['canviewaggregate']);
1391          $this->assertTrue($result['ratinginfo']['canviewall']);
1392          $this->assertTrue($result['ratinginfo']['ratings'][0]['canrate']);
1393          $this->assertEquals(2, $result['ratinginfo']['ratings'][0]['count']);
1394          $this->assertEquals(100, $result['ratinginfo']['ratings'][0]['aggregate']); // Expect maximium scale value.
1395      }
1396  }