Search moodle.org's
Developer Documentation

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