__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ V /  | |__) | __ ___   ____ _| |_ ___  | (___ | |__   ___| | |
 | |\/| | '__|> <   |  ___/ '__| \ \ / / _` | __/ _ \  \___ \| '_ \ / _ \ | |
 | |  | | |_ / . \  | |   | |  | |\ V / (_| | ||  __/  ____) | | | |  __/ | |
 |_|  |_|_(_)_/ \_\ |_|   |_|  |_| \_/ \__,_|\__\___| |_____/|_| |_|\___V 2.1
 if you need WebShell for Seo everyday contact me on Telegram
 Telegram Address : @jackleet
        
        
For_More_Tools: Telegram: @jackleet | Bulk Smtp support mail sender | Business Mail Collector | Mail Bouncer All Mail | Bulk Office Mail Validator | Html Letter private



Upload:

Command:

www-data@216.73.216.10: ~ $
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

namespace mod_data;

use externallib_advanced_testcase;
use mod_data_external;
use core_external\external_api;
use core_external\external_settings;

defined('MOODLE_INTERNAL') || die();

global $CFG;

require_once($CFG->dirroot . '/webservice/tests/helpers.php');

/**
 * Database module external functions tests
 *
 * @package    mod_data
 * @category   external
 * @copyright  2015 Juan Leyva <juan@moodle.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 * @since      Moodle 2.9
 * @coversDefaultClass \mod_data_external
 */
final class externallib_test extends externallib_advanced_testcase {

    /** @var stdClass Test module context. */
    protected $context;

    /** @var stdClass Test course.*/
    protected $course;

    /** @var stdClass Test course module. */
    protected $cm;

    /** @var  stdClass Test database activity. */
    protected $database;

    /** @var stdClass Test group 1. */
    protected $group1;

    /** @var stdClass Test group 2. */
    protected $group2;

    /** @var stdClass Test student 1. */
    protected $student1;

    /** @var stdClass Test student 2. */
    protected $student2;

    /** @var stdClass Test student 3. */
    protected $student3;

    /** @var stdClass Test student 4. */
    protected $student4;

    /** @var stdClass Student role. */
    protected $studentrole;

    /** @var stdClass Test teacher. */
    protected $teacher;

    /** @var stdClass Teacher role. */
    protected $teacherrole;

    /**
     * Set up for every test
     */
    public function setUp(): void {
        global $DB;
        parent::setUp();
        $this->resetAfterTest();
        $this->setAdminUser();

        // Setup test data.
        $course = new \stdClass();
        $course->groupmode = SEPARATEGROUPS;
        $course->groupmodeforce = true;
        $this->course = $this->getDataGenerator()->create_course($course);
        $this->database = $this->getDataGenerator()->create_module('data', array('course' => $this->course->id));
        $this->context = \context_module::instance($this->database->cmid);
        $this->cm = get_coursemodule_from_instance('data', $this->database->id);

        // Create users.
        $this->student1 = self::getDataGenerator()->create_user(['firstname' => 'Olivia', 'lastname' => 'Smith']);
        $this->student2 = self::getDataGenerator()->create_user(['firstname' => 'Ezra', 'lastname' => 'Johnson']);
        $this->student3 = self::getDataGenerator()->create_user(['firstname' => 'Amelia', 'lastname' => 'Williams']);
        $this->teacher = self::getDataGenerator()->create_user(['firstname' => 'Asher', 'lastname' => 'Jones']);

        // Users enrolments.
        $this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
        $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
        $this->getDataGenerator()->enrol_user($this->student1->id, $this->course->id, $this->studentrole->id, 'manual');
        $this->getDataGenerator()->enrol_user($this->student2->id, $this->course->id, $this->studentrole->id, 'manual');
        $this->getDataGenerator()->enrol_user($this->student3->id, $this->course->id, $this->studentrole->id, 'manual');
        $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual');

        $this->group1 = $this->getDataGenerator()->create_group(array('courseid' => $this->course->id));
        $this->group2 = $this->getDataGenerator()->create_group(array('courseid' => $this->course->id));
        groups_add_member($this->group1, $this->student1);
        groups_add_member($this->group1, $this->student2);
        groups_add_member($this->group2, $this->student3);
    }

    /**
     * Add a test field to the database activity instance to be used in the unit tests.
     *
     * @return \data_field_base
     */
    protected function add_test_field(): \data_field_base {
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');

        // Add fields.
        $fieldrecord = new \stdClass();
        $fieldrecord->name = 'Test field'; // Identifier of the records for testing.
        $fieldrecord->type = 'text';
        return $generator->create_field($fieldrecord, $this->database);
    }

    /**
     * Test get databases by courses
     */
    public function test_mod_data_get_databases_by_courses(): void {
        global $DB;

        $this->resetAfterTest(true);

        // Create users.
        $student = self::getDataGenerator()->create_user();
        $teacher = self::getDataGenerator()->create_user();

        // Set to the student user.
        self::setUser($student);

        // Create courses to add the modules.
        $course1 = self::getDataGenerator()->create_course();
        $course2 = self::getDataGenerator()->create_course();

        // First database.
        $record = new \stdClass();
        $record->introformat = FORMAT_HTML;
        $record->course = $course1->id;
        // Set multilang text to check that is properly filtered to "en" only.
        $record->name = '<span lang="en" class="multilang">English</span><span lang="es" class="multilang">EspaƱol</span>';
        $record->intro = '<button>Test with HTML allowed.</button>';
        $database1 = self::getDataGenerator()->create_module('data', $record);

        // Second database.
        $record = new \stdClass();
        $record->introformat = FORMAT_HTML;
        $record->course = $course2->id;
        $database2 = self::getDataGenerator()->create_module('data', $record);

        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
        $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));

        // Users enrolments.
        $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
        $this->getDataGenerator()->enrol_user($teacher->id, $course1->id, $teacherrole->id, 'manual');

        // Execute real Moodle enrolment as we'll call unenrol() method on the instance later.
        $enrol = enrol_get_plugin('manual');
        $enrolinstances = enrol_get_instances($course2->id, true);
        foreach ($enrolinstances as $courseenrolinstance) {
            if ($courseenrolinstance->enrol == "manual") {
                $instance2 = $courseenrolinstance;
                break;
            }
        }
        $enrol->enrol_user($instance2, $student->id, $studentrole->id);

        // Enable multilang filter to on content and heading.
        \filter_manager::reset_caches();
        filter_set_global_state('multilang', TEXTFILTER_ON);
        filter_set_applies_to_strings('multilang', true);
        // Set WS filtering.
        $wssettings = external_settings::get_instance();
        $wssettings->set_filter(true);

        // Create what we expect to be returned when querying the two courses.
        // First for the student user.
        $expectedfields = array('id', 'coursemodule', 'course', 'name', 'comments', 'timeavailablefrom',
                            'timeavailableto', 'timeviewfrom', 'timeviewto', 'requiredentries', 'requiredentriestoview',
                            'intro', 'introformat', 'introfiles', 'lang',
                            'maxentries', 'rssarticles', 'singletemplate', 'listtemplate',
                            'listtemplateheader', 'listtemplatefooter', 'addtemplate', 'rsstemplate', 'rsstitletemplate',
                            'csstemplate', 'jstemplate', 'asearchtemplate', 'approval',
                            'defaultsort', 'defaultsortdir', 'manageapproved');

        // Add expected coursemodule.
        $database1->coursemodule = $database1->cmid;
        $database1->introfiles = [];
        $database1->lang = '';
        $database2->coursemodule = $database2->cmid;
        $database2->introfiles = [];
        $database2->lang = '';

        $expected1 = array();
        $expected2 = array();
        foreach ($expectedfields as $field) {
            if ($field == 'approval' or $field == 'manageapproved') {
                $database1->{$field} = (bool) $database1->{$field};
                $database2->{$field} = (bool) $database2->{$field};
            }
            $expected1[$field] = $database1->{$field};
            $expected2[$field] = $database2->{$field};
        }
        $expected1['name'] = 'English'; // Lang filtered expected.
        $expected1['comments'] = (bool) $expected1['comments'];
        $expected2['comments'] = (bool) $expected2['comments'];

        $expecteddatabases = array();
        $expecteddatabases[] = $expected2;
        $expecteddatabases[] = $expected1;

        // Call the external function passing course ids.
        $result = mod_data_external::get_databases_by_courses(array($course2->id, $course1->id));
        $result = external_api::clean_returnvalue(mod_data_external::get_databases_by_courses_returns(), $result);
        $this->assertEquals($expecteddatabases, $result['databases']);

        // Call the external function without passing course id.
        $result = mod_data_external::get_databases_by_courses();
        $result = external_api::clean_returnvalue(mod_data_external::get_databases_by_courses_returns(), $result);
        $this->assertEquals($expecteddatabases, $result['databases']);

        // Unenrol user from second course and alter expected databases.
        $enrol->unenrol_user($instance2, $student->id);
        array_shift($expecteddatabases);

        // Call the external function without passing course id.
        $result = mod_data_external::get_databases_by_courses();
        $result = external_api::clean_returnvalue(mod_data_external::get_databases_by_courses_returns(), $result);
        $this->assertEquals($expecteddatabases, $result['databases']);

        // Call for the second course we unenrolled the user from, expected warning.
        $result = mod_data_external::get_databases_by_courses(array($course2->id));
        $this->assertCount(1, $result['warnings']);
        $this->assertEquals('1', $result['warnings'][0]['warningcode']);
        $this->assertEquals($course2->id, $result['warnings'][0]['itemid']);

        // Now, try as a teacher for getting all the additional fields.
        self::setUser($teacher);

        $additionalfields = array('scale', 'assessed', 'assesstimestart', 'assesstimefinish', 'editany', 'notification', 'timemodified');

        foreach ($additionalfields as $field) {
            if ($field == 'editany') {
                $database1->{$field} = (bool) $database1->{$field};
            }
            $expecteddatabases[0][$field] = $database1->{$field};
        }
        $result = mod_data_external::get_databases_by_courses();
        $result = external_api::clean_returnvalue(mod_data_external::get_databases_by_courses_returns(), $result);
        $this->assertEquals($expecteddatabases, $result['databases']);

        // Admin should get all the information.
        self::setAdminUser();

        $result = mod_data_external::get_databases_by_courses(array($course1->id));
        $result = external_api::clean_returnvalue(mod_data_external::get_databases_by_courses_returns(), $result);
        $this->assertEquals($expecteddatabases, $result['databases']);
    }

    /**
     * Test view_database invalid id.
     */
    public function test_view_database_invalid_id(): void {

        // Test invalid instance id.
        $this->expectException('moodle_exception');
        mod_data_external::view_database(0);
    }

    /**
     * Test view_database not enrolled user.
     */
    public function test_view_database_not_enrolled_user(): void {

        $usernotenrolled = self::getDataGenerator()->create_user();
        $this->setUser($usernotenrolled);

        $this->expectException('moodle_exception');
        mod_data_external::view_database(0);
    }

    /**
     * Test view_database no capabilities.
     */
    public function test_view_database_no_capabilities(): void {
        // Test user with no capabilities.
        // We need a explicit prohibit since this capability is allowed for students by default.
        assign_capability('mod/data:view', CAP_PROHIBIT, $this->studentrole->id, $this->context->id);
        accesslib_clear_all_caches_for_unit_testing();

        $this->expectException('moodle_exception');
        mod_data_external::view_database(0);
    }

    /**
     * Test view_database.
     */
    public function test_view_database(): void {

        // Test user with full capabilities.
        $this->setUser($this->student1);

        // Trigger and capture the event.
        $sink = $this->redirectEvents();

        $result = mod_data_external::view_database($this->database->id);
        $result = external_api::clean_returnvalue(mod_data_external::view_database_returns(), $result);

        $events = $sink->get_events();
        $this->assertCount(1, $events);
        $event = array_shift($events);

        // Checking that the event contains the expected values.
        $this->assertInstanceOf('\mod_data\event\course_module_viewed', $event);
        $this->assertEquals($this->context, $event->get_context());
        $moodledata = new \moodle_url('/mod/data/view.php', array('id' => $this->cm->id));
        $this->assertEquals($moodledata, $event->get_url());
        $this->assertEventContextNotUsed($event);
        $this->assertNotEmpty($event->get_name());
    }

    /**
     * Test get_data_access_information for student.
     */
    public function test_get_data_access_information_student(): void {
        global $DB;

        // Add a field to database to let users add new entries.
        $this->add_test_field();

        // Modify the database to add access restrictions.
        $this->database->timeavailablefrom = time() + DAYSECS;
        $this->database->requiredentries = 2;
        $this->database->requiredentriestoview = 2;
        $DB->update_record('data', $this->database);

        // Test user with full capabilities.
        $this->setUser($this->student1);

        $result = mod_data_external::get_data_access_information($this->database->id);
        $result = external_api::clean_returnvalue(mod_data_external::get_data_access_information_returns(), $result);

        $this->assertEquals($this->group1->id, $result['groupid']);

        $this->assertFalse($result['canmanageentries']);
        $this->assertFalse($result['canapprove']);
        $this->assertTrue($result['canaddentry']);  // It return true because it doen't check time restrictions.
        $this->assertFalse($result['timeavailable']);
        $this->assertFalse($result['inreadonlyperiod']);
        $this->assertEquals(0, $result['numentries']);
        $this->assertEquals($this->database->requiredentries, $result['entrieslefttoadd']);
        $this->assertEquals($this->database->requiredentriestoview, $result['entrieslefttoview']);
    }

    /**
     * Test get_data_access_information for teacher.
     */
    public function test_get_data_access_information_teacher(): void {
        global $DB;

        // Add a field to database to let users add new entries.
        $this->add_test_field();

        // Modify the database to add access restrictions.
        $this->database->timeavailablefrom = time() + DAYSECS;
        $this->database->requiredentries = 2;
        $this->database->requiredentriestoview = 2;
        $DB->update_record('data', $this->database);

        // Test user with full capabilities.
        $this->setUser($this->teacher);

        $result = mod_data_external::get_data_access_information($this->database->id);
        $result = external_api::clean_returnvalue(mod_data_external::get_data_access_information_returns(), $result);

        $this->assertEquals(0, $result['groupid']);

        $this->assertTrue($result['canmanageentries']);
        $this->assertTrue($result['canapprove']);
        $this->assertTrue($result['canaddentry']);  // It return true because it doen't check time restrictions.
        $this->assertTrue($result['timeavailable']);
        $this->assertFalse($result['inreadonlyperiod']);
        $this->assertEquals(0, $result['numentries']);
        $this->assertEquals(0, $result['entrieslefttoadd']);
        $this->assertEquals(0, $result['entrieslefttoview']);
    }

    /**
     * Test get_data_access_information with groups.
     */
    public function test_get_data_access_information_groups(): void {
        global $DB;

        // Add a field to database to let users add new entries.
        $this->add_test_field();

        $DB->set_field('course', 'groupmode', VISIBLEGROUPS, ['id' => $this->course->id]);

        // Check I can see my group.
        $this->setUser($this->student1);

        $result = mod_data_external::get_data_access_information($this->database->id);
        $result = external_api::clean_returnvalue(mod_data_external::get_data_access_information_returns(), $result);

        $this->assertEquals($this->group1->id, $result['groupid']); // My group is correctly found.
        $this->assertFalse($result['canmanageentries']);
        $this->assertFalse($result['canapprove']);
        $this->assertTrue($result['canaddentry']);  // I can entries in my groups.
        $this->assertTrue($result['timeavailable']);
        $this->assertFalse($result['inreadonlyperiod']);
        $this->assertEquals(0, $result['numentries']);
        $this->assertEquals(0, $result['entrieslefttoadd']);
        $this->assertEquals(0, $result['entrieslefttoview']);

        // Check the other course group in visible groups mode.
        $result = mod_data_external::get_data_access_information($this->database->id, $this->group2->id);
        $result = external_api::clean_returnvalue(mod_data_external::get_data_access_information_returns(), $result);

        $this->assertEquals($this->group2->id, $result['groupid']); // The group is correctly found.
        $this->assertFalse($result['canmanageentries']);
        $this->assertFalse($result['canapprove']);
        $this->assertFalse($result['canaddentry']);  // I cannot add entries in other groups.
        $this->assertTrue($result['timeavailable']);
        $this->assertFalse($result['inreadonlyperiod']);
        $this->assertEquals(0, $result['numentries']);
        $this->assertEquals(0, $result['entrieslefttoadd']);
        $this->assertEquals(0, $result['entrieslefttoview']);
    }

    /**
     * Helper method to populate the database with some entries.
     *
     * @return array the entry ids created
     */
    public function populate_database_with_entries() {
        global $DB;

        // Force approval.
        $DB->set_field('data', 'approval', 1, array('id' => $this->database->id));
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
        $fieldtypes = array('checkbox', 'date', 'menu', 'multimenu', 'number', 'radiobutton', 'text', 'textarea', 'url');

        $count = 1;
        // Creating test Fields with default parameter values.
        foreach ($fieldtypes as $fieldtype) {
            $fieldname = 'field-' . $count;
            $record = new \stdClass();
            $record->name = $fieldname;
            $record->type = $fieldtype;
            $record->required = 1;

            $generator->create_field($record, $this->database);
            $count++;
        }
        // Get all the fields created.
        $fields = $DB->get_records('data_fields', array('dataid' => $this->database->id), 'id');

        // Populate with contents, creating a new entry.
        $contents = array();
        $contents[] = array('opt1', 'opt2', 'opt3', 'opt4');
        $contents[] = '01-01-2037'; // It should be lower than 2038, to avoid failing on 32-bit windows.
        $contents[] = 'menu1';
        $contents[] = array('multimenu1', 'multimenu2', 'multimenu3', 'multimenu4');
        $contents[] = '12345';
        $contents[] = 'radioopt1';
        $contents[] = 'text for testing';
        $contents[] = '<p>text area testing<br /></p>';
        $contents[] = array('example.url', 'sampleurl');
        $count = 0;
        $fieldcontents = array();
        foreach ($fields as $fieldrecord) {
            $fieldcontents[$fieldrecord->id] = $contents[$count++];
        }

        $this->setUser($this->student1);
        $entry11 = $generator->create_entry($this->database, $fieldcontents, $this->group1->id, ['Cats', 'Dogs']);
        $this->setUser($this->student2);
        $entry12 = $generator->create_entry($this->database, $fieldcontents, $this->group1->id, ['Cats']);
        $entry13 = $generator->create_entry($this->database, $fieldcontents, $this->group1->id);
        // Entry not in group.
        $entry14 = $generator->create_entry($this->database, $fieldcontents, 0);

        $this->setUser($this->student3);
        $entry21 = $generator->create_entry($this->database, $fieldcontents, $this->group2->id);

        // Approve all except $entry13.
        $DB->set_field('data_records', 'approved', 1, ['id' => $entry11]);
        $DB->set_field('data_records', 'approved', 1, ['id' => $entry12]);
        $DB->set_field('data_records', 'approved', 1, ['id' => $entry14]);
        $DB->set_field('data_records', 'approved', 1, ['id' => $entry21]);

        return [$entry11, $entry12, $entry13, $entry14, $entry21];
    }

    /**
     * Test get_entries
     */
    public function test_get_entries(): void {
        global $DB;

        // Check the behaviour when the database has no entries.
        $result = mod_data_external::get_entries($this->database->id);
        $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
        $this->assertEmpty($result['entries']);

        $result = mod_data_external::get_entries($this->database->id, 0, true);
        $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
        $this->assertEmpty($result['entries']);
        $this->assertEmpty($result['listviewcontents']);

        // Add a few fields to the database.
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();

        // First of all, expect to see only my group entries (not other users in other groups ones).
        // We may expect entries without group also.
        $this->setUser($this->student1);
        $result = mod_data_external::get_entries($this->database->id);
        $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
        $this->assertCount(0, $result['warnings']);
        $this->assertCount(3, $result['entries']);
        $this->assertEquals(3, $result['totalcount']);
        $this->assertEquals($entry11, $result['entries'][0]['id']);
        $this->assertCount(2, $result['entries'][0]['tags']);
        $this->assertEquals($this->student1->id, $result['entries'][0]['userid']);
        $this->assertEquals($this->group1->id, $result['entries'][0]['groupid']);
        $this->assertEquals($this->database->id, $result['entries'][0]['dataid']);
        $this->assertEquals($entry12, $result['entries'][1]['id']);
        $this->assertCount(1, $result['entries'][1]['tags']);
        $this->assertEquals('Cats', $result['entries'][1]['tags'][0]['rawname']);
        $this->assertEquals($this->student2->id, $result['entries'][1]['userid']);
        $this->assertEquals($this->group1->id, $result['entries'][1]['groupid']);
        $this->assertEquals($this->database->id, $result['entries'][1]['dataid']);
        $this->assertEquals($entry14, $result['entries'][2]['id']);
        $this->assertEquals($this->student2->id, $result['entries'][2]['userid']);
        $this->assertEquals(0, $result['entries'][2]['groupid']);
        $this->assertEquals($this->database->id, $result['entries'][2]['dataid']);
        // Other user in same group.
        $this->setUser($this->student2);
        $result = mod_data_external::get_entries($this->database->id);
        $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
        $this->assertCount(0, $result['warnings']);
        $this->assertCount(4, $result['entries']);  // I can see my entry is pending approval.
        $this->assertEquals(4, $result['totalcount']);

        // Now try with the user in the second group that must see only two entries (his group entry and the one without group).
        $this->setUser($this->student3);
        $result = mod_data_external::get_entries($this->database->id);
        $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
        $this->assertCount(0, $result['warnings']);
        $this->assertCount(2, $result['entries']);
        $this->assertEquals(2, $result['totalcount']);
        $this->assertEquals($entry14, $result['entries'][0]['id']);
        $this->assertEquals($this->student2->id, $result['entries'][0]['userid']);
        $this->assertEquals(0, $result['entries'][0]['groupid']);
        $this->assertEquals($this->database->id, $result['entries'][0]['dataid']);
        $this->assertEquals($entry21, $result['entries'][1]['id']);
        $this->assertEquals($this->student3->id, $result['entries'][1]['userid']);
        $this->assertEquals($this->group2->id, $result['entries'][1]['groupid']);
        $this->assertEquals($this->database->id, $result['entries'][1]['dataid']);

        // Now, as teacher we should see all (we have permissions to view all groups).
        $this->setUser($this->teacher);
        $result = mod_data_external::get_entries($this->database->id);
        $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
        $this->assertCount(0, $result['warnings']);
        $this->assertCount(5, $result['entries']);  // I can see the not approved one.
        $this->assertEquals(5, $result['totalcount']);

        $entries = $DB->get_records('data_records', array('dataid' => $this->database->id), 'id');
        $this->assertCount(5, $entries);
        $count = 0;
        foreach ($entries as $entry) {
            $this->assertEquals($entry->id, $result['entries'][$count]['id']);
            $count++;
        }

        // Basic test passing the parameter (instead having to calculate it).
        $this->setUser($this->student1);
        $result = mod_data_external::get_entries($this->database->id, $this->group1->id);
        $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
        $this->assertCount(0, $result['warnings']);
        $this->assertCount(3, $result['entries']);
        $this->assertEquals(3, $result['totalcount']);

        // Test ordering (reverse).
        $this->setUser($this->student1);
        $result = mod_data_external::get_entries($this->database->id, $this->group1->id, false, null, 'DESC');
        $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
        $this->assertCount(0, $result['warnings']);
        $this->assertCount(3, $result['entries']);
        $this->assertEquals(3, $result['totalcount']);
        $this->assertEquals($entry14, $result['entries'][0]['id']);

        // Test pagination.
        $this->setUser($this->student1);
        $result = mod_data_external::get_entries($this->database->id, $this->group1->id, false, null, null, 0, 1);
        $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
        $this->assertCount(0, $result['warnings']);
        $this->assertCount(1, $result['entries']);
        $this->assertEquals(3, $result['totalcount']);
        $this->assertEquals($entry11, $result['entries'][0]['id']);

        $result = mod_data_external::get_entries($this->database->id, $this->group1->id, false, null, null, 1, 1);
        $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
        $this->assertCount(0, $result['warnings']);
        $this->assertCount(1, $result['entries']);
        $this->assertEquals(3, $result['totalcount']);
        $this->assertEquals($entry12, $result['entries'][0]['id']);

        // Now test the return contents.
        data_generate_default_template($this->database, 'listtemplate', 0, false, true); // Generate a default list template.
        $result = mod_data_external::get_entries($this->database->id, $this->group1->id, true, null, null, 0, 2);
        $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
        $this->assertCount(0, $result['warnings']);
        $this->assertCount(2, $result['entries']);
        $this->assertEquals(3, $result['totalcount']);
        $this->assertCount(9, $result['entries'][0]['contents']);
        $this->assertCount(9, $result['entries'][1]['contents']);
        // Search for some content.
        $this->assertTrue(strpos($result['listviewcontents'], 'opt1') !== false);
        $this->assertTrue(strpos($result['listviewcontents'], 'January') !== false);
        $this->assertTrue(strpos($result['listviewcontents'], 'menu1') !== false);
        $this->assertTrue(strpos($result['listviewcontents'], 'text for testing') !== false);
        $this->assertTrue(strpos($result['listviewcontents'], 'sampleurl') !== false);
    }

    /**
     * Test get_entry_visible_groups.
     */
    public function test_get_entry_visible_groups(): void {
        global $DB;

        $DB->set_field('course', 'groupmode', VISIBLEGROUPS, ['id' => $this->course->id]);
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();

        // Check I can see my approved group entries.
        $this->setUser($this->student1);
        $result = mod_data_external::get_entry($entry11);
        $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
        $this->assertCount(0, $result['warnings']);
        $this->assertEquals($entry11, $result['entry']['id']);
        $this->assertTrue($result['entry']['approved']);
        $this->assertTrue($result['entry']['canmanageentry']); // Is mine, I can manage it.

        // Entry from other group.
        $result = mod_data_external::get_entry($entry21);
        $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
        $this->assertCount(0, $result['warnings']);
        $this->assertEquals($entry21, $result['entry']['id']);
    }

    /**
     * Test get_entry_separated_groups.
     */
    public function test_get_entry_separated_groups(): void {
        global $DB;
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();

        // Check I can see my approved group entries.
        $this->setUser($this->student1);
        $result = mod_data_external::get_entry($entry11);
        $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
        $this->assertCount(0, $result['warnings']);
        $this->assertEquals($entry11, $result['entry']['id']);
        $this->assertTrue($result['entry']['approved']);
        $this->assertTrue($result['entry']['canmanageentry']); // Is mine, I can manage it.

        // Retrieve contents.
        data_generate_default_template($this->database, 'singletemplate', 0, false, true);
        $result = mod_data_external::get_entry($entry11, true);
        $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
        $this->assertCount(0, $result['warnings']);
        $this->assertCount(9, $result['entry']['contents']);
        $this->assertTrue(strpos($result['entryviewcontents'], 'opt1') !== false);
        $this->assertTrue(strpos($result['entryviewcontents'], 'January') !== false);
        $this->assertTrue(strpos($result['entryviewcontents'], 'menu1') !== false);
        $this->assertTrue(strpos($result['entryviewcontents'], 'text for testing') !== false);
        $this->assertTrue(strpos($result['entryviewcontents'], 'sampleurl') !== false);

        // This is in my group but I'm not the author.
        $result = mod_data_external::get_entry($entry12);
        $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
        $this->assertCount(0, $result['warnings']);
        $this->assertEquals($entry12, $result['entry']['id']);
        $this->assertTrue($result['entry']['approved']);
        $this->assertFalse($result['entry']['canmanageentry']); // Not mine.

        $this->setUser($this->student3);
        $result = mod_data_external::get_entry($entry21);
        $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
        $this->assertCount(0, $result['warnings']);
        $this->assertEquals($entry21, $result['entry']['id']);
        $this->assertTrue($result['entry']['approved']);
        $this->assertTrue($result['entry']['canmanageentry']); // Is mine, I can manage it.

        // As teacher I should be able to see all the entries.
        $this->setUser($this->teacher);
        $result = mod_data_external::get_entry($entry11);
        $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
        $this->assertEquals($entry11, $result['entry']['id']);

        $result = mod_data_external::get_entry($entry12);
        $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
        $this->assertEquals($entry12, $result['entry']['id']);
        // This is the not approved one.
        $result = mod_data_external::get_entry($entry13);
        $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
        $this->assertEquals($entry13, $result['entry']['id']);

        $result = mod_data_external::get_entry($entry21);
        $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
        $this->assertEquals($entry21, $result['entry']['id']);

        // Now, try to get a pending approval.
        $this->setUser($this->student1);
        $this->expectException('moodle_exception');
        $result = mod_data_external::get_entry($entry13);
    }

    /**
     * Test get_entry from other group in separated groups.
     */
    public function test_get_entry_other_group_separated_groups(): void {
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();

        // We should not be able to view other gropu entries (in separated groups).
        $this->setUser($this->student1);
        $this->expectException('moodle_exception');
        $result = mod_data_external::get_entry($entry21);
    }

    /**
     * Test get_fields.
     */
    public function test_get_fields(): void {
        global $DB;
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();

        $this->setUser($this->student1);
        $result = mod_data_external::get_fields($this->database->id);
        $result = external_api::clean_returnvalue(mod_data_external::get_fields_returns(), $result);

        // Basically compare we retrieve all the fields and the correct values.
        $fields = $DB->get_records('data_fields', array('dataid' => $this->database->id), 'id');
        foreach ($result['fields'] as $field) {
            $this->assertEquals($field, (array) $fields[$field['id']]);
        }
    }

    /**
     * Test get_fields_database_without_fields.
     */
    public function test_get_fields_database_without_fields(): void {

        $this->setUser($this->student1);
        $result = mod_data_external::get_fields($this->database->id);
        $result = external_api::clean_returnvalue(mod_data_external::get_fields_returns(), $result);

        $this->assertEmpty($result['fields']);
    }

    /**
     * Test search_entries.
     */
    public function test_search_entries(): void {
        global $DB;
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();

        $this->setUser($this->student1);
        // Empty search, it should return all the visible entries.
        $result = mod_data_external::search_entries($this->database->id, 0, false);
        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
        $this->assertCount(3, $result['entries']);
        $this->assertEquals(3, $result['totalcount']);

        // Search for something that does not exists.
        $result = mod_data_external::search_entries($this->database->id, 0, false, 'abc');
        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
        $this->assertCount(0, $result['entries']);
        $this->assertEquals(0, $result['totalcount']);

        // Search by text matching all the entries.
        $result = mod_data_external::search_entries($this->database->id, 0, false, 'text');
        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
        $this->assertCount(3, $result['entries']);
        $this->assertEquals(3, $result['totalcount']);
        $this->assertEquals(3, $result['maxcount']);

        // Now as the other student I should receive my not approved entry. Apply ordering here.
        $this->setUser($this->student2);
        $result = mod_data_external::search_entries($this->database->id, 0, false, 'text', [], DATA_APPROVED, 'ASC');
        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
        $this->assertCount(4, $result['entries']);
        $this->assertEquals(4, $result['totalcount']);
        $this->assertEquals(4, $result['maxcount']);
        // The not approved one should be the first.
        $this->assertEquals($entry13, $result['entries'][0]['id']);

        // Now as the other group student.
        $this->setUser($this->student3);
        $result = mod_data_external::search_entries($this->database->id, 0, false, 'text');
        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
        $this->assertCount(2, $result['entries']);
        $this->assertEquals(2, $result['totalcount']);
        $this->assertEquals(2, $result['maxcount']);
        $this->assertEquals($this->student2->id, $result['entries'][0]['userid']);
        $this->assertEquals($this->student3->id, $result['entries'][1]['userid']);

        // Same normal text search as teacher.
        $this->setUser($this->teacher);
        $result = mod_data_external::search_entries($this->database->id, 0, false, 'text');
        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
        $this->assertCount(5, $result['entries']);  // I can see all groups and non approved.
        $this->assertEquals(5, $result['totalcount']);
        $this->assertEquals(5, $result['maxcount']);

        // Pagination.
        $this->setUser($this->teacher);
        $result = mod_data_external::search_entries($this->database->id, 0, false, 'text', [], DATA_TIMEADDED, 'ASC', 0, 2);
        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
        $this->assertCount(2, $result['entries']);  // Only 2 per page.
        $this->assertEquals(5, $result['totalcount']);
        $this->assertEquals(5, $result['maxcount']);

        // Now advanced search or not dinamic fields (user firstname for example).
        $this->setUser($this->student1);
        $advsearch = [
            ['name' => 'fn', 'value' => json_encode($this->student2->firstname)]
        ];
        $result = mod_data_external::search_entries($this->database->id, 0, false, '', $advsearch);
        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
        $this->assertCount(2, $result['entries']);
        $this->assertEquals(2, $result['totalcount']);
        $this->assertEquals(3, $result['maxcount']);
        $this->assertEquals($this->student2->id, $result['entries'][0]['userid']);  // I only found mine!

        // Advanced search for fields.
        $field = $DB->get_record('data_fields', array('type' => 'url'));
        $advsearch = [
            ['name' => 'f_' . $field->id , 'value' => 'sampleurl']
        ];
        $result = mod_data_external::search_entries($this->database->id, 0, false, '', $advsearch);
        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
        $this->assertCount(3, $result['entries']);  // Found two entries matching this.
        $this->assertEquals(3, $result['totalcount']);
        $this->assertEquals(3, $result['maxcount']);

        // Combined search.
        $field2 = $DB->get_record('data_fields', array('type' => 'number'));
        $advsearch = [
            ['name' => 'f_' . $field->id , 'value' => 'sampleurl'],
            ['name' => 'f_' . $field2->id , 'value' => '12345'],
            ['name' => 'ln', 'value' => json_encode($this->student2->lastname)]
        ];
        $result = mod_data_external::search_entries($this->database->id, 0, false, '', $advsearch);
        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
        $this->assertCount(2, $result['entries']);  // Only one matching everything.
        $this->assertEquals(2, $result['totalcount']);
        $this->assertEquals(3, $result['maxcount']);

        // Combined search (no results).
        $field2 = $DB->get_record('data_fields', array('type' => 'number'));
        $advsearch = [
            ['name' => 'f_' . $field->id , 'value' => 'sampleurl'],
            ['name' => 'f_' . $field2->id , 'value' => '98780333'], // Non existent number.
        ];
        $result = mod_data_external::search_entries($this->database->id, 0, false, '', $advsearch);
        $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
        $this->assertCount(0, $result['entries']);  // Only one matching everything.
        $this->assertEquals(0, $result['totalcount']);
        $this->assertEquals(3, $result['maxcount']);
    }

    /**
     * Test approve_entry.
     */
    public function test_approve_entry(): void {
        global $DB;
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();

        $this->setUser($this->teacher);
        $this->assertEquals(0, $DB->get_field('data_records', 'approved', array('id' => $entry13)));
        $result = mod_data_external::approve_entry($entry13);
        $result = external_api::clean_returnvalue(mod_data_external::approve_entry_returns(), $result);
        $this->assertEquals(1, $DB->get_field('data_records', 'approved', array('id' => $entry13)));
    }

    /**
     * Test unapprove_entry.
     */
    public function test_unapprove_entry(): void {
        global $DB;
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();

        $this->setUser($this->teacher);
        $this->assertEquals(1, $DB->get_field('data_records', 'approved', array('id' => $entry11)));
        $result = mod_data_external::approve_entry($entry11, false);
        $result = external_api::clean_returnvalue(mod_data_external::approve_entry_returns(), $result);
        $this->assertEquals(0, $DB->get_field('data_records', 'approved', array('id' => $entry11)));
    }

    /**
     * Test approve_entry missing permissions.
     */
    public function test_approve_entry_missing_permissions(): void {
        global $DB;
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();

        $this->setUser($this->student1);
        $this->expectException('moodle_exception');
        mod_data_external::approve_entry($entry13);
    }

    /**
     * Test delete_entry as teacher. Check I can delete any entry.
     */
    public function test_delete_entry_as_teacher(): void {
        global $DB;
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();

        $this->setUser($this->teacher);
        $result = mod_data_external::delete_entry($entry11);
        $result = external_api::clean_returnvalue(mod_data_external::delete_entry_returns(), $result);
        $this->assertEquals(0, $DB->count_records('data_records', array('id' => $entry11)));

        // Entry in other group.
        $result = mod_data_external::delete_entry($entry21);
        $result = external_api::clean_returnvalue(mod_data_external::delete_entry_returns(), $result);
        $this->assertEquals(0, $DB->count_records('data_records', array('id' => $entry21)));
    }

    /**
     * Test delete_entry as student. Check I can delete my own entries.
     */
    public function test_delete_entry_as_student(): void {
        global $DB;
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();

        $this->setUser($this->student1);
        $result = mod_data_external::delete_entry($entry11);
        $result = external_api::clean_returnvalue(mod_data_external::delete_entry_returns(), $result);
        $this->assertEquals(0, $DB->count_records('data_records', array('id' => $entry11)));
    }

    /**
     * Test delete_entry as student in read only mode period. Check I cannot delete my own entries in that period.
     */
    public function test_delete_entry_as_student_in_read_only_period(): void {
        global $DB;
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
        // Set a time period.
        $this->database->timeviewfrom = time() - HOURSECS;
        $this->database->timeviewto = time() + HOURSECS;
        $DB->update_record('data', $this->database);

        $this->setUser($this->student1);
        $this->expectException('moodle_exception');
        mod_data_external::delete_entry($entry11);
    }

    /**
     * Test delete_entry with an user missing permissions.
     */
    public function test_delete_entry_missing_permissions(): void {
        global $DB;
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();

        $this->setUser($this->student1);
        $this->expectException('moodle_exception');
        mod_data_external::delete_entry($entry21);
    }

    /**
     * Test add_entry.
     */
    public function test_add_entry(): void {
        global $DB;
        // First create the record structure and add some entries.
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();

        $this->setUser($this->student1);
        $newentrydata = [];
        $fields = $DB->get_records('data_fields', array('dataid' => $this->database->id), 'id');
        // Prepare the new entry data.
        foreach ($fields as $field) {
            $subfield = $value = '';

            switch ($field->type) {
                case 'checkbox':
                    $value = ['opt1', 'opt2'];
                    break;
                case 'date':
                    // Add two extra.
                    $newentrydata[] = [
                        'fieldid' => $field->id,
                        'subfield' => 'day',
                        'value' => json_encode('5')
                    ];
                    $newentrydata[] = [
                        'fieldid' => $field->id,
                        'subfield' => 'month',
                        'value' => json_encode('1')
                    ];
                    $subfield = 'year';
                    $value = '1981';
                    break;
                case 'menu':
                    $value = 'menu1';
                    break;
                case 'multimenu':
                    $value = ['multimenu1', 'multimenu4'];
                    break;
                case 'number':
                    $value = 6;
                    break;
                case 'radiobutton':
                    $value = 'radioopt1';
                    break;
                case 'text':
                    $value = 'some text';
                    break;
                case 'textarea':
                    $newentrydata[] = [
                        'fieldid' => $field->id,
                        'subfield' => 'content1',
                        'value' => json_encode(FORMAT_MOODLE)
                    ];
                    $newentrydata[] = [
                        'fieldid' => $field->id,
                        'subfield' => 'itemid',
                        'value' => json_encode(0)
                    ];
                    $value = 'more text';
                    break;
                case 'url':
                    $value = 'https://moodle.org';
                    $subfield = 0;
                    break;
            }

            $newentrydata[] = [
                'fieldid' => $field->id,
                'subfield' => $subfield,
                'value' => json_encode($value)
            ];
        }
        $result = mod_data_external::add_entry($this->database->id, 0, $newentrydata);
        $result = external_api::clean_returnvalue(mod_data_external::add_entry_returns(), $result);

        $newentryid = $result['newentryid'];
        $result = mod_data_external::get_entry($newentryid, true);
        $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
        $this->assertEquals($this->student1->id, $result['entry']['userid']);
        $this->assertCount(9, $result['entry']['contents']);
        foreach ($result['entry']['contents'] as $content) {
            $field = $fields[$content['fieldid']];
            // Stored content same that the one retrieved by WS.
            $dbcontent = $DB->get_record('data_content', array('fieldid' => $field->id, 'recordid' => $newentryid));
            $this->assertEquals($dbcontent->content, $content['content']);

            // Now double check everything stored is correct.
            if ($field->type == 'checkbox') {
                $this->assertEquals('opt1##opt2', $content['content']);
                continue;
            }
            if ($field->type == 'date') {
                $this->assertEquals(347500800, $content['content']); // Date in gregorian format.
                continue;
            }
            if ($field->type == 'menu') {
                $this->assertEquals('menu1', $content['content']);
                continue;
            }
            if ($field->type == 'multimenu') {
                $this->assertEquals('multimenu1##multimenu4', $content['content']);
                continue;
            }
            if ($field->type == 'number') {
                $this->assertEquals(6, $content['content']);
                continue;
            }
            if ($field->type == 'radiobutton') {
                $this->assertEquals('radioopt1', $content['content']);
                continue;
            }
            if ($field->type == 'text') {
                $this->assertEquals('some text', $content['content']);
                continue;
            }
            if ($field->type == 'textarea') {
                $this->assertEquals('more text', $content['content']);
                $this->assertEquals(FORMAT_MOODLE, $content['content1']);
                continue;
            }
            if ($field->type == 'url') {
                $this->assertEquals('https://moodle.org', $content['content']);
                continue;
            }
            $this->assertEquals('multimenu1##multimenu4', $content['content']);
        }

        // Now, try to add another entry but removing some required data.
        unset($newentrydata[0]);
        $result = mod_data_external::add_entry($this->database->id, 0, $newentrydata);
        $result = external_api::clean_returnvalue(mod_data_external::add_entry_returns(), $result);
        $this->assertEquals(0, $result['newentryid']);
        $this->assertCount(0, $result['generalnotifications']);
        $this->assertCount(1, $result['fieldnotifications']);
        $this->assertEquals('field-1', $result['fieldnotifications'][0]['fieldname']);
        $this->assertEquals(get_string('errormustsupplyvalue', 'data'), $result['fieldnotifications'][0]['notification']);
    }

    /**
     * Test add_entry empty_form.
     */
    public function test_add_entry_empty_form(): void {

        // Add a field to database to let users add new entries.
        $this->add_test_field();

        $result = mod_data_external::add_entry($this->database->id, 0, []);
        $result = external_api::clean_returnvalue(mod_data_external::add_entry_returns(), $result);
        $this->assertEquals(0, $result['newentryid']);
        $this->assertCount(1, $result['generalnotifications']);
        $this->assertCount(0, $result['fieldnotifications']);
        $this->assertEquals(get_string('emptyaddform', 'data'), $result['generalnotifications'][0]);
    }

    /**
     * Test add_entry read_only_period.
     */
    public function test_add_entry_read_only_period(): void {
        global $DB;
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
        // Set a time period.
        $this->database->timeviewfrom = time() - HOURSECS;
        $this->database->timeviewto = time() + HOURSECS;
        $DB->update_record('data', $this->database);

        $this->setUser($this->student1);
        $this->expectExceptionMessage(get_string('noaccess', 'data'));
        $this->expectException('moodle_exception');
        mod_data_external::add_entry($this->database->id, 0, []);
    }

    /**
     * Test add_entry max_num_entries.
     */
    public function test_add_entry_max_num_entries(): void {
        global $DB;
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
        // Set a time period.
        $this->database->maxentries = 1;
        $DB->update_record('data', $this->database);

        $this->setUser($this->student1);
        $this->expectExceptionMessage(get_string('noaccess', 'data'));
        $this->expectException('moodle_exception');
        mod_data_external::add_entry($this->database->id, 0, []);
    }

    /**
     * Test add_entry invalid group.
     */
    public function test_add_entry_invalid_group(): void {

        // Add a field to database to let users add new entries.
        $this->add_test_field();

        $this->setUser($this->student1);
        $this->expectExceptionMessage(get_string('noaccess', 'data'));
        $this->expectException('moodle_exception');
        mod_data_external::add_entry($this->database->id, $this->group2->id, []);
    }

    /**
     * Test add_entry for an empty database (no fields).
     *
     * @covers ::add_entry
     */
    public function test_add_entry_empty_database(): void {
        $this->expectException('moodle_exception');
        mod_data_external::add_entry($this->database->id, 0, []);
    }

    /**
     * Test update_entry.
     */
    public function test_update_entry(): void {
        global $DB;
        // First create the record structure and add some entries.
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();

        $this->setUser($this->student1);
        $newentrydata = [];
        $fields = $DB->get_records('data_fields', array('dataid' => $this->database->id), 'id');
        // Prepare the new entry data.
        foreach ($fields as $field) {
            $subfield = $value = '';

            switch ($field->type) {
                case 'checkbox':
                    $value = ['opt1', 'opt2'];
                    break;
                case 'date':
                    // Add two extra.
                    $newentrydata[] = [
                        'fieldid' => $field->id,
                        'subfield' => 'day',
                        'value' => json_encode('5')
                    ];
                    $newentrydata[] = [
                        'fieldid' => $field->id,
                        'subfield' => 'month',
                        'value' => json_encode('1')
                    ];
                    $subfield = 'year';
                    $value = '1981';
                    break;
                case 'menu':
                    $value = 'menu1';
                    break;
                case 'multimenu':
                    $value = ['multimenu1', 'multimenu4'];
                    break;
                case 'number':
                    $value = 6;
                    break;
                case 'radiobutton':
                    $value = 'radioopt2';
                    break;
                case 'text':
                    $value = 'some text';
                    break;
                case 'textarea':
                    $newentrydata[] = [
                        'fieldid' => $field->id,
                        'subfield' => 'content1',
                        'value' => json_encode(FORMAT_MOODLE)
                    ];
                    $newentrydata[] = [
                        'fieldid' => $field->id,
                        'subfield' => 'itemid',
                        'value' => json_encode(0)
                    ];
                    $value = 'more text';
                    break;
                case 'url':
                    $value = 'https://moodle.org';
                    $subfield = 0;
                    break;
            }

            $newentrydata[] = [
                'fieldid' => $field->id,
                'subfield' => $subfield,
                'value' => json_encode($value)
            ];
        }
        $result = mod_data_external::update_entry($entry11, $newentrydata);
        $result = external_api::clean_returnvalue(mod_data_external::update_entry_returns(), $result);
        $this->assertTrue($result['updated']);
        $this->assertCount(0, $result['generalnotifications']);
        $this->assertCount(0, $result['fieldnotifications']);

        $result = mod_data_external::get_entry($entry11, true);
        $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
        $this->assertEquals($this->student1->id, $result['entry']['userid']);
        $this->assertCount(9, $result['entry']['contents']);
        foreach ($result['entry']['contents'] as $content) {
            $field = $fields[$content['fieldid']];
            // Stored content same that the one retrieved by WS.
            $dbcontent = $DB->get_record('data_content', array('fieldid' => $field->id, 'recordid' => $entry11));
            $this->assertEquals($dbcontent->content, $content['content']);

            // Now double check everything stored is correct.
            if ($field->type == 'checkbox') {
                $this->assertEquals('opt1##opt2', $content['content']);
                continue;
            }
            if ($field->type == 'date') {
                $this->assertEquals(347500800, $content['content']); // Date in gregorian format.
                continue;
            }
            if ($field->type == 'menu') {
                $this->assertEquals('menu1', $content['content']);
                continue;
            }
            if ($field->type == 'multimenu') {
                $this->assertEquals('multimenu1##multimenu4', $content['content']);
                continue;
            }
            if ($field->type == 'number') {
                $this->assertEquals(6, $content['content']);
                continue;
            }
            if ($field->type == 'radiobutton') {
                $this->assertEquals('radioopt2', $content['content']);
                continue;
            }
            if ($field->type == 'text') {
                $this->assertEquals('some text', $content['content']);
                continue;
            }
            if ($field->type == 'textarea') {
                $this->assertEquals('more text', $content['content']);
                $this->assertEquals(FORMAT_MOODLE, $content['content1']);
                continue;
            }
            if ($field->type == 'url') {
                $this->assertEquals('https://moodle.org', $content['content']);
                continue;
            }
            $this->assertEquals('multimenu1##multimenu4', $content['content']);
        }

        // Now, try to update the entry but removing some required data.
        unset($newentrydata[0]);
        $result = mod_data_external::update_entry($entry11, $newentrydata);
        $result = external_api::clean_returnvalue(mod_data_external::update_entry_returns(), $result);
        $this->assertFalse($result['updated']);
        $this->assertCount(0, $result['generalnotifications']);
        $this->assertCount(1, $result['fieldnotifications']);
        $this->assertEquals('field-1', $result['fieldnotifications'][0]['fieldname']);
        $this->assertEquals(get_string('errormustsupplyvalue', 'data'), $result['fieldnotifications'][0]['notification']);
    }

    /**
     * Test update_entry sending empty data.
     */
    public function test_update_entry_empty_data(): void {
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();

        $this->setUser($this->student1);
        $result = mod_data_external::update_entry($entry11, []);
        $result = external_api::clean_returnvalue(mod_data_external::update_entry_returns(), $result);
        $this->assertFalse($result['updated']);
        $this->assertCount(1, $result['generalnotifications']);
        $this->assertCount(9, $result['fieldnotifications']);
        $this->assertEquals(get_string('emptyaddform', 'data'), $result['generalnotifications'][0]);
    }

    /**
     * Test update_entry in read only period.
     */
    public function test_update_entry_read_only_period(): void {
        global $DB;
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
        // Set a time period.
        $this->database->timeviewfrom = time() - HOURSECS;
        $this->database->timeviewto = time() + HOURSECS;
        $DB->update_record('data', $this->database);

        $this->setUser($this->student1);
        $this->expectExceptionMessage(get_string('noaccess', 'data'));
        $this->expectException('moodle_exception');
        mod_data_external::update_entry($entry11, []);
    }

    /**
     * Test update_entry other_user.
     */
    public function test_update_entry_other_user(): void {
        // Try to update other user entry.
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();
        $this->setUser($this->student2);
        $this->expectExceptionMessage(get_string('noaccess', 'data'));
        $this->expectException('moodle_exception');
        mod_data_external::update_entry($entry11, []);
    }

    /**
     * Test get_entry_rating_information.
     */
    public function test_get_entry_rating_information(): void {
        global $DB, $CFG;
        require_once($CFG->dirroot . '/rating/lib.php');

        $DB->set_field('data', 'assessed', RATING_AGGREGATE_SUM, array('id' => $this->database->id));
        $DB->set_field('data', 'scale', 100, array('id' => $this->database->id));
        list($entry11, $entry12, $entry13, $entry14, $entry21) = self::populate_database_with_entries();

        $user1 = self::getDataGenerator()->create_user();
        $user2 = self::getDataGenerator()->create_user();
        $this->getDataGenerator()->enrol_user($user1->id, $this->course->id, $this->studentrole->id, 'manual');
        $this->getDataGenerator()->enrol_user($user2->id, $this->course->id, $this->studentrole->id, 'manual');

        // Rate the entry as user1.
        $rating1 = new \stdClass();
        $rating1->contextid = $this->context->id;
        $rating1->component = 'mod_data';
        $rating1->ratingarea = 'entry';
        $rating1->itemid = $entry11;
        $rating1->rating = 50;
        $rating1->scaleid = 100;
        $rating1->userid = $user1->id;
        $rating1->timecreated = time();
        $rating1->timemodified = time();
        $rating1->id = $DB->insert_record('rating', $rating1);

        // Rate the entry as user2.
        $rating2 = new \stdClass();
        $rating2->contextid = $this->context->id;
        $rating2->component = 'mod_data';
        $rating2->ratingarea = 'entry';
        $rating2->itemid = $entry11;
        $rating2->rating = 100;
        $rating2->scaleid = 100;
        $rating2->userid = $user2->id;
        $rating2->timecreated = time() + 1;
        $rating2->timemodified = time() + 1;
        $rating2->id = $DB->insert_record('rating', $rating2);

        // As student, retrieve ratings information.
        $this->setUser($this->student2);
        $result = mod_data_external::get_entry($entry11);
        $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
        $this->assertCount(1, $result['ratinginfo']['ratings']);
        $this->assertFalse($result['ratinginfo']['ratings'][0]['canviewaggregate']);
        $this->assertFalse($result['ratinginfo']['canviewall']);
        $this->assertFalse($result['ratinginfo']['ratings'][0]['canrate']);
        $this->assertTrue(!isset($result['ratinginfo']['ratings'][0]['count']));

        // Now, as teacher, I should see the info correctly.
        $this->setUser($this->teacher);
        $result = mod_data_external::get_entry($entry11);
        $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
        $this->assertCount(1, $result['ratinginfo']['ratings']);
        $this->assertTrue($result['ratinginfo']['ratings'][0]['canviewaggregate']);
        $this->assertTrue($result['ratinginfo']['canviewall']);
        $this->assertTrue($result['ratinginfo']['ratings'][0]['canrate']);
        $this->assertEquals(2, $result['ratinginfo']['ratings'][0]['count']);
        $this->assertEquals(100, $result['ratinginfo']['ratings'][0]['aggregate']); // Expect maximium scale value.
    }
}

Filemanager

Name Type Size Permission Actions
backup Folder 0777
behat Folder 0777
event Folder 0777
external Folder 0777
fixtures Folder 0777
generator Folder 0777
privacy Folder 0777
search Folder 0777
custom_completion_test.php File 7.05 KB 0777
dates_test.php File 4.24 KB 0777
entries_export_test.php File 11.32 KB 0777
entries_exporter_test.php File 8.73 KB 0777
entries_import_test.php File 19 KB 0777
entries_importer_test.php File 7.92 KB 0777
externallib_test.php File 65.42 KB 0777
generator_test.php File 14.09 KB 0777
lib_test.php File 87.41 KB 0777
locallib_test.php File 9.47 KB 0777
manager_test.php File 29.78 KB 0777
preset_importer_test.php File 18.9 KB 0777
preset_test.php File 39.38 KB 0777
template_test.php File 42.3 KB 0777
Filemanager