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