Proyectos de Subversion Moodle

Rev

Rev 11 | | Comparar con el anterior | Ultima modificación | Ver Log |

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