Proyectos de Subversion Moodle

Rev

Rev 1 | | 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
/**
18
 * Unit tests for lib.php
19
 *
20
 * @package    mod_data
21
 * @category   phpunit
22
 * @copyright  2013 Adrian Greeve
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
namespace mod_data;
26
 
27
use stdClass;
28
 
29
defined('MOODLE_INTERNAL') || die();
30
 
31
global $CFG;
32
require_once($CFG->dirroot . '/mod/data/lib.php');
33
 
34
/**
35
 * Unit tests for lib.php
36
 *
37
 * @package    mod_data
38
 * @copyright  2013 Adrian Greeve
39
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40
 */
41
class lib_test extends \advanced_testcase {
42
 
43
    /**
44
     * @var moodle_database
45
     */
46
    protected $DB = null;
47
 
48
    /**
49
     * Tear Down to reset DB.
50
     */
51
    public function tearDown(): void {
52
        global $DB;
53
 
54
        if (isset($this->DB)) {
55
            $DB = $this->DB;
56
            $this->DB = null;
57
        }
58
    }
59
 
60
    /**
61
     * Confirms that completionentries is working
62
     * Sets it to 1, confirms that
63
     * it is not complete. Inserts a record and
64
     * confirms that it is complete.
65
     */
11 efrain 66
    public function test_data_completion(): void {
1 efrain 67
        global $DB, $CFG;
68
        $this->resetAfterTest();
69
        $this->setAdminUser();
70
        $CFG->enablecompletion = 1;
71
        $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
72
        $record = new \stdClass();
73
        $record->course = $course->id;
74
        $record->name = "Mod data completion test";
75
        $record->intro = "Some intro of some sort";
76
        $record->completionentries = "1";
77
        /* completion=2 means Show activity commplete when condition is met and completionentries means 1 record is
78
         * required for the activity to be considered complete
79
         */
80
        $module = $this->getDataGenerator()->create_module('data', $record, array('completion' => 2, 'completionentries' => 1));
81
 
82
        $cm = get_coursemodule_from_instance('data', $module->id, $course->id);
83
        $completion = new \completion_info($course);
84
        $completiondata = $completion->get_data($cm, true, 0);
85
        /* Confirm it is not complete as there are no entries */
86
        $this->assertNotEquals(1, $completiondata->completionstate);
87
 
88
        $field = data_get_field_new('text', $module);
89
        $fielddetail = new \stdClass();
90
        $fielddetail->d = $module->id;
91
        $fielddetail->mode = 'add';
92
        $fielddetail->type = 'text';
93
        $fielddetail->sesskey = sesskey();
94
        $fielddetail->name = 'Name';
95
        $fielddetail->description = 'Some name';
96
 
97
        $field->define_field($fielddetail);
98
        $field->insert_field();
99
        $recordid = data_add_record($module);
100
 
101
        $datacontent = array();
102
        $datacontent['fieldid'] = $field->field->id;
103
        $datacontent['recordid'] = $recordid;
104
        $datacontent['content'] = 'Asterix';
105
        $contentid = $DB->insert_record('data_content', $datacontent);
106
 
107
        $cm = get_coursemodule_from_instance('data', $module->id, $course->id);
108
        $completion = new \completion_info($course);
109
        $completiondata = $completion->get_data($cm);
110
        /* Confirm it is complete because it has 1 entry */
111
        $this->assertEquals(1, $completiondata->completionstate);
112
    }
113
 
11 efrain 114
    public function test_data_delete_record(): void {
1 efrain 115
        global $DB;
116
 
117
        $this->resetAfterTest();
118
 
119
        // Create a record for deleting.
120
        $this->setAdminUser();
121
        $course = $this->getDataGenerator()->create_course();
122
        $record = new \stdClass();
123
        $record->course = $course->id;
124
        $record->name = "Mod data delete test";
125
        $record->intro = "Some intro of some sort";
126
 
127
        $module = $this->getDataGenerator()->create_module('data', $record);
128
 
129
        $field = data_get_field_new('text', $module);
130
 
131
        $fielddetail = new \stdClass();
132
        $fielddetail->d = $module->id;
133
        $fielddetail->mode = 'add';
134
        $fielddetail->type = 'text';
135
        $fielddetail->sesskey = sesskey();
136
        $fielddetail->name = 'Name';
137
        $fielddetail->description = 'Some name';
138
 
139
        $field->define_field($fielddetail);
140
        $field->insert_field();
141
        $recordid = data_add_record($module);
142
 
143
        $datacontent = array();
144
        $datacontent['fieldid'] = $field->field->id;
145
        $datacontent['recordid'] = $recordid;
146
        $datacontent['content'] = 'Asterix';
147
 
148
        $contentid = $DB->insert_record('data_content', $datacontent);
149
        $cm = get_coursemodule_from_instance('data', $module->id, $course->id);
150
 
151
        // Check to make sure that we have a database record.
152
        $data = $DB->get_records('data', array('id' => $module->id));
153
        $this->assertEquals(1, count($data));
154
 
155
        $datacontent = $DB->get_records('data_content', array('id' => $contentid));
156
        $this->assertEquals(1, count($datacontent));
157
 
158
        $datafields = $DB->get_records('data_fields', array('id' => $field->field->id));
159
        $this->assertEquals(1, count($datafields));
160
 
161
        $datarecords = $DB->get_records('data_records', array('id' => $recordid));
162
        $this->assertEquals(1, count($datarecords));
163
 
164
        // Test to see if a failed delete returns false.
165
        $result = data_delete_record(8798, $module, $course->id, $cm->id);
166
        $this->assertFalse($result);
167
 
168
        // Delete the record.
169
        $result = data_delete_record($recordid, $module, $course->id, $cm->id);
170
 
171
        // Check that all of the record is gone.
172
        $datacontent = $DB->get_records('data_content', array('id' => $contentid));
173
        $this->assertEquals(0, count($datacontent));
174
 
175
        $datarecords = $DB->get_records('data_records', array('id' => $recordid));
176
        $this->assertEquals(0, count($datarecords));
177
 
178
        // Make sure the function returns true on a successful deletion.
179
        $this->assertTrue($result);
180
    }
181
 
182
    /**
183
     * Test comment_created event.
184
     */
11 efrain 185
    public function test_data_comment_created_event(): void {
1 efrain 186
        global $CFG, $DB;
187
        require_once($CFG->dirroot . '/comment/lib.php');
188
 
189
        $this->resetAfterTest();
190
 
191
        // Create a record for deleting.
192
        $this->setAdminUser();
193
        $course = $this->getDataGenerator()->create_course();
194
        $record = new \stdClass();
195
        $record->course = $course->id;
196
        $record->name = "Mod data delete test";
197
        $record->intro = "Some intro of some sort";
198
        $record->comments = 1;
199
 
200
        $module = $this->getDataGenerator()->create_module('data', $record);
201
        $field = data_get_field_new('text', $module);
202
 
203
        $fielddetail = new \stdClass();
204
        $fielddetail->name = 'Name';
205
        $fielddetail->description = 'Some name';
206
 
207
        $field->define_field($fielddetail);
208
        $field->insert_field();
209
        $recordid = data_add_record($module);
210
 
211
        $datacontent = array();
212
        $datacontent['fieldid'] = $field->field->id;
213
        $datacontent['recordid'] = $recordid;
214
        $datacontent['content'] = 'Asterix';
215
 
216
        $contentid = $DB->insert_record('data_content', $datacontent);
217
        $cm = get_coursemodule_from_instance('data', $module->id, $course->id);
218
 
219
        $context = \context_module::instance($module->cmid);
220
        $cmt = new \stdClass();
221
        $cmt->context = $context;
222
        $cmt->course = $course;
223
        $cmt->cm = $cm;
224
        $cmt->area = 'database_entry';
225
        $cmt->itemid = $recordid;
226
        $cmt->showcount = true;
227
        $cmt->component = 'mod_data';
228
        $comment = new \comment($cmt);
229
 
230
        // Triggering and capturing the event.
231
        $sink = $this->redirectEvents();
232
        $comment->add('New comment');
233
        $events = $sink->get_events();
234
        $this->assertCount(1, $events);
235
        $event = reset($events);
236
 
237
        // Checking that the event contains the expected values.
238
        $this->assertInstanceOf('\mod_data\event\comment_created', $event);
239
        $this->assertEquals($context, $event->get_context());
240
        $url = new \moodle_url('/mod/data/view.php', array('id' => $cm->id));
241
        $this->assertEquals($url, $event->get_url());
242
        $this->assertEventContextNotUsed($event);
243
    }
244
 
245
    /**
246
     * Test comment_deleted event.
247
     */
11 efrain 248
    public function test_data_comment_deleted_event(): void {
1 efrain 249
        global $CFG, $DB;
250
        require_once($CFG->dirroot . '/comment/lib.php');
251
 
252
        $this->resetAfterTest();
253
 
254
        // Create a record for deleting.
255
        $this->setAdminUser();
256
        $course = $this->getDataGenerator()->create_course();
257
        $record = new \stdClass();
258
        $record->course = $course->id;
259
        $record->name = "Mod data delete test";
260
        $record->intro = "Some intro of some sort";
261
        $record->comments = 1;
262
 
263
        $module = $this->getDataGenerator()->create_module('data', $record);
264
        $field = data_get_field_new('text', $module);
265
 
266
        $fielddetail = new \stdClass();
267
        $fielddetail->name = 'Name';
268
        $fielddetail->description = 'Some name';
269
 
270
        $field->define_field($fielddetail);
271
        $field->insert_field();
272
        $recordid = data_add_record($module);
273
 
274
        $datacontent = array();
275
        $datacontent['fieldid'] = $field->field->id;
276
        $datacontent['recordid'] = $recordid;
277
        $datacontent['content'] = 'Asterix';
278
 
279
        $contentid = $DB->insert_record('data_content', $datacontent);
280
        $cm = get_coursemodule_from_instance('data', $module->id, $course->id);
281
 
282
        $context = \context_module::instance($module->cmid);
283
        $cmt = new \stdClass();
284
        $cmt->context = $context;
285
        $cmt->course = $course;
286
        $cmt->cm = $cm;
287
        $cmt->area = 'database_entry';
288
        $cmt->itemid = $recordid;
289
        $cmt->showcount = true;
290
        $cmt->component = 'mod_data';
291
        $comment = new \comment($cmt);
292
        $newcomment = $comment->add('New comment 1');
293
 
294
        // Triggering and capturing the event.
295
        $sink = $this->redirectEvents();
296
        $comment->delete($newcomment->id);
297
        $events = $sink->get_events();
298
        $this->assertCount(1, $events);
299
        $event = reset($events);
300
 
301
        // Checking that the event contains the expected values.
302
        $this->assertInstanceOf('\mod_data\event\comment_deleted', $event);
303
        $this->assertEquals($context, $event->get_context());
304
        $url = new \moodle_url('/mod/data/view.php', array('id' => $module->cmid));
305
        $this->assertEquals($url, $event->get_url());
306
        $this->assertEventContextNotUsed($event);
307
    }
308
 
309
    /**
310
     * Checks that data_user_can_manage_entry will return true if the user
311
     * has the mod/data:manageentries capability.
312
     */
11 efrain 313
    public function test_data_user_can_manage_entry_return_true_with_capability(): void {
1 efrain 314
 
315
        $this->resetAfterTest();
316
        $testdata = $this->create_user_test_data();
317
 
318
        $user = $testdata['user'];
319
        $course = $testdata['course'];
320
        $roleid = $testdata['roleid'];
321
        $context = $testdata['context'];
322
        $record = $testdata['record'];
323
        $data = new \stdClass();
324
 
325
        $this->setUser($user);
326
 
327
        assign_capability('mod/data:manageentries', CAP_ALLOW, $roleid, $context);
328
 
329
        $this->assertTrue(data_user_can_manage_entry($record, $data, $context),
330
            'data_user_can_manage_entry() returns true if the user has mod/data:manageentries capability');
331
    }
332
 
333
    /**
334
     * Checks that data_user_can_manage_entry will return false if the data
335
     * is set to readonly.
336
     */
11 efrain 337
    public function test_data_user_can_manage_entry_return_false_readonly(): void {
1 efrain 338
 
339
        $this->resetAfterTest();
340
        $testdata = $this->create_user_test_data();
341
 
342
        $user = $testdata['user'];
343
        $course = $testdata['course'];
344
        $roleid = $testdata['roleid'];
345
        $context = $testdata['context'];
346
        $record = $testdata['record'];
347
 
348
        $this->setUser($user);
349
 
350
        // Need to make sure they don't have this capability in order to fall back to
351
        // the other checks.
352
        assign_capability('mod/data:manageentries', CAP_PROHIBIT, $roleid, $context);
353
 
354
        // Causes readonly mode to be enabled.
355
        $data = new \stdClass();
356
        $now = time();
357
        // Add a small margin around the periods to prevent errors with slow tests.
358
        $data->timeviewfrom = $now - 1;
359
        $data->timeviewto = $now + 5;
360
 
361
        $this->assertFalse(data_user_can_manage_entry($record, $data, $context),
362
            'data_user_can_manage_entry() returns false if the data is read only');
363
    }
364
 
365
    /**
366
     * Checks that data_user_can_manage_entry will return false if the record
367
     * can't be found in the database.
368
     */
11 efrain 369
    public function test_data_user_can_manage_entry_return_false_no_record(): void {
1 efrain 370
 
371
        $this->resetAfterTest();
372
        $testdata = $this->create_user_test_data();
373
 
374
        $user = $testdata['user'];
375
        $course = $testdata['course'];
376
        $roleid = $testdata['roleid'];
377
        $context = $testdata['context'];
378
        $record = $testdata['record'];
379
        $data = new \stdClass();
380
        // Causes readonly mode to be disabled.
381
        $now = time();
382
        $data->timeviewfrom = $now + 100;
383
        $data->timeviewto = $now - 100;
384
 
385
        $this->setUser($user);
386
 
387
        // Need to make sure they don't have this capability in order to fall back to
388
        // the other checks.
389
        assign_capability('mod/data:manageentries', CAP_PROHIBIT, $roleid, $context);
390
 
391
        // Pass record id instead of object to force DB lookup.
392
        $this->assertFalse(data_user_can_manage_entry(1, $data, $context),
393
            'data_user_can_manage_entry() returns false if the record cannot be found');
394
    }
395
 
396
    /**
397
     * Checks that data_user_can_manage_entry will return false if the record
398
     * isn't owned by the user.
399
     */
11 efrain 400
    public function test_data_user_can_manage_entry_return_false_not_owned_record(): void {
1 efrain 401
 
402
        $this->resetAfterTest();
403
        $testdata = $this->create_user_test_data();
404
 
405
        $user = $testdata['user'];
406
        $course = $testdata['course'];
407
        $roleid = $testdata['roleid'];
408
        $context = $testdata['context'];
409
        $record = $testdata['record'];
410
        $data = new \stdClass();
411
        // Causes readonly mode to be disabled.
412
        $now = time();
413
        $data->timeviewfrom = $now + 100;
414
        $data->timeviewto = $now - 100;
415
        // Make sure the record isn't owned by this user.
416
        $record->userid = $user->id + 1;
417
 
418
        $this->setUser($user);
419
 
420
        // Need to make sure they don't have this capability in order to fall back to
421
        // the other checks.
422
        assign_capability('mod/data:manageentries', CAP_PROHIBIT, $roleid, $context);
423
 
424
        $this->assertFalse(data_user_can_manage_entry($record, $data, $context),
425
            'data_user_can_manage_entry() returns false if the record isnt owned by the user');
426
    }
427
 
428
    /**
429
     * Checks that data_user_can_manage_entry will return true if the data
430
     * doesn't require approval.
431
     */
11 efrain 432
    public function test_data_user_can_manage_entry_return_true_data_no_approval(): void {
1 efrain 433
 
434
        $this->resetAfterTest();
435
        $testdata = $this->create_user_test_data();
436
 
437
        $user = $testdata['user'];
438
        $course = $testdata['course'];
439
        $roleid = $testdata['roleid'];
440
        $context = $testdata['context'];
441
        $record = $testdata['record'];
442
        $data = new \stdClass();
443
        // Causes readonly mode to be disabled.
444
        $now = time();
445
        $data->timeviewfrom = $now + 100;
446
        $data->timeviewto = $now - 100;
447
        // The record doesn't need approval.
448
        $data->approval = false;
449
        // Make sure the record is owned by this user.
450
        $record->userid = $user->id;
451
 
452
        $this->setUser($user);
453
 
454
        // Need to make sure they don't have this capability in order to fall back to
455
        // the other checks.
456
        assign_capability('mod/data:manageentries', CAP_PROHIBIT, $roleid, $context);
457
 
458
        $this->assertTrue(data_user_can_manage_entry($record, $data, $context),
459
            'data_user_can_manage_entry() returns true if the record doesnt require approval');
460
    }
461
 
462
    /**
463
     * Checks that data_user_can_manage_entry will return true if the record
464
     * isn't yet approved.
465
     */
11 efrain 466
    public function test_data_user_can_manage_entry_return_true_record_unapproved(): void {
1 efrain 467
 
468
        $this->resetAfterTest();
469
        $testdata = $this->create_user_test_data();
470
 
471
        $user = $testdata['user'];
472
        $course = $testdata['course'];
473
        $roleid = $testdata['roleid'];
474
        $context = $testdata['context'];
475
        $record = $testdata['record'];
476
        $data = new \stdClass();
477
        // Causes readonly mode to be disabled.
478
        $now = time();
479
        $data->timeviewfrom = $now + 100;
480
        $data->timeviewto = $now - 100;
481
        // The record needs approval.
482
        $data->approval = true;
483
        // Make sure the record is owned by this user.
484
        $record->userid = $user->id;
485
        // The record hasn't yet been approved.
486
        $record->approved = false;
487
 
488
        $this->setUser($user);
489
 
490
        // Need to make sure they don't have this capability in order to fall back to
491
        // the other checks.
492
        assign_capability('mod/data:manageentries', CAP_PROHIBIT, $roleid, $context);
493
 
494
        $this->assertTrue(data_user_can_manage_entry($record, $data, $context),
495
            'data_user_can_manage_entry() returns true if the record is not yet approved');
496
    }
497
 
498
    /**
499
     * Checks that data_user_can_manage_entry will return the 'manageapproved'
500
     * value if the record has already been approved.
501
     */
11 efrain 502
    public function test_data_user_can_manage_entry_return_manageapproved(): void {
1 efrain 503
 
504
        $this->resetAfterTest();
505
        $testdata = $this->create_user_test_data();
506
 
507
        $user = $testdata['user'];
508
        $course = $testdata['course'];
509
        $roleid = $testdata['roleid'];
510
        $context = $testdata['context'];
511
        $record = $testdata['record'];
512
        $data = new \stdClass();
513
        // Causes readonly mode to be disabled.
514
        $now = time();
515
        $data->timeviewfrom = $now + 100;
516
        $data->timeviewto = $now - 100;
517
        // The record needs approval.
518
        $data->approval = true;
519
        // Can the user managed approved records?
520
        $data->manageapproved = false;
521
        // Make sure the record is owned by this user.
522
        $record->userid = $user->id;
523
        // The record has been approved.
524
        $record->approved = true;
525
 
526
        $this->setUser($user);
527
 
528
        // Need to make sure they don't have this capability in order to fall back to
529
        // the other checks.
530
        assign_capability('mod/data:manageentries', CAP_PROHIBIT, $roleid, $context);
531
 
532
        $canmanageentry = data_user_can_manage_entry($record, $data, $context);
533
 
534
        // Make sure the result of the check is what ever the manageapproved setting
535
        // is set to.
536
        $this->assertEquals($data->manageapproved, $canmanageentry,
537
            'data_user_can_manage_entry() returns the manageapproved setting on approved records');
538
    }
539
 
540
    /**
541
     * Helper method to create a set of test data for data_user_can_manage tests
542
     *
543
     * @return array contains user, course, roleid, module, context and record
544
     */
545
    private function create_user_test_data() {
546
        $user = $this->getDataGenerator()->create_user();
547
        $course = $this->getDataGenerator()->create_course();
548
        $roleid = $this->getDataGenerator()->create_role();
549
        $record = new \stdClass();
550
        $record->name = "test name";
551
        $record->intro = "test intro";
552
        $record->comments = 1;
553
        $record->course = $course->id;
554
        $record->userid = $user->id;
555
 
556
        $module = $this->getDataGenerator()->create_module('data', $record);
557
        $cm = get_coursemodule_from_instance('data', $module->id, $course->id);
558
        $context = \context_module::instance($module->cmid);
559
 
560
        $this->getDataGenerator()->role_assign($roleid, $user->id, $context->id);
561
 
562
        return array(
563
            'user' => $user,
564
            'course' => $course,
565
            'roleid' => $roleid,
566
            'module' => $module,
567
            'context' => $context,
568
            'record' => $record
569
        );
570
    }
571
 
572
    /**
573
     * Tests for mod_data_rating_can_see_item_ratings().
574
     *
575
     * @throws coding_exception
576
     * @throws rating_exception
577
     */
11 efrain 578
    public function test_mod_data_rating_can_see_item_ratings(): void {
1 efrain 579
        global $DB;
580
 
581
        $this->resetAfterTest();
582
 
583
        // Setup test data.
584
        $course = new \stdClass();
585
        $course->groupmode = SEPARATEGROUPS;
586
        $course->groupmodeforce = true;
587
        $course = $this->getDataGenerator()->create_course($course);
588
        $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id));
589
        $cm = get_coursemodule_from_instance('data', $data->id);
590
        $context = \context_module::instance($cm->id);
591
 
592
        // Create users.
593
        $user1 = $this->getDataGenerator()->create_user();
594
        $user2 = $this->getDataGenerator()->create_user();
595
        $user3 = $this->getDataGenerator()->create_user();
596
        $user4 = $this->getDataGenerator()->create_user();
597
 
598
        // Groups and stuff.
599
        $role = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST);
600
        $this->getDataGenerator()->enrol_user($user1->id, $course->id, $role->id);
601
        $this->getDataGenerator()->enrol_user($user2->id, $course->id, $role->id);
602
        $this->getDataGenerator()->enrol_user($user3->id, $course->id, $role->id);
603
        $this->getDataGenerator()->enrol_user($user4->id, $course->id, $role->id);
604
 
605
        $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
606
        $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
607
        groups_add_member($group1, $user1);
608
        groups_add_member($group1, $user2);
609
        groups_add_member($group2, $user3);
610
        groups_add_member($group2, $user4);
611
 
612
        // Add data.
613
        $field = data_get_field_new('text', $data);
614
 
615
        $fielddetail = new \stdClass();
616
        $fielddetail->name = 'Name';
617
        $fielddetail->description = 'Some name';
618
 
619
        $field->define_field($fielddetail);
620
        $field->insert_field();
621
 
622
        // Add a record with a group id of zero (all participants).
623
        $recordid1 = data_add_record($data, 0);
624
 
625
        $datacontent = array();
626
        $datacontent['fieldid'] = $field->field->id;
627
        $datacontent['recordid'] = $recordid1;
628
        $datacontent['content'] = 'Obelix';
629
        $DB->insert_record('data_content', $datacontent);
630
 
631
        $recordid = data_add_record($data, $group1->id);
632
 
633
        $datacontent = array();
634
        $datacontent['fieldid'] = $field->field->id;
635
        $datacontent['recordid'] = $recordid;
636
        $datacontent['content'] = 'Asterix';
637
        $DB->insert_record('data_content', $datacontent);
638
 
639
        // Now try to access it as various users.
640
        unassign_capability('moodle/site:accessallgroups', $role->id);
641
        // Eveyone should have access to the record with the group id of zero.
642
        $params1 = array('contextid' => 2,
643
                        'component' => 'mod_data',
644
                        'ratingarea' => 'entry',
645
                        'itemid' => $recordid1,
646
                        'scaleid' => 2);
647
 
648
        $params = array('contextid' => 2,
649
                        'component' => 'mod_data',
650
                        'ratingarea' => 'entry',
651
                        'itemid' => $recordid,
652
                        'scaleid' => 2);
653
 
654
        $this->setUser($user1);
655
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
656
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
657
        $this->setUser($user2);
658
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
659
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
660
        $this->setUser($user3);
661
        $this->assertFalse(mod_data_rating_can_see_item_ratings($params));
662
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
663
        $this->setUser($user4);
664
        $this->assertFalse(mod_data_rating_can_see_item_ratings($params));
665
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
666
 
667
        // Now try with accessallgroups cap and make sure everything is visible.
668
        assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $role->id, $context->id);
669
        $this->setUser($user1);
670
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
671
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
672
        $this->setUser($user2);
673
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
674
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
675
        $this->setUser($user3);
676
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
677
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
678
        $this->setUser($user4);
679
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
680
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
681
 
682
        // Change group mode and verify visibility.
683
        $course->groupmode = VISIBLEGROUPS;
684
        $DB->update_record('course', $course);
685
        unassign_capability('moodle/site:accessallgroups', $role->id);
686
        $this->setUser($user1);
687
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
688
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
689
        $this->setUser($user2);
690
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
691
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
692
        $this->setUser($user3);
693
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
694
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
695
        $this->setUser($user4);
696
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params));
697
        $this->assertTrue(mod_data_rating_can_see_item_ratings($params1));
698
 
699
    }
700
 
701
    /**
702
     * Tests for mod_data_refresh_events.
703
     */
11 efrain 704
    public function test_data_refresh_events(): void {
1 efrain 705
        global $DB;
706
        $this->resetAfterTest();
707
        $this->setAdminUser();
708
 
709
        $timeopen = time();
710
        $timeclose = time() + 86400;
711
 
712
        $course = $this->getDataGenerator()->create_course();
713
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
714
        $params['course'] = $course->id;
715
        $params['timeavailablefrom'] = $timeopen;
716
        $params['timeavailableto'] = $timeclose;
717
        $data = $generator->create_instance($params);
718
 
719
        // Normal case, with existing course.
720
        $this->assertTrue(data_refresh_events($course->id));
721
        $eventparams = array('modulename' => 'data', 'instance' => $data->id, 'eventtype' => 'open');
722
        $openevent = $DB->get_record('event', $eventparams, '*', MUST_EXIST);
723
        $this->assertEquals($openevent->timestart, $timeopen);
724
 
725
        $eventparams = array('modulename' => 'data', 'instance' => $data->id, 'eventtype' => 'close');
726
        $closeevent = $DB->get_record('event', $eventparams, '*', MUST_EXIST);
727
        $this->assertEquals($closeevent->timestart, $timeclose);
728
        // In case the course ID is passed as a numeric string.
729
        $this->assertTrue(data_refresh_events('' . $course->id));
730
        // Course ID not provided.
731
        $this->assertTrue(data_refresh_events());
732
        $eventparams = array('modulename' => 'data');
733
        $events = $DB->get_records('event', $eventparams);
734
        foreach ($events as $event) {
735
            if ($event->modulename === 'data' && $event->instance === $data->id && $event->eventtype === 'open') {
736
                $this->assertEquals($event->timestart, $timeopen);
737
            }
738
            if ($event->modulename === 'data' && $event->instance === $data->id && $event->eventtype === 'close') {
739
                $this->assertEquals($event->timestart, $timeclose);
740
            }
741
        }
742
    }
743
 
744
    /**
745
     * Data provider for tests of data_get_config.
746
     *
747
     * @return array
748
     */
749
    public function data_get_config_provider() {
750
        $initialdata = (object) [
751
            'template_foo' => true,
752
            'template_bar' => false,
753
            'template_baz' => null,
754
        ];
755
 
756
        $database = (object) [
757
            'config' => json_encode($initialdata),
758
        ];
759
 
760
        return [
761
            'Return full dataset (no key/default)' => [
762
                [$database],
763
                $initialdata,
764
            ],
765
            'Return full dataset (no default)' => [
766
                [$database, null],
767
                $initialdata,
768
            ],
769
            'Return full dataset' => [
770
                [$database, null, null],
771
                $initialdata,
772
            ],
773
            'Return requested key only, value true, no default' => [
774
                [$database, 'template_foo'],
775
                true,
776
            ],
777
            'Return requested key only, value false, no default' => [
778
                [$database, 'template_bar'],
779
                false,
780
            ],
781
            'Return requested key only, value null, no default' => [
782
                [$database, 'template_baz'],
783
                null,
784
            ],
785
            'Return unknown key, value null, no default' => [
786
                [$database, 'template_bum'],
787
                null,
788
            ],
789
            'Return requested key only, value true, default null' => [
790
                [$database, 'template_foo', null],
791
                true,
792
            ],
793
            'Return requested key only, value false, default null' => [
794
                [$database, 'template_bar', null],
795
                false,
796
            ],
797
            'Return requested key only, value null, default null' => [
798
                [$database, 'template_baz', null],
799
                null,
800
            ],
801
            'Return unknown key, value null, default null' => [
802
                [$database, 'template_bum', null],
803
                null,
804
            ],
805
            'Return requested key only, value true, default 42' => [
806
                [$database, 'template_foo', 42],
807
                true,
808
            ],
809
            'Return requested key only, value false, default 42' => [
810
                [$database, 'template_bar', 42],
811
                false,
812
            ],
813
            'Return requested key only, value null, default 42' => [
814
                [$database, 'template_baz', 42],
815
                null,
816
            ],
817
            'Return unknown key, value null, default 42' => [
818
                [$database, 'template_bum', 42],
819
                42,
820
            ],
821
        ];
822
    }
823
 
824
    /**
825
     * Tests for data_get_config.
826
     *
827
     * @dataProvider    data_get_config_provider
828
     * @param   array   $funcargs       The args to pass to data_get_config
829
     * @param   mixed   $expectation    The expected value
830
     */
11 efrain 831
    public function test_data_get_config($funcargs, $expectation): void {
1 efrain 832
        $this->assertEquals($expectation, call_user_func_array('data_get_config', $funcargs));
833
    }
834
 
835
    /**
836
     * Data provider for tests of data_set_config.
837
     *
838
     * @return array
839
     */
840
    public function data_set_config_provider() {
841
        $basevalue = (object) ['id' => rand(1, 1000)];
842
        $config = [
843
            'template_foo'  => true,
844
            'template_bar'  => false,
845
        ];
846
 
847
        $withvalues = clone $basevalue;
848
        $withvalues->config = json_encode((object) $config);
849
 
850
        return [
851
            'Empty config, New value' => [
852
                $basevalue,
853
                'etc',
854
                'newvalue',
855
                true,
856
                json_encode((object) ['etc' => 'newvalue'])
857
            ],
858
            'Has config, New value' => [
859
                clone $withvalues,
860
                'etc',
861
                'newvalue',
862
                true,
863
                json_encode((object) array_merge($config, ['etc' => 'newvalue']))
864
            ],
865
            'Has config, Update value, string' => [
866
                clone $withvalues,
867
                'template_foo',
868
                'newvalue',
869
                true,
870
                json_encode((object) array_merge($config, ['template_foo' => 'newvalue']))
871
            ],
872
            'Has config, Update value, true' => [
873
                clone $withvalues,
874
                'template_bar',
875
                true,
876
                true,
877
                json_encode((object) array_merge($config, ['template_bar' => true]))
878
            ],
879
            'Has config, Update value, false' => [
880
                clone $withvalues,
881
                'template_foo',
882
                false,
883
                true,
884
                json_encode((object) array_merge($config, ['template_foo' => false]))
885
            ],
886
            'Has config, Update value, null' => [
887
                clone $withvalues,
888
                'template_foo',
889
                null,
890
                true,
891
                json_encode((object) array_merge($config, ['template_foo' => null]))
892
            ],
893
            'Has config, No update, value true' => [
894
                clone $withvalues,
895
                'template_foo',
896
                true,
897
                false,
898
                $withvalues->config,
899
            ],
900
        ];
901
    }
902
 
903
    /**
904
     * Tests for data_set_config.
905
     *
906
     * @dataProvider    data_set_config_provider
907
     * @param   object  $database       The example row for the entry
908
     * @param   string  $key            The config key to set
909
     * @param   mixed   $value          The value of the key
910
     * @param   bool    $expectupdate   Whether we expected an update
911
     * @param   mixed   $newconfigvalue The expected value
912
     */
11 efrain 913
    public function test_data_set_config($database, $key, $value, $expectupdate, $newconfigvalue): void {
1 efrain 914
        global $DB;
915
 
916
        // Mock the database.
917
        // Note: Use the actual test class here rather than the abstract because are testing concrete methods.
918
        $this->DB = $DB;
919
        $DB = $this->getMockBuilder(get_class($DB))
920
            ->onlyMethods(['set_field'])
921
            ->getMock();
922
 
923
        $DB->expects($this->exactly((int) $expectupdate))
924
            ->method('set_field')
925
            ->with(
926
                'data',
927
                'config',
928
                $newconfigvalue,
929
                ['id' => $database->id]
930
            );
931
 
932
        // Perform the update.
933
        data_set_config($database, $key, $value);
934
 
935
        // Ensure that the value was updated by reference in $database.
936
        $config = json_decode($database->config);
937
        $this->assertEquals($value, $config->$key);
938
    }
939
 
11 efrain 940
    public function test_mod_data_get_tagged_records(): void {
1 efrain 941
        $this->resetAfterTest();
942
        $this->setAdminUser();
943
 
944
        // Setup test data.
945
        $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
946
        $course1 = $this->getDataGenerator()->create_course();
947
 
948
        $fieldrecord = new \stdClass();
949
        $fieldrecord->name = 'field-1';
950
        $fieldrecord->type = 'text';
951
 
952
        $data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id, 'approval' => true));
953
        $field1 = $datagenerator->create_field($fieldrecord, $data1);
954
 
955
        $datagenerator->create_entry($data1, [$field1->field->id => 'value11'], 0, ['Cats', 'Dogs']);
956
        $datagenerator->create_entry($data1, [$field1->field->id => 'value12'], 0, ['Cats', 'mice']);
957
        $datagenerator->create_entry($data1, [$field1->field->id => 'value13'], 0, ['Cats']);
958
        $datagenerator->create_entry($data1, [$field1->field->id => 'value14'], 0);
959
 
960
        $tag = \core_tag_tag::get_by_name(0, 'Cats');
961
 
962
        // Admin can see everything.
963
        $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
964
        $this->assertStringContainsString('value11', $res->content);
965
        $this->assertStringContainsString('value12', $res->content);
966
        $this->assertStringContainsString('value13', $res->content);
967
        $this->assertStringNotContainsString('value14', $res->content);
968
    }
969
 
11 efrain 970
    public function test_mod_data_get_tagged_records_approval(): void {
1 efrain 971
        global $DB;
972
 
973
        $this->resetAfterTest();
974
        $this->setAdminUser();
975
 
976
        // Setup test data.
977
        $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
978
        $course2 = $this->getDataGenerator()->create_course();
979
        $course1 = $this->getDataGenerator()->create_course();
980
 
981
        $fieldrecord = new \stdClass();
982
        $fieldrecord->name = 'field-1';
983
        $fieldrecord->type = 'text';
984
 
985
        $data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id));
986
        $field1 = $datagenerator->create_field($fieldrecord, $data1);
987
        $data2 = $this->getDataGenerator()->create_module('data', array('course' => $course2->id, 'approval' => true));
988
        $field2 = $datagenerator->create_field($fieldrecord, $data2);
989
 
990
        $record11 = $datagenerator->create_entry($data1, [$field1->field->id => 'value11'], 0, ['Cats', 'Dogs']);
991
        $record21 = $datagenerator->create_entry($data2, [$field2->field->id => 'value21'], 0, ['Cats'], ['approved' => false]);
992
        $tag = \core_tag_tag::get_by_name(0, 'Cats');
993
 
994
        // Admin can see everything.
995
        $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
996
        $this->assertStringContainsString('value11', $res->content);
997
        $this->assertStringContainsString('value21', $res->content);
998
        $this->assertEmpty($res->prevpageurl);
999
        $this->assertEmpty($res->nextpageurl);
1000
 
1001
        // Create and enrol a user.
1002
        $student = self::getDataGenerator()->create_user();
1003
        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1004
        $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
1005
        $this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual');
1006
        $this->setUser($student);
1007
 
1008
        // User can search data records inside a course.
1009
        \core_tag_index_builder::reset_caches();
1010
        $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1011
 
1012
        $this->assertStringContainsString('value11', $res->content);
1013
        $this->assertStringNotContainsString('value21', $res->content);
1014
 
1015
        $recordtoupdate = new \stdClass();
1016
        $recordtoupdate->id = $record21;
1017
        $recordtoupdate->approved = true;
1018
        $DB->update_record('data_records', $recordtoupdate);
1019
 
1020
        \core_tag_index_builder::reset_caches();
1021
        $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1022
 
1023
        $this->assertStringContainsString('value11', $res->content);
1024
        $this->assertStringContainsString('value21', $res->content);
1025
    }
1026
 
11 efrain 1027
    public function test_mod_data_get_tagged_records_time(): void {
1 efrain 1028
        global $DB;
1029
 
1030
        $this->resetAfterTest();
1031
        $this->setAdminUser();
1032
 
1033
        // Setup test data.
1034
        $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
1035
        $course2 = $this->getDataGenerator()->create_course();
1036
        $course1 = $this->getDataGenerator()->create_course();
1037
 
1038
        $fieldrecord = new \stdClass();
1039
        $fieldrecord->name = 'field-1';
1040
        $fieldrecord->type = 'text';
1041
 
1042
        $timefrom = time() - YEARSECS;
1043
        $timeto = time() - WEEKSECS;
1044
 
1045
        $data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id, 'approval' => true));
1046
        $field1 = $datagenerator->create_field($fieldrecord, $data1);
1047
        $data2 = $this->getDataGenerator()->create_module('data', array('course' => $course2->id,
1048
                                                                        'timeviewfrom' => $timefrom,
1049
                                                                        'timeviewto'   => $timeto));
1050
        $field2 = $datagenerator->create_field($fieldrecord, $data2);
1051
        $record11 = $datagenerator->create_entry($data1, [$field1->field->id => 'value11'], 0, ['Cats', 'Dogs']);
1052
        $record21 = $datagenerator->create_entry($data2, [$field2->field->id => 'value21'], 0, ['Cats']);
1053
        $tag = \core_tag_tag::get_by_name(0, 'Cats');
1054
 
1055
        // Admin can see everything.
1056
        $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1057
        $this->assertStringContainsString('value11', $res->content);
1058
        $this->assertStringContainsString('value21', $res->content);
1059
        $this->assertEmpty($res->prevpageurl);
1060
        $this->assertEmpty($res->nextpageurl);
1061
 
1062
        // Create and enrol a user.
1063
        $student = self::getDataGenerator()->create_user();
1064
        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1065
        $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
1066
        $this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual');
1067
        $this->setUser($student);
1068
 
1069
        // User can search data records inside a course.
1070
        \core_tag_index_builder::reset_caches();
1071
        $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1072
 
1073
        $this->assertStringContainsString('value11', $res->content);
1074
        $this->assertStringNotContainsString('value21', $res->content);
1075
 
1076
        $data2->timeviewto = time() + YEARSECS;
1077
        $DB->update_record('data', $data2);
1078
 
1079
        \core_tag_index_builder::reset_caches();
1080
        $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1081
 
1082
        $this->assertStringContainsString('value11', $res->content);
1083
        $this->assertStringContainsString('value21', $res->content);
1084
    }
1085
 
11 efrain 1086
    public function test_mod_data_get_tagged_records_course_enrolment(): void {
1 efrain 1087
        global $DB;
1088
 
1089
        $this->resetAfterTest();
1090
        $this->setAdminUser();
1091
 
1092
        // Setup test data.
1093
        $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
1094
        $course2 = $this->getDataGenerator()->create_course();
1095
        $course1 = $this->getDataGenerator()->create_course();
1096
 
1097
        $fieldrecord = new \stdClass();
1098
        $fieldrecord->name = 'field-1';
1099
        $fieldrecord->type = 'text';
1100
 
1101
        $data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id, 'approval' => true));
1102
        $field1 = $datagenerator->create_field($fieldrecord, $data1);
1103
        $data2 = $this->getDataGenerator()->create_module('data', array('course' => $course2->id));
1104
        $field2 = $datagenerator->create_field($fieldrecord, $data2);
1105
 
1106
        $record11 = $datagenerator->create_entry($data1, [$field1->field->id => 'value11'], 0, ['Cats', 'Dogs']);
1107
        $record21 = $datagenerator->create_entry($data2, [$field2->field->id => 'value21'], 0, ['Cats']);
1108
        $tag = \core_tag_tag::get_by_name(0, 'Cats');
1109
 
1110
        // Admin can see everything.
1111
        $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1112
        $this->assertStringContainsString('value11', $res->content);
1113
        $this->assertStringContainsString('value21', $res->content);
1114
        $this->assertEmpty($res->prevpageurl);
1115
        $this->assertEmpty($res->nextpageurl);
1116
 
1117
        // Create and enrol a user.
1118
        $student = self::getDataGenerator()->create_user();
1119
        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1120
        $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
1121
        $this->setUser($student);
1122
        \core_tag_index_builder::reset_caches();
1123
 
1124
        // User can search data records inside a course.
1125
        $coursecontext = \context_course::instance($course1->id);
1126
        $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1127
 
1128
        $this->assertStringContainsString('value11', $res->content);
1129
        $this->assertStringNotContainsString('value21', $res->content);
1130
 
1131
        $this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual');
1132
 
1133
        \core_tag_index_builder::reset_caches();
1134
        $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1135
 
1136
        $this->assertStringContainsString('value11', $res->content);
1137
        $this->assertStringContainsString('value21', $res->content);
1138
    }
1139
 
11 efrain 1140
    public function test_mod_data_get_tagged_records_course_groups(): void {
1 efrain 1141
        global $DB;
1142
 
1143
        $this->resetAfterTest();
1144
        $this->setAdminUser();
1145
 
1146
        // Setup test data.
1147
        $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
1148
        $course2 = $this->getDataGenerator()->create_course();
1149
        $course1 = $this->getDataGenerator()->create_course();
1150
 
1151
        $groupa = $this->getDataGenerator()->create_group(array('courseid' => $course2->id, 'name' => 'groupA'));
1152
        $groupb = $this->getDataGenerator()->create_group(array('courseid' => $course2->id, 'name' => 'groupB'));
1153
 
1154
        $fieldrecord = new \stdClass();
1155
        $fieldrecord->name = 'field-1';
1156
        $fieldrecord->type = 'text';
1157
 
1158
        $data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id, 'approval' => true));
1159
        $field1 = $datagenerator->create_field($fieldrecord, $data1);
1160
        $data2 = $this->getDataGenerator()->create_module('data', array('course' => $course2->id));
1161
        $field2 = $datagenerator->create_field($fieldrecord, $data2);
1162
        set_coursemodule_groupmode($data2->cmid, SEPARATEGROUPS);
1163
 
1164
        $record11 = $datagenerator->create_entry($data1, [$field1->field->id => 'value11'],
1165
                0, ['Cats', 'Dogs']);
1166
        $record21 = $datagenerator->create_entry($data2, [$field2->field->id => 'value21'],
1167
                $groupa->id, ['Cats']);
1168
        $record22 = $datagenerator->create_entry($data2, [$field2->field->id => 'value22'],
1169
                $groupb->id, ['Cats']);
1170
        $tag = \core_tag_tag::get_by_name(0, 'Cats');
1171
 
1172
        // Admin can see everything.
1173
        $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1174
        $this->assertStringContainsString('value11', $res->content);
1175
        $this->assertStringContainsString('value21', $res->content);
1176
        $this->assertStringContainsString('value22', $res->content);
1177
        $this->assertEmpty($res->prevpageurl);
1178
        $this->assertEmpty($res->nextpageurl);
1179
 
1180
        // Create and enrol a user.
1181
        $student = self::getDataGenerator()->create_user();
1182
        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1183
        $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
1184
        $this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual');
1185
        groups_add_member($groupa, $student);
1186
        $this->setUser($student);
1187
        \core_tag_index_builder::reset_caches();
1188
 
1189
        // User can search data records inside a course.
1190
        $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1191
 
1192
        $this->assertStringContainsString('value11', $res->content);
1193
        $this->assertStringContainsString('value21', $res->content);
1194
        $this->assertStringNotContainsString('value22', $res->content);
1195
 
1196
        groups_add_member($groupb, $student);
1197
        \core_tag_index_builder::reset_caches();
1198
        $res = mod_data_get_tagged_records($tag, false, 0, 0, 1, 0);
1199
 
1200
        $this->assertStringContainsString('value11', $res->content);
1201
        $this->assertStringContainsString('value21', $res->content);
1202
        $this->assertStringContainsString('value22', $res->content);
1203
    }
1204
 
1205
    /**
1206
     * Test check_updates_since callback.
1207
     */
11 efrain 1208
    public function test_check_updates_since(): void {
1 efrain 1209
        global $DB;
1210
        $this->resetAfterTest();
1211
        $this->setAdminUser();
1212
        $course = $this->getDataGenerator()->create_course();
1213
        // Create user.
1214
        $student = self::getDataGenerator()->create_user();
1215
        // User enrolment.
1216
        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1217
        $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual');
1218
        $this->setCurrentTimeStart();
1219
        $record = array(
1220
            'course' => $course->id,
1221
        );
1222
        $data = $this->getDataGenerator()->create_module('data', $record);
1223
        $cm = get_coursemodule_from_instance('data', $data->id, $course->id);
1224
        $cm = \cm_info::create($cm);
1225
        $this->setUser($student);
1226
 
1227
        // Check that upon creation, the updates are only about the new configuration created.
1228
        $onehourago = time() - HOURSECS;
1229
        $updates = data_check_updates_since($cm, $onehourago);
1230
        foreach ($updates as $el => $val) {
1231
            if ($el == 'configuration') {
1232
                $this->assertTrue($val->updated);
1233
                $this->assertTimeCurrent($val->timeupdated);
1234
            } else {
1235
                $this->assertFalse($val->updated);
1236
            }
1237
        }
1238
 
1239
        // Add a couple of entries.
1240
        $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
1241
        $fieldtypes = array('checkbox', 'date');
1242
 
1243
        $count = 1;
1244
        // Creating test Fields with default parameter values.
1245
        foreach ($fieldtypes as $fieldtype) {
1246
            // Creating variables dynamically.
1247
            $fieldname = 'field-' . $count;
1248
            $record = new \stdClass();
1249
            $record->name = $fieldname;
1250
            $record->type = $fieldtype;
1251
            $record->required = 1;
1252
 
1253
            ${$fieldname} = $datagenerator->create_field($record, $data);
1254
            $count++;
1255
        }
1256
 
1257
        $fields = $DB->get_records('data_fields', array('dataid' => $data->id), 'id');
1258
 
1259
        $contents = array();
1260
        $contents[] = array('opt1', 'opt2', 'opt3', 'opt4');
1261
        $contents[] = '01-01-2037'; // It should be lower than 2038, to avoid failing on 32-bit windows.
1262
        $count = 0;
1263
        $fieldcontents = array();
1264
        foreach ($fields as $fieldrecord) {
1265
            $fieldcontents[$fieldrecord->id] = $contents[$count++];
1266
        }
1267
 
1268
        $datarecor1did = $datagenerator->create_entry($data, $fieldcontents);
1269
        $datarecor2did = $datagenerator->create_entry($data, $fieldcontents);
1270
        $records = $DB->get_records('data_records', array('dataid' => $data->id));
1271
        $this->assertCount(2, $records);
1272
        // Check we received the entries updated.
1273
        $updates = data_check_updates_since($cm, $onehourago);
1274
        $this->assertTrue($updates->entries->updated);
1275
        $this->assertEqualsCanonicalizing([$datarecor1did, $datarecor2did], $updates->entries->itemids);
1276
    }
1277
 
11 efrain 1278
    public function test_data_core_calendar_provide_event_action_in_hidden_section(): void {
1 efrain 1279
        global $CFG;
1280
 
1281
        $this->resetAfterTest();
1282
 
1283
        $this->setAdminUser();
1284
 
1285
        // Create a course.
1286
        $course = $this->getDataGenerator()->create_course();
1287
 
1288
        // Create a student.
1289
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1290
 
1291
        // Create a database activity.
1292
        $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id,
1293
                'timeavailablefrom' => time() - DAYSECS, 'timeavailableto' => time() + DAYSECS));
1294
 
1295
        // Create a calendar event.
1296
        $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1297
 
1298
        // Set sections 0 as hidden.
1299
        set_section_visible($course->id, 0, 0);
1300
 
1301
        // Now, log out.
1302
        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
1303
        $this->setUser();
1304
 
1305
        // Create an action factory.
1306
        $factory = new \core_calendar\action_factory();
1307
 
1308
        // Decorate action event for the student.
1309
        $actionevent = mod_data_core_calendar_provide_event_action($event, $factory, $student->id);
1310
 
1311
        // Confirm the event is not shown at all.
1312
        $this->assertNull($actionevent);
1313
    }
1314
 
11 efrain 1315
    public function test_data_core_calendar_provide_event_action_for_non_user(): void {
1 efrain 1316
        global $CFG;
1317
 
1318
        $this->resetAfterTest();
1319
 
1320
        $this->setAdminUser();
1321
 
1322
        // Create a course.
1323
        $course = $this->getDataGenerator()->create_course();
1324
 
1325
        // Create a database activity.
1326
        $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id,
1327
            'timeavailablefrom' => time() - DAYSECS, 'timeavailableto' => time() + DAYSECS));
1328
 
1329
        // Create a calendar event.
1330
        $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1331
 
1332
        // Now, log out.
1333
        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
1334
        $this->setUser();
1335
 
1336
        // Create an action factory.
1337
        $factory = new \core_calendar\action_factory();
1338
 
1339
        // Decorate action event.
1340
        $actionevent = mod_data_core_calendar_provide_event_action($event, $factory);
1341
 
1342
        // Confirm the event is not shown at all.
1343
        $this->assertNull($actionevent);
1344
    }
1345
 
11 efrain 1346
    public function test_data_core_calendar_provide_event_action_open(): void {
1 efrain 1347
        $this->resetAfterTest();
1348
 
1349
        $this->setAdminUser();
1350
 
1351
        // Create a course.
1352
        $course = $this->getDataGenerator()->create_course();
1353
 
1354
        // Create a database activity.
1355
        $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id,
1356
            'timeavailablefrom' => time() - DAYSECS, 'timeavailableto' => time() + DAYSECS));
1357
 
1358
        // Create a calendar event.
1359
        $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1360
 
1361
        // Create an action factory.
1362
        $factory = new \core_calendar\action_factory();
1363
 
1364
        // Decorate action event.
1365
        $actionevent = mod_data_core_calendar_provide_event_action($event, $factory);
1366
 
1367
        // Confirm the event was decorated.
1368
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
1369
        $this->assertEquals(get_string('add', 'data'), $actionevent->get_name());
1370
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
1371
        $this->assertEquals(1, $actionevent->get_item_count());
1372
        $this->assertTrue($actionevent->is_actionable());
1373
    }
1374
 
11 efrain 1375
    public function test_data_core_calendar_provide_event_action_open_for_user(): void {
1 efrain 1376
        global $CFG;
1377
 
1378
        $this->resetAfterTest();
1379
 
1380
        $this->setAdminUser();
1381
 
1382
        // Create a course.
1383
        $course = $this->getDataGenerator()->create_course();
1384
 
1385
        // Create a student.
1386
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1387
 
1388
        // Create a database activity.
1389
        $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id,
1390
            'timeavailablefrom' => time() - DAYSECS, 'timeavailableto' => time() + DAYSECS));
1391
 
1392
        // Create a calendar event.
1393
        $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1394
 
1395
        // Now log out.
1396
        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
1397
        $this->setUser();
1398
 
1399
        // Create an action factory.
1400
        $factory = new \core_calendar\action_factory();
1401
 
1402
        // Decorate action event for the student.
1403
        $actionevent = mod_data_core_calendar_provide_event_action($event, $factory, $student->id);
1404
 
1405
        // Confirm the event was decorated.
1406
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
1407
        $this->assertEquals(get_string('add', 'data'), $actionevent->get_name());
1408
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
1409
        $this->assertEquals(1, $actionevent->get_item_count());
1410
        $this->assertTrue($actionevent->is_actionable());
1411
    }
1412
 
11 efrain 1413
    public function test_data_core_calendar_provide_event_action_closed(): void {
1 efrain 1414
        $this->resetAfterTest();
1415
 
1416
        $this->setAdminUser();
1417
 
1418
        // Create a course.
1419
        $course = $this->getDataGenerator()->create_course();
1420
 
1421
        // Create a database activity.
1422
        $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id,
1423
            'timeavailableto' => time() - DAYSECS));
1424
 
1425
        // Create a calendar event.
1426
        $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1427
 
1428
        // Create an action factory.
1429
        $factory = new \core_calendar\action_factory();
1430
 
1431
        // Decorate action event.
1432
        $actionevent = mod_data_core_calendar_provide_event_action($event, $factory);
1433
 
1434
        // No event on the dashboard if module is closed.
1435
        $this->assertNull($actionevent);
1436
    }
1437
 
11 efrain 1438
    public function test_data_core_calendar_provide_event_action_closed_for_user(): void {
1 efrain 1439
        $this->resetAfterTest();
1440
 
1441
        $this->setAdminUser();
1442
 
1443
        // Create a course.
1444
        $course = $this->getDataGenerator()->create_course();
1445
 
1446
        // Create a student.
1447
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1448
 
1449
        // Create a database activity.
1450
        $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id,
1451
            'timeavailableto' => time() - DAYSECS));
1452
 
1453
        // Create a calendar event.
1454
        $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1455
 
1456
        // Now log out.
1457
        $this->setUser();
1458
 
1459
        // Create an action factory.
1460
        $factory = new \core_calendar\action_factory();
1461
 
1462
        // Decorate action event for the student.
1463
        $actionevent = mod_data_core_calendar_provide_event_action($event, $factory, $student->id);
1464
 
1465
        // No event on the dashboard if module is closed.
1466
        $this->assertNull($actionevent);
1467
    }
1468
 
11 efrain 1469
    public function test_data_core_calendar_provide_event_action_open_in_future(): void {
1 efrain 1470
        $this->resetAfterTest();
1471
 
1472
        $this->setAdminUser();
1473
 
1474
        // Create a course.
1475
        $course = $this->getDataGenerator()->create_course();
1476
 
1477
        // Create a database activity.
1478
        $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id,
1479
            'timeavailablefrom' => time() + DAYSECS));
1480
 
1481
        // Create a calendar event.
1482
        $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1483
 
1484
        // Create an action factory.
1485
        $factory = new \core_calendar\action_factory();
1486
 
1487
        // Decorate action event.
1488
        $actionevent = mod_data_core_calendar_provide_event_action($event, $factory);
1489
 
1490
        // Confirm the event was decorated.
1491
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
1492
        $this->assertEquals(get_string('add', 'data'), $actionevent->get_name());
1493
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
1494
        $this->assertEquals(1, $actionevent->get_item_count());
1495
        $this->assertFalse($actionevent->is_actionable());
1496
    }
1497
 
11 efrain 1498
    public function test_data_core_calendar_provide_event_action_open_in_future_for_user(): void {
1 efrain 1499
        global $CFG;
1500
 
1501
        $this->resetAfterTest();
1502
 
1503
        $this->setAdminUser();
1504
 
1505
        // Create a course.
1506
        $course = $this->getDataGenerator()->create_course();
1507
 
1508
        // Create a student.
1509
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1510
 
1511
        // Create a database activity.
1512
        $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id,
1513
            'timeavailablefrom' => time() + DAYSECS));
1514
 
1515
        // Create a calendar event.
1516
        $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1517
 
1518
        // Now log out.
1519
        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
1520
        $this->setUser();
1521
 
1522
        // Create an action factory.
1523
        $factory = new \core_calendar\action_factory();
1524
 
1525
        // Decorate action event for the student.
1526
        $actionevent = mod_data_core_calendar_provide_event_action($event, $factory, $student->id);
1527
 
1528
        // Confirm the event was decorated.
1529
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
1530
        $this->assertEquals(get_string('add', 'data'), $actionevent->get_name());
1531
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
1532
        $this->assertEquals(1, $actionevent->get_item_count());
1533
        $this->assertFalse($actionevent->is_actionable());
1534
    }
1535
 
11 efrain 1536
    public function test_data_core_calendar_provide_event_action_no_time_specified(): void {
1 efrain 1537
        $this->resetAfterTest();
1538
 
1539
        $this->setAdminUser();
1540
 
1541
        // Create a course.
1542
        $course = $this->getDataGenerator()->create_course();
1543
 
1544
        // Create a database activity.
1545
        $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id));
1546
 
1547
        // Create a calendar event.
1548
        $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1549
 
1550
        // Create an action factory.
1551
        $factory = new \core_calendar\action_factory();
1552
 
1553
        // Decorate action event.
1554
        $actionevent = mod_data_core_calendar_provide_event_action($event, $factory);
1555
 
1556
        // Confirm the event was decorated.
1557
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
1558
        $this->assertEquals(get_string('add', 'data'), $actionevent->get_name());
1559
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
1560
        $this->assertEquals(1, $actionevent->get_item_count());
1561
        $this->assertTrue($actionevent->is_actionable());
1562
    }
1563
 
11 efrain 1564
    public function test_data_core_calendar_provide_event_action_no_time_specified_for_user(): void {
1 efrain 1565
        global $CFG;
1566
 
1567
        $this->resetAfterTest();
1568
 
1569
        $this->setAdminUser();
1570
 
1571
        // Create a course.
1572
        $course = $this->getDataGenerator()->create_course();
1573
 
1574
        // Create a student.
1575
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1576
 
1577
        // Create a database activity.
1578
        $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id));
1579
 
1580
        // Create a calendar event.
1581
        $event = $this->create_action_event($course->id, $data->id, DATA_EVENT_TYPE_OPEN);
1582
 
1583
        // Now log out.
1584
        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
1585
        $this->setUser();
1586
 
1587
        // Create an action factory.
1588
        $factory = new \core_calendar\action_factory();
1589
 
1590
        // Decorate action event for the student.
1591
        $actionevent = mod_data_core_calendar_provide_event_action($event, $factory, $student->id);
1592
 
1593
        // Confirm the event was decorated.
1594
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
1595
        $this->assertEquals(get_string('add', 'data'), $actionevent->get_name());
1596
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
1597
        $this->assertEquals(1, $actionevent->get_item_count());
1598
        $this->assertTrue($actionevent->is_actionable());
1599
    }
1600
 
1601
    /**
1602
     * Creates an action event.
1603
     *
1604
     * @param int $courseid
1605
     * @param int $instanceid The data id.
1606
     * @param string $eventtype The event type. eg. DATA_EVENT_TYPE_OPEN.
1607
     * @param int|null $timestart The start timestamp for the event
1608
     * @return bool|calendar_event
1609
     */
1610
    private function create_action_event($courseid, $instanceid, $eventtype, $timestart = null) {
1611
        $event = new \stdClass();
1612
        $event->name = 'Calendar event';
1613
        $event->modulename  = 'data';
1614
        $event->courseid = $courseid;
1615
        $event->instance = $instanceid;
1616
        $event->type = CALENDAR_EVENT_TYPE_ACTION;
1617
        $event->eventtype = $eventtype;
1618
        if ($timestart) {
1619
            $event->timestart = $timestart;
1620
        } else {
1621
            $event->timestart = time();
1622
        }
1623
 
1624
        return \calendar_event::create($event);
1625
    }
1626
 
1627
    /**
1628
     * Test the callback responsible for returning the completion rule descriptions.
1629
     * This function should work given either an instance of the module (cm_info), such as when checking the active rules,
1630
     * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type.
1631
     */
11 efrain 1632
    public function test_mod_data_completion_get_active_rule_descriptions(): void {
1 efrain 1633
        $this->resetAfterTest();
1634
        $this->setAdminUser();
1635
 
1636
        // Two activities, both with automatic completion. One has the 'completionentries' rule, one doesn't.
1637
        $course = $this->getDataGenerator()->create_course(['enablecompletion' => 2]);
1638
        $data1 = $this->getDataGenerator()->create_module('data', [
1639
            'course' => $course->id,
1640
            'completion' => 2,
1641
            'completionentries' => 3
1642
        ]);
1643
        $data2 = $this->getDataGenerator()->create_module('data', [
1644
            'course' => $course->id,
1645
            'completion' => 2,
1646
            'completionentries' => 0
1647
        ]);
1648
        $cm1 = \cm_info::create(get_coursemodule_from_instance('data', $data1->id));
1649
        $cm2 = \cm_info::create(get_coursemodule_from_instance('data', $data2->id));
1650
 
1651
        // Data for the stdClass input type.
1652
        // This type of input would occur when checking the default completion rules for an activity type, where we don't have
1653
        // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info.
1654
        $moddefaults = new \stdClass();
1655
        $moddefaults->customdata = ['customcompletionrules' => ['completionentries' => 3]];
1656
        $moddefaults->completion = 2;
1657
 
1658
        $activeruledescriptions = [get_string('completionentriesdesc', 'data', 3)];
1659
        $this->assertEquals(mod_data_get_completion_active_rule_descriptions($cm1), $activeruledescriptions);
1660
        $this->assertEquals(mod_data_get_completion_active_rule_descriptions($cm2), []);
1661
        $this->assertEquals(mod_data_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions);
1662
        $this->assertEquals(mod_data_get_completion_active_rule_descriptions(new \stdClass()), []);
1663
    }
1664
 
1665
    /**
1666
     * An unknown event type should not change the data instance.
1667
     */
11 efrain 1668
    public function test_mod_data_core_calendar_event_timestart_updated_unknown_event(): void {
1 efrain 1669
        global $CFG, $DB;
1670
        require_once($CFG->dirroot . "/calendar/lib.php");
1671
 
1672
        $this->resetAfterTest(true);
1673
        $this->setAdminUser();
1674
        $generator = $this->getDataGenerator();
1675
        $course = $generator->create_course();
1676
        $datagenerator = $generator->get_plugin_generator('mod_data');
1677
        $timeopen = time();
1678
        $timeclose = $timeopen + DAYSECS;
1679
        $data = $datagenerator->create_instance(['course' => $course->id]);
1680
        $data->timeavailablefrom = $timeopen;
1681
        $data->timeavailableto = $timeclose;
1682
        $DB->update_record('data', $data);
1683
 
1684
        // Create a valid event.
1685
        $event = new \calendar_event([
1686
            'name' => 'Test event',
1687
            'description' => '',
1688
            'format' => 1,
1689
            'courseid' => $course->id,
1690
            'groupid' => 0,
1691
            'userid' => 2,
1692
            'modulename' => 'data',
1693
            'instance' => $data->id,
1694
            'eventtype' => DATA_EVENT_TYPE_OPEN . "SOMETHING ELSE",
1695
            'timestart' => 1,
1696
            'timeduration' => 86400,
1697
            'visible' => 1
1698
        ]);
1699
 
1700
        mod_data_core_calendar_event_timestart_updated($event, $data);
1701
        $data = $DB->get_record('data', ['id' => $data->id]);
1702
        $this->assertEquals($timeopen, $data->timeavailablefrom);
1703
        $this->assertEquals($timeclose, $data->timeavailableto);
1704
    }
1705
 
1706
    /**
1707
     * A DATA_EVENT_TYPE_OPEN event should update the timeavailablefrom property of the data activity.
1708
     */
11 efrain 1709
    public function test_mod_data_core_calendar_event_timestart_updated_open_event(): void {
1 efrain 1710
        global $CFG, $DB;
1711
        require_once($CFG->dirroot . "/calendar/lib.php");
1712
 
1713
        $this->resetAfterTest(true);
1714
        $this->setAdminUser();
1715
        $generator = $this->getDataGenerator();
1716
        $course = $generator->create_course();
1717
        $datagenerator = $generator->get_plugin_generator('mod_data');
1718
        $timeopen = time();
1719
        $timeclose = $timeopen + DAYSECS;
1720
        $timemodified = 1;
1721
        $newtimeopen = $timeopen - DAYSECS;
1722
        $data = $datagenerator->create_instance(['course' => $course->id]);
1723
        $data->timeavailablefrom = $timeopen;
1724
        $data->timeavailableto = $timeclose;
1725
        $data->timemodified = $timemodified;
1726
        $DB->update_record('data', $data);
1727
 
1728
        // Create a valid event.
1729
        $event = new \calendar_event([
1730
            'name' => 'Test event',
1731
            'description' => '',
1732
            'format' => 1,
1733
            'courseid' => $course->id,
1734
            'groupid' => 0,
1735
            'userid' => 2,
1736
            'modulename' => 'data',
1737
            'instance' => $data->id,
1738
            'eventtype' => DATA_EVENT_TYPE_OPEN,
1739
            'timestart' => $newtimeopen,
1740
            'timeduration' => 86400,
1741
            'visible' => 1
1742
        ]);
1743
 
1744
        // Trigger and capture the event when adding a contact.
1745
        $sink = $this->redirectEvents();
1746
        mod_data_core_calendar_event_timestart_updated($event, $data);
1747
        $triggeredevents = $sink->get_events();
1748
        $moduleupdatedevents = array_filter($triggeredevents, function($e) {
1749
            return is_a($e, 'core\event\course_module_updated');
1750
        });
1751
        $data = $DB->get_record('data', ['id' => $data->id]);
1752
 
1753
        // Ensure the timeavailablefrom property matches the event timestart.
1754
        $this->assertEquals($newtimeopen, $data->timeavailablefrom);
1755
        // Ensure the timeavailableto isn't changed.
1756
        $this->assertEquals($timeclose, $data->timeavailableto);
1757
        // Ensure the timemodified property has been changed.
1758
        $this->assertNotEquals($timemodified, $data->timemodified);
1759
        // Confirm that a module updated event is fired when the module is changed.
1760
        $this->assertNotEmpty($moduleupdatedevents);
1761
    }
1762
 
1763
    /**
1764
     * A DATA_EVENT_TYPE_CLOSE event should update the timeavailableto property of the data activity.
1765
     */
11 efrain 1766
    public function test_mod_data_core_calendar_event_timestart_updated_close_event(): void {
1 efrain 1767
        global $CFG, $DB;
1768
        require_once($CFG->dirroot . "/calendar/lib.php");
1769
 
1770
        $this->resetAfterTest(true);
1771
        $this->setAdminUser();
1772
        $generator = $this->getDataGenerator();
1773
        $course = $generator->create_course();
1774
        $datagenerator = $generator->get_plugin_generator('mod_data');
1775
        $timeopen = time();
1776
        $timeclose = $timeopen + DAYSECS;
1777
        $timemodified = 1;
1778
        $newtimeclose = $timeclose + DAYSECS;
1779
        $data = $datagenerator->create_instance(['course' => $course->id]);
1780
        $data->timeavailablefrom = $timeopen;
1781
        $data->timeavailableto = $timeclose;
1782
        $data->timemodified = $timemodified;
1783
        $DB->update_record('data', $data);
1784
 
1785
        // Create a valid event.
1786
        $event = new \calendar_event([
1787
            'name' => 'Test event',
1788
            'description' => '',
1789
            'format' => 1,
1790
            'courseid' => $course->id,
1791
            'groupid' => 0,
1792
            'userid' => 2,
1793
            'modulename' => 'data',
1794
            'instance' => $data->id,
1795
            'eventtype' => DATA_EVENT_TYPE_CLOSE,
1796
            'timestart' => $newtimeclose,
1797
            'timeduration' => 86400,
1798
            'visible' => 1
1799
        ]);
1800
 
1801
        // Trigger and capture the event when adding a contact.
1802
        $sink = $this->redirectEvents();
1803
        mod_data_core_calendar_event_timestart_updated($event, $data);
1804
        $triggeredevents = $sink->get_events();
1805
        $moduleupdatedevents = array_filter($triggeredevents, function($e) {
1806
            return is_a($e, 'core\event\course_module_updated');
1807
        });
1808
        $data = $DB->get_record('data', ['id' => $data->id]);
1809
 
1810
        // Ensure the timeavailableto property matches the event timestart.
1811
        $this->assertEquals($newtimeclose, $data->timeavailableto);
1812
        // Ensure the timeavailablefrom isn't changed.
1813
        $this->assertEquals($timeopen, $data->timeavailablefrom);
1814
        // Ensure the timemodified property has been changed.
1815
        $this->assertNotEquals($timemodified, $data->timemodified);
1816
        // Confirm that a module updated event is fired when the module is changed.
1817
        $this->assertNotEmpty($moduleupdatedevents);
1818
    }
1819
 
1820
    /**
1821
     * An unknown event type should not have any limits.
1822
     */
11 efrain 1823
    public function test_mod_data_core_calendar_get_valid_event_timestart_range_unknown_event(): void {
1 efrain 1824
        global $CFG;
1825
        require_once($CFG->dirroot . "/calendar/lib.php");
1826
 
1827
        $this->resetAfterTest(true);
1828
        $this->setAdminUser();
1829
        $generator = $this->getDataGenerator();
1830
        $course = $generator->create_course();
1831
        $timeopen = time();
1832
        $timeclose = $timeopen + DAYSECS;
1833
        $data = new \stdClass();
1834
        $data->timeavailablefrom = $timeopen;
1835
        $data->timeavailableto = $timeclose;
1836
 
1837
        // Create a valid event.
1838
        $event = new \calendar_event([
1839
            'name' => 'Test event',
1840
            'description' => '',
1841
            'format' => 1,
1842
            'courseid' => $course->id,
1843
            'groupid' => 0,
1844
            'userid' => 2,
1845
            'modulename' => 'data',
1846
            'instance' => 1,
1847
            'eventtype' => DATA_EVENT_TYPE_OPEN . "SOMETHING ELSE",
1848
            'timestart' => 1,
1849
            'timeduration' => 86400,
1850
            'visible' => 1
1851
        ]);
1852
 
1853
        list ($min, $max) = mod_data_core_calendar_get_valid_event_timestart_range($event, $data);
1854
        $this->assertNull($min);
1855
        $this->assertNull($max);
1856
    }
1857
 
1858
    /**
1859
     * The open event should be limited by the data's timeclose property, if it's set.
1860
     */
11 efrain 1861
    public function test_mod_data_core_calendar_get_valid_event_timestart_range_open_event(): void {
1 efrain 1862
        global $CFG;
1863
        require_once($CFG->dirroot . "/calendar/lib.php");
1864
 
1865
        $this->resetAfterTest(true);
1866
        $this->setAdminUser();
1867
        $generator = $this->getDataGenerator();
1868
        $course = $generator->create_course();
1869
        $timeopen = time();
1870
        $timeclose = $timeopen + DAYSECS;
1871
        $data = new \stdClass();
1872
        $data->timeavailablefrom = $timeopen;
1873
        $data->timeavailableto = $timeclose;
1874
 
1875
        // Create a valid event.
1876
        $event = new \calendar_event([
1877
            'name' => 'Test event',
1878
            'description' => '',
1879
            'format' => 1,
1880
            'courseid' => $course->id,
1881
            'groupid' => 0,
1882
            'userid' => 2,
1883
            'modulename' => 'data',
1884
            'instance' => 1,
1885
            'eventtype' => DATA_EVENT_TYPE_OPEN,
1886
            'timestart' => 1,
1887
            'timeduration' => 86400,
1888
            'visible' => 1
1889
        ]);
1890
 
1891
        // The max limit should be bounded by the timeclose value.
1892
        list ($min, $max) = mod_data_core_calendar_get_valid_event_timestart_range($event, $data);
1893
        $this->assertNull($min);
1894
        $this->assertEquals($timeclose, $max[0]);
1895
 
1896
        // No timeclose value should result in no upper limit.
1897
        $data->timeavailableto = 0;
1898
        list ($min, $max) = mod_data_core_calendar_get_valid_event_timestart_range($event, $data);
1899
        $this->assertNull($min);
1900
        $this->assertNull($max);
1901
    }
1902
 
1903
    /**
1904
     * The close event should be limited by the data's timeavailablefrom property, if it's set.
1905
     */
11 efrain 1906
    public function test_mod_data_core_calendar_get_valid_event_timestart_range_close_event(): void {
1 efrain 1907
        global $CFG;
1908
 
1909
        require_once($CFG->dirroot . "/calendar/lib.php");
1910
 
1911
        $this->resetAfterTest(true);
1912
        $this->setAdminUser();
1913
        $generator = $this->getDataGenerator();
1914
        $course = $generator->create_course();
1915
        $timeopen = time();
1916
        $timeclose = $timeopen + DAYSECS;
1917
        $data = new \stdClass();
1918
        $data->timeavailablefrom = $timeopen;
1919
        $data->timeavailableto = $timeclose;
1920
 
1921
        // Create a valid event.
1922
        $event = new \calendar_event([
1923
            'name' => 'Test event',
1924
            'description' => '',
1925
            'format' => 1,
1926
            'courseid' => $course->id,
1927
            'groupid' => 0,
1928
            'userid' => 2,
1929
            'modulename' => 'data',
1930
            'instance' => 1,
1931
            'eventtype' => DATA_EVENT_TYPE_CLOSE,
1932
            'timestart' => 1,
1933
            'timeduration' => 86400,
1934
            'visible' => 1
1935
        ]);
1936
 
1937
        // The max limit should be bounded by the timeclose value.
1938
        list ($min, $max) = mod_data_core_calendar_get_valid_event_timestart_range($event, $data);
1939
        $this->assertEquals($timeopen, $min[0]);
1940
        $this->assertNull($max);
1941
 
1942
        // No timeavailableto value should result in no upper limit.
1943
        $data->timeavailablefrom = 0;
1944
        list ($min, $max) = mod_data_core_calendar_get_valid_event_timestart_range($event, $data);
1945
        $this->assertNull($min);
1946
        $this->assertNull($max);
1947
    }
1948
 
1949
    /**
1950
     * A user who does not have capabilities to add events to the calendar should be able to create an database.
1951
     */
11 efrain 1952
    public function test_creation_with_no_calendar_capabilities(): void {
1 efrain 1953
        $this->resetAfterTest();
1954
        $course = self::getDataGenerator()->create_course();
1955
        $context = \context_course::instance($course->id);
1956
        $user = self::getDataGenerator()->create_and_enrol($course, 'editingteacher');
1957
        $roleid = self::getDataGenerator()->create_role();
1958
        self::getDataGenerator()->role_assign($roleid, $user->id, $context->id);
1959
        assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true);
1960
        $generator = self::getDataGenerator()->get_plugin_generator('mod_data');
1961
        // Create an instance as a user without the calendar capabilities.
1962
        $this->setUser($user);
1963
        $time = time();
1964
        $params = array(
1965
            'course' => $course->id,
1966
            'timeavailablefrom' => $time + 200,
1967
            'timeavailableto' => $time + 2000,
1968
            'timeviewfrom' => $time + 400,
1969
            'timeviewto' => $time + 2000,
1970
        );
1971
        $generator->create_instance($params);
1972
    }
1973
 
1974
    /**
1975
     * Test for data_generate_default_template(). This method covers different scenarios for checking when the returned value
1976
     * is empty or not, but doesn't check if the content has the expected value when it's not empty.
1977
     *
1978
     * @covers ::data_generate_default_template
1979
     */
1980
    public function test_data_generate_default_template(): void {
1981
        $this->resetAfterTest();
1982
        $this->setAdminUser();
1983
 
1984
        $course = $this->getDataGenerator()->create_course();
1985
        $activity = $this->getDataGenerator()->create_module(manager::MODULE, ['course' => $course]);
1986
 
1987
        // Check the result is empty when $data and/or $template are null.
1988
        $nullactivity = null;
1989
        $result = data_generate_default_template($nullactivity, 'listtemplate', 0, false, false);
1990
        $this->assertEmpty($result);
1991
        $result = data_generate_default_template($activity, null, 0, false, false);
1992
        $this->assertEmpty($result);
1993
        $result = data_generate_default_template($nullactivity, null, 0, false, false);
1994
        $this->assertEmpty($result);
1995
 
1996
        // Check the result is empty when any of the templates that are empty are given.
1997
        $emptytemplates = [
1998
            'csstemplate',
1999
            'jstemplate',
2000
            'listtemplateheader',
2001
            'listtemplatefooter',
2002
            'rsstitletemplate',
2003
        ];
2004
        foreach ($emptytemplates as $emptytemplate) {
2005
            $result = data_generate_default_template($activity, $emptytemplate, 0, false, false);
2006
            $this->assertEmpty($result);
2007
        }
2008
 
2009
        $templates = [
2010
            'listtemplate',
2011
            'singletemplate',
2012
            'asearchtemplate',
2013
        ];
2014
        // Check the result is empty when the database has no fields.
2015
        foreach ($templates as $template) {
2016
            $result = data_generate_default_template($activity, $template, 0, false, false);
2017
            $this->assertEmpty($result);
2018
            $this->assertEmpty($activity->{$template});
2019
        }
2020
 
2021
        // Add a field to the activity.
2022
        $fieldrecord = new stdClass();
2023
        $fieldrecord->name = 'field-1';
2024
        $fieldrecord->type = 'text';
2025
        $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
2026
        $datagenerator->create_field($fieldrecord, $activity);
2027
 
2028
        // Check the result is not empty when the database has no entries.
2029
        foreach ($templates as $template) {
2030
            $result = data_generate_default_template($activity, $template, 0, false, false);
2031
            $this->assertNotEmpty($result);
2032
            $this->assertEmpty($activity->{$template});
2033
        }
2034
 
2035
        // Check the result is not empty when the database has no entries and the result is saved when $update = true.
2036
        foreach ($templates as $template) {
2037
            $result = data_generate_default_template($activity, $template, 0, false, true);
2038
            $this->assertNotEmpty($result);
2039
            $this->assertNotEmpty($activity->{$template});
2040
        }
2041
    }
2042
 
2043
    /**
2044
     * Test for data_replace_field_in_templates().
2045
     *
2046
     * @covers ::data_replace_field_in_templates
2047
     */
2048
    public function test_data_replace_field_in_templates(): void {
2049
        global $DB;
2050
        $this->resetAfterTest();
2051
        $this->setAdminUser();
2052
 
2053
        $course = $this->getDataGenerator()->create_course();
2054
        $templatecontent = "Field [[myfield]], [[myfield#id]], [[myfield#name]], [[myfield#description]], ";
2055
 
2056
        $params = ['course' => $course];
2057
        foreach (manager::TEMPLATES_LIST as $templatename => $templatefile) {
2058
            $params[$templatename] = $templatecontent;
2059
        }
2060
        $activity = $this->getDataGenerator()->create_module(manager::MODULE, $params);
2061
 
2062
        $generator = $this->getDataGenerator()->get_plugin_generator(manager::PLUGINNAME);
2063
        $fieldrecord = (object)['name' => 'myfield', 'type' => 'text', 'description' => 'This is a field'];
2064
        $generator->create_field($fieldrecord, $activity);
2065
 
2066
        data_replace_field_in_templates($activity, 'myfield', 'newfieldname');
2067
        $dbactivity = $DB->get_record(manager::MODULE, ['id' => $activity->id]);
2068
 
2069
        $newcontent = "Field [[newfieldname]], [[newfieldname#id]], [[newfieldname#name]], [[newfieldname#description]], ";
2070
        // Field compatible templates.
2071
        $this->assertEquals($newcontent, $dbactivity->listtemplate);
2072
        $this->assertEquals($newcontent, $dbactivity->singletemplate);
2073
        $this->assertEquals($newcontent, $dbactivity->asearchtemplate);
2074
        $this->assertEquals($newcontent, $dbactivity->addtemplate);
2075
        $this->assertEquals($newcontent, $dbactivity->rsstemplate);
2076
        // Other templates.
2077
        $this->assertEquals($templatecontent, $dbactivity->listtemplateheader);
2078
        $this->assertEquals($templatecontent, $dbactivity->listtemplatefooter);
2079
        $this->assertEquals($templatecontent, $dbactivity->csstemplate);
2080
        $this->assertEquals($templatecontent, $dbactivity->jstemplate);
2081
        $this->assertEquals($templatecontent, $dbactivity->rsstitletemplate);
2082
    }
2083
 
2084
    /**
2085
     * Test for data_append_new_field_to_templates().
2086
     *
2087
     * @covers ::data_append_new_field_to_templates
2088
     * @dataProvider data_append_new_field_to_templates_provider
2089
     * @param bool $hasfield if the field is present in the templates
2090
     * @param bool $hasotherfields if the field is not present in the templates
2091
     * @param bool $expected the expected return
2092
     */
11 efrain 2093
    public function test_data_append_new_field_to_templates(bool $hasfield, bool $hasotherfields, bool $expected): void {
1 efrain 2094
        global $DB;
2095
        $this->resetAfterTest();
2096
        $this->setAdminUser();
2097
 
2098
        $templatecontent = "Template content";
2099
        if ($hasfield) {
2100
            $templatecontent .= "Has [[myfield]].";
2101
        }
2102
        if ($hasotherfields) {
2103
            $templatecontent .= "And also ##otherfields##.";
2104
        }
2105
 
2106
        $course = $this->getDataGenerator()->create_course();
2107
        $params = ['course' => $course];
2108
        foreach (manager::TEMPLATES_LIST as $templatename => $templatefile) {
2109
            $params[$templatename] = $templatecontent;
2110
        }
2111
        $activity = $this->getDataGenerator()->create_module(manager::MODULE, $params);
2112
 
2113
        $result = data_append_new_field_to_templates($activity, 'myfield');
2114
        $this->assertEquals($expected, $result);
2115
 
2116
        // Check fields with auto add fields.
2117
        $dbactivity = $DB->get_record(manager::MODULE, ['id' => $activity->id]);
2118
        if ($hasfield || $hasotherfields) {
2119
            $this->assertEquals($dbactivity->singletemplate, $templatecontent);
2120
            $this->assertEquals($dbactivity->addtemplate, $templatecontent);
2121
            $this->assertEquals($dbactivity->rsstemplate, $templatecontent);
2122
        } else {
2123
            $regexp = '|Template content.*\[\[myfield\]\]|';
2124
            // We don't want line breaks for the validations.
2125
            $this->assertMatchesRegularExpression($regexp, str_replace("\n", '', $dbactivity->singletemplate));
2126
            $this->assertMatchesRegularExpression($regexp, str_replace("\n", '', $dbactivity->addtemplate));
2127
            $this->assertMatchesRegularExpression($regexp, str_replace("\n", '', $dbactivity->rsstemplate));
2128
        }
2129
        // No auto add field templates.
2130
        $this->assertEquals($dbactivity->asearchtemplate, $templatecontent);
2131
        $this->assertEquals($dbactivity->listtemplate, $templatecontent);
2132
        $this->assertEquals($dbactivity->listtemplateheader, $templatecontent);
2133
        $this->assertEquals($dbactivity->listtemplatefooter, $templatecontent);
2134
        $this->assertEquals($dbactivity->csstemplate, $templatecontent);
2135
        $this->assertEquals($dbactivity->jstemplate, $templatecontent);
2136
        $this->assertEquals($dbactivity->rsstitletemplate, $templatecontent);
2137
    }
2138
 
2139
    /**
2140
     * Data provider for test_data_append_new_field_to_templates().
2141
     *
2142
     * @return array of scenarios
2143
     */
2144
    public function data_append_new_field_to_templates_provider(): array {
2145
        return [
2146
            'Plain template' => [
2147
                'hasfield' => false,
2148
                'hasotherfields' => false,
2149
                'expected' => true,
2150
            ],
2151
            'Field already present' => [
2152
                'hasfield' => true,
2153
                'hasotherfields' => false,
2154
                'expected' => false,
2155
            ],
2156
            '##otherfields## tag present' => [
2157
                'hasfield' => false,
2158
                'hasotherfields' => true,
2159
                'expected' => false,
2160
            ],
2161
            'Field already present and ##otherfields## tag present' => [
2162
                'hasfield' => true,
2163
                'hasotherfields' => true,
2164
                'expected' => false,
2165
            ],
2166
        ];
2167
    }
2168
 
2169
    /**
2170
     * Test that format that are not supported are raising an exception
2171
     *
2172
     * @param string $type
2173
     * @param string $expected
2174
     * @covers \data_get_field_new
2175
     * @dataProvider format_parser_provider
2176
     */
11 efrain 2177
    public function test_create_field(string $type, string $expected): void {
1 efrain 2178
        $this->resetAfterTest();
2179
        $this->setAdminUser();
2180
        $generator = $this->getDataGenerator();
2181
        $course = $generator->create_course();
2182
 
2183
        $data = $this->getDataGenerator()->create_module('data', ['course' => $course->id]  );
2184
        if ($expected === 'exception') {
2185
            $this->expectException(\moodle_exception::class);
2186
        }
2187
        $field = data_get_field_new($type, $data);
2188
        $this->assertStringContainsString($expected, get_class($field));
2189
    }
2190
 
2191
    /**
2192
     * Data provider for test_format_parser
2193
     *
2194
     * @return array[]
2195
     */
2196
    public static function format_parser_provider(): array {
2197
        return [
2198
            'text' => [
2199
                'type' => 'text',
2200
                'expected' => 'data_field_text',
2201
            ],
2202
            'picture' => [
2203
                'type' => 'picture',
2204
                'expected' => 'data_field_picture',
2205
            ],
2206
            'wrong type' => [
2207
                'type' => '../wrongformat123',
2208
                'expected' => 'exception',
2209
            ],
2210
        ];
2211
    }
2212
}