Proyectos de Subversion Moodle

Rev

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

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