Merge branch 'MDL-81073' of https://github.com/paulholden/moodle
[moodle.git] / mod / glossary / tests / lib_test.php
blob9d78bed6c33469ad0c265496ac36468370f172c9
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.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 /**
18 * Glossary lib tests.
20 * @package mod_glossary
21 * @copyright 2015 Frédéric Massart - FMCorz.net
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 namespace mod_glossary;
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
29 require_once($CFG->dirroot . '/mod/glossary/lib.php');
30 require_once($CFG->dirroot . '/mod/glossary/locallib.php');
32 /**
33 * Glossary lib testcase.
35 * @package mod_glossary
36 * @copyright 2015 Frédéric Massart - FMCorz.net
37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39 class lib_test extends \advanced_testcase {
41 public function test_glossary_view() {
42 global $CFG;
43 $origcompletion = $CFG->enablecompletion;
44 $CFG->enablecompletion = true;
45 $this->resetAfterTest(true);
47 // Generate all the things.
48 $c1 = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
49 $g1 = $this->getDataGenerator()->create_module('glossary', array(
50 'course' => $c1->id,
51 'completion' => COMPLETION_TRACKING_AUTOMATIC,
52 'completionview' => 1
53 ));
54 $g2 = $this->getDataGenerator()->create_module('glossary', array(
55 'course' => $c1->id,
56 'completion' => COMPLETION_TRACKING_AUTOMATIC,
57 'completionview' => 1
58 ));
59 $u1 = $this->getDataGenerator()->create_user();
60 $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
61 $modinfo = \course_modinfo::instance($c1->id);
62 $cm1 = $modinfo->get_cm($g1->cmid);
63 $cm2 = $modinfo->get_cm($g2->cmid);
64 $ctx1 = $cm1->context;
65 $completion = new \completion_info($c1);
67 $this->setUser($u1);
69 // Confirm what we've set up.
70 $this->assertEquals(COMPLETION_NOT_VIEWED, $completion->get_data($cm1, false, $u1->id)->viewed);
71 $this->assertEquals(COMPLETION_INCOMPLETE, $completion->get_data($cm1, false, $u1->id)->completionstate);
72 $this->assertEquals(COMPLETION_NOT_VIEWED, $completion->get_data($cm2, false, $u1->id)->viewed);
73 $this->assertEquals(COMPLETION_INCOMPLETE, $completion->get_data($cm2, false, $u1->id)->completionstate);
75 // Simulate the view call.
76 $sink = $this->redirectEvents();
77 glossary_view($g1, $c1, $cm1, $ctx1, 'letter');
78 $events = $sink->get_events();
80 // Assertions.
81 $this->assertCount(3, $events);
82 $this->assertEquals('\core\event\course_module_completion_updated', $events[0]->eventname);
83 $this->assertEquals('\core\event\course_module_completion_updated', $events[1]->eventname);
84 $this->assertEquals('\mod_glossary\event\course_module_viewed', $events[2]->eventname);
85 $this->assertEquals($g1->id, $events[2]->objectid);
86 $this->assertEquals('letter', $events[2]->other['mode']);
87 $this->assertEquals(COMPLETION_VIEWED, $completion->get_data($cm1, false, $u1->id)->viewed);
88 $this->assertEquals(COMPLETION_COMPLETE, $completion->get_data($cm1, false, $u1->id)->completionstate);
89 $this->assertEquals(COMPLETION_NOT_VIEWED, $completion->get_data($cm2, false, $u1->id)->viewed);
90 $this->assertEquals(COMPLETION_INCOMPLETE, $completion->get_data($cm2, false, $u1->id)->completionstate);
92 // Tear down.
93 $sink->close();
94 $CFG->enablecompletion = $origcompletion;
97 public function test_glossary_entry_view() {
98 $this->resetAfterTest(true);
100 // Generate all the things.
101 $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
102 $c1 = $this->getDataGenerator()->create_course();
103 $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
104 $e1 = $gg->create_content($g1);
105 $u1 = $this->getDataGenerator()->create_user();
106 $ctx = \context_module::instance($g1->cmid);
107 $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
109 // Assertions.
110 $sink = $this->redirectEvents();
111 glossary_entry_view($e1, $ctx);
112 $events = $sink->get_events();
113 $this->assertCount(1, $events);
114 $this->assertEquals('\mod_glossary\event\entry_viewed', $events[0]->eventname);
115 $this->assertEquals($e1->id, $events[0]->objectid);
116 $sink->close();
119 public function test_glossary_core_calendar_provide_event_action() {
120 $this->resetAfterTest();
121 $this->setAdminUser();
123 // Create the activity.
124 $course = $this->getDataGenerator()->create_course();
125 $glossary = $this->getDataGenerator()->create_module('glossary', array('course' => $course->id));
127 // Create a calendar event.
128 $event = $this->create_action_event($course->id, $glossary->id,
129 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
131 // Create an action factory.
132 $factory = new \core_calendar\action_factory();
134 // Decorate action event.
135 $actionevent = mod_glossary_core_calendar_provide_event_action($event, $factory);
137 // Confirm the event was decorated.
138 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
139 $this->assertEquals(get_string('view'), $actionevent->get_name());
140 $this->assertInstanceOf('moodle_url', $actionevent->get_url());
141 $this->assertEquals(1, $actionevent->get_item_count());
142 $this->assertTrue($actionevent->is_actionable());
145 public function test_glossary_core_calendar_provide_event_action_as_non_user() {
146 global $CFG;
148 $this->resetAfterTest();
149 $this->setAdminUser();
151 // Create the activity.
152 $course = $this->getDataGenerator()->create_course();
153 $glossary = $this->getDataGenerator()->create_module('glossary', array('course' => $course->id));
155 // Create a calendar event.
156 $event = $this->create_action_event($course->id, $glossary->id,
157 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
159 // Now log out.
160 $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
161 $this->setUser();
163 // Create an action factory.
164 $factory = new \core_calendar\action_factory();
166 // Decorate action event for the student.
167 $actionevent = mod_glossary_core_calendar_provide_event_action($event, $factory);
169 // Confirm the event is not shown at all.
170 $this->assertNull($actionevent);
173 public function test_glossary_core_calendar_provide_event_action_for_user() {
174 global $CFG;
176 $this->resetAfterTest();
177 $this->setAdminUser();
179 // Create a course.
180 $course = $this->getDataGenerator()->create_course();
182 // Create a student.
183 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
185 // Create the activity.
186 $glossary = $this->getDataGenerator()->create_module('glossary', array('course' => $course->id));
188 // Create a calendar event.
189 $event = $this->create_action_event($course->id, $glossary->id,
190 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
192 // Now log out.
193 $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
194 $this->setUser();
196 // Create an action factory.
197 $factory = new \core_calendar\action_factory();
199 // Decorate action event for the student.
200 $actionevent = mod_glossary_core_calendar_provide_event_action($event, $factory, $student->id);
202 // Confirm the event was decorated.
203 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
204 $this->assertEquals(get_string('view'), $actionevent->get_name());
205 $this->assertInstanceOf('moodle_url', $actionevent->get_url());
206 $this->assertEquals(1, $actionevent->get_item_count());
207 $this->assertTrue($actionevent->is_actionable());
210 public function test_glossary_core_calendar_provide_event_action_in_hidden_section() {
211 $this->resetAfterTest();
212 $this->setAdminUser();
214 // Create a course.
215 $course = $this->getDataGenerator()->create_course();
217 // Create a student.
218 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
220 // Create the activity.
221 $glossary = $this->getDataGenerator()->create_module('glossary', array('course' => $course->id));
223 // Create a calendar event.
224 $event = $this->create_action_event($course->id, $glossary->id,
225 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
227 // Set sections 0 as hidden.
228 set_section_visible($course->id, 0, 0);
230 // Create an action factory.
231 $factory = new \core_calendar\action_factory();
233 // Decorate action event for the student.
234 $actionevent = mod_glossary_core_calendar_provide_event_action($event, $factory, $student->id);
236 // Confirm the event is not shown at all.
237 $this->assertNull($actionevent);
240 public function test_glossary_core_calendar_provide_event_action_already_completed() {
241 global $CFG;
243 $this->resetAfterTest();
244 $this->setAdminUser();
246 $CFG->enablecompletion = 1;
248 // Create the activity.
249 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
250 $glossary = $this->getDataGenerator()->create_module('glossary', array('course' => $course->id),
251 array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
253 // Get some additional data.
254 $cm = get_coursemodule_from_instance('glossary', $glossary->id);
256 // Create a calendar event.
257 $event = $this->create_action_event($course->id, $glossary->id,
258 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
260 // Mark the activity as completed.
261 $completion = new \completion_info($course);
262 $completion->set_module_viewed($cm);
264 // Create an action factory.
265 $factory = new \core_calendar\action_factory();
267 // Decorate action event.
268 $actionevent = mod_glossary_core_calendar_provide_event_action($event, $factory);
270 // Ensure result was null.
271 $this->assertNull($actionevent);
274 public function test_glossary_core_calendar_provide_event_action_already_completed_for_user() {
275 global $CFG;
277 $this->resetAfterTest();
278 $this->setAdminUser();
280 $CFG->enablecompletion = 1;
282 // Create a course.
283 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
285 // Create a student.
286 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
288 // Create the activity.
289 $glossary = $this->getDataGenerator()->create_module('glossary', array('course' => $course->id),
290 array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
292 // Get some additional data.
293 $cm = get_coursemodule_from_instance('glossary', $glossary->id);
295 // Create a calendar event.
296 $event = $this->create_action_event($course->id, $glossary->id,
297 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
299 // Mark the activity as completed for the user.
300 $completion = new \completion_info($course);
301 $completion->set_module_viewed($cm, $student->id);
303 // Create an action factory.
304 $factory = new \core_calendar\action_factory();
306 // Decorate action event.
307 $actionevent = mod_glossary_core_calendar_provide_event_action($event, $factory, $student->id);
309 // Ensure result was null.
310 $this->assertNull($actionevent);
314 * Creates an action event.
316 * @param int $courseid The course id.
317 * @param int $instanceid The instance id.
318 * @param string $eventtype The event type.
319 * @return bool|calendar_event
321 private function create_action_event($courseid, $instanceid, $eventtype) {
322 $event = new \stdClass();
323 $event->name = 'Calendar event';
324 $event->modulename = 'glossary';
325 $event->courseid = $courseid;
326 $event->instance = $instanceid;
327 $event->type = CALENDAR_EVENT_TYPE_ACTION;
328 $event->eventtype = $eventtype;
329 $event->timestart = time();
331 return \calendar_event::create($event);
335 * Test the callback responsible for returning the completion rule descriptions.
336 * This function should work given either an instance of the module (cm_info), such as when checking the active rules,
337 * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type.
339 public function test_mod_glossary_completion_get_active_rule_descriptions() {
340 $this->resetAfterTest();
341 $this->setAdminUser();
343 // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't.
344 $course = $this->getDataGenerator()->create_course(['enablecompletion' => 2]);
345 $glossary1 = $this->getDataGenerator()->create_module('glossary', [
346 'course' => $course->id,
347 'completion' => 2,
348 'completionentries' => 3
350 $glossary2 = $this->getDataGenerator()->create_module('glossary', [
351 'course' => $course->id,
352 'completion' => 2,
353 'completionentries' => 0
355 $cm1 = \cm_info::create(get_coursemodule_from_instance('glossary', $glossary1->id));
356 $cm2 = \cm_info::create(get_coursemodule_from_instance('glossary', $glossary2->id));
358 // Data for the stdClass input type.
359 // This type of input would occur when checking the default completion rules for an activity type, where we don't have
360 // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info.
361 $moddefaults = new \stdClass();
362 $moddefaults->customdata = ['customcompletionrules' => ['completionentries' => 3]];
363 $moddefaults->completion = 2;
365 $activeruledescriptions = [get_string('completionentriesdesc', 'glossary', $glossary1->completionentries)];
366 $this->assertEquals(mod_glossary_get_completion_active_rule_descriptions($cm1), $activeruledescriptions);
367 $this->assertEquals(mod_glossary_get_completion_active_rule_descriptions($cm2), []);
368 $this->assertEquals(mod_glossary_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions);
369 $this->assertEquals(mod_glossary_get_completion_active_rule_descriptions(new \stdClass()), []);
372 public function test_mod_glossary_get_tagged_entries() {
373 global $DB;
375 $this->resetAfterTest();
376 $this->setAdminUser();
378 // Setup test data.
379 $glossarygenerator = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
380 $course3 = $this->getDataGenerator()->create_course();
381 $course2 = $this->getDataGenerator()->create_course();
382 $course1 = $this->getDataGenerator()->create_course();
384 // Create and enrol a student.
385 $student = self::getDataGenerator()->create_user();
386 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
387 $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
388 $this->getDataGenerator()->enrol_user($student->id, $course2->id, $studentrole->id, 'manual');
390 // Create glossaries and entries.
391 $glossary1 = $this->getDataGenerator()->create_module('glossary', array('course' => $course1->id));
392 $glossary2 = $this->getDataGenerator()->create_module('glossary', array('course' => $course2->id));
393 $glossary3 = $this->getDataGenerator()->create_module('glossary', array('course' => $course3->id));
394 $entry11 = $glossarygenerator->create_content($glossary1, array('tags' => array('Cats', 'Dogs')));
395 $entry12 = $glossarygenerator->create_content($glossary1, array('tags' => array('Cats', 'mice')));
396 $entry13 = $glossarygenerator->create_content($glossary1, array('tags' => array('Cats')));
397 $entry14 = $glossarygenerator->create_content($glossary1);
398 $entry15 = $glossarygenerator->create_content($glossary1, array('tags' => array('Cats')));
399 $entry16 = $glossarygenerator->create_content($glossary1, array('tags' => array('Cats'), 'approved' => false));
400 $entry17 = $glossarygenerator->create_content($glossary1, array('tags' => array('Cats'), 'approved' => false, 'userid' => $student->id));
401 $entry21 = $glossarygenerator->create_content($glossary2, array('tags' => array('Cats')));
402 $entry22 = $glossarygenerator->create_content($glossary2, array('tags' => array('Cats', 'Dogs')));
403 $entry23 = $glossarygenerator->create_content($glossary2, array('tags' => array('mice', 'Cats')));
404 $entry31 = $glossarygenerator->create_content($glossary3, array('tags' => array('mice', 'Cats')));
406 $tag = \core_tag_tag::get_by_name(0, 'Cats');
408 // Admin can see everything.
409 // Get first page of tagged entries (first 5 entries).
410 $res = mod_glossary_get_tagged_entries($tag, /*$exclusivemode = */false,
411 /*$fromctx = */0, /*$ctx = */0, /*$rec = */1, /*$entry = */0);
412 $this->assertMatchesRegularExpression('/'.$entry11->concept.'</', $res->content);
413 $this->assertMatchesRegularExpression('/'.$entry12->concept.'</', $res->content);
414 $this->assertMatchesRegularExpression('/'.$entry13->concept.'</', $res->content);
415 $this->assertDoesNotMatchRegularExpression('/'.$entry14->concept.'</', $res->content);
416 $this->assertMatchesRegularExpression('/'.$entry15->concept.'</', $res->content);
417 $this->assertMatchesRegularExpression('/'.$entry16->concept.'</', $res->content);
418 $this->assertDoesNotMatchRegularExpression('/'.$entry17->concept.'</', $res->content);
419 $this->assertDoesNotMatchRegularExpression('/'.$entry21->concept.'</', $res->content);
420 $this->assertDoesNotMatchRegularExpression('/'.$entry22->concept.'</', $res->content);
421 $this->assertDoesNotMatchRegularExpression('/'.$entry23->concept.'</', $res->content);
422 $this->assertDoesNotMatchRegularExpression('/'.$entry31->concept.'</', $res->content);
423 $this->assertEmpty($res->prevpageurl);
424 $this->assertNotEmpty($res->nextpageurl);
425 // Get second page of tagged entries (second 5 entries).
426 $res = mod_glossary_get_tagged_entries($tag, /*$exclusivemode = */false,
427 /*$fromctx = */0, /*$ctx = */0, /*$rec = */1, /*$entry = */1);
428 $this->assertDoesNotMatchRegularExpression('/'.$entry11->concept.'</', $res->content);
429 $this->assertDoesNotMatchRegularExpression('/'.$entry12->concept.'</', $res->content);
430 $this->assertDoesNotMatchRegularExpression('/'.$entry13->concept.'</', $res->content);
431 $this->assertDoesNotMatchRegularExpression('/'.$entry14->concept.'</', $res->content);
432 $this->assertDoesNotMatchRegularExpression('/'.$entry15->concept.'</', $res->content);
433 $this->assertDoesNotMatchRegularExpression('/'.$entry16->concept.'</', $res->content);
434 $this->assertMatchesRegularExpression('/'.$entry17->concept.'</', $res->content);
435 $this->assertMatchesRegularExpression('/'.$entry21->concept.'</', $res->content);
436 $this->assertMatchesRegularExpression('/'.$entry22->concept.'</', $res->content);
437 $this->assertMatchesRegularExpression('/'.$entry23->concept.'</', $res->content);
438 $this->assertMatchesRegularExpression('/'.$entry31->concept.'</', $res->content);
439 $this->assertNotEmpty($res->prevpageurl);
440 $this->assertEmpty($res->nextpageurl);
442 $this->setUser($student);
443 \core_tag_index_builder::reset_caches();
445 // User can not see entries in course 3 because he is not enrolled.
446 $res = mod_glossary_get_tagged_entries($tag, /*$exclusivemode = */false,
447 /*$fromctx = */0, /*$ctx = */0, /*$rec = */1, /*$entry = */1);
448 $this->assertMatchesRegularExpression('/'.$entry22->concept.'/', $res->content);
449 $this->assertMatchesRegularExpression('/'.$entry23->concept.'/', $res->content);
450 $this->assertDoesNotMatchRegularExpression('/'.$entry31->concept.'/', $res->content);
452 // User can search glossary entries inside a course.
453 $coursecontext = \context_course::instance($course1->id);
454 $res = mod_glossary_get_tagged_entries($tag, /*$exclusivemode = */false,
455 /*$fromctx = */0, /*$ctx = */$coursecontext->id, /*$rec = */1, /*$entry = */0);
456 $this->assertMatchesRegularExpression('/'.$entry11->concept.'/', $res->content);
457 $this->assertMatchesRegularExpression('/'.$entry12->concept.'/', $res->content);
458 $this->assertMatchesRegularExpression('/'.$entry13->concept.'/', $res->content);
459 $this->assertDoesNotMatchRegularExpression('/'.$entry14->concept.'/', $res->content);
460 $this->assertMatchesRegularExpression('/'.$entry15->concept.'/', $res->content);
461 $this->assertDoesNotMatchRegularExpression('/'.$entry21->concept.'/', $res->content);
462 $this->assertDoesNotMatchRegularExpression('/'.$entry22->concept.'/', $res->content);
463 $this->assertDoesNotMatchRegularExpression('/'.$entry23->concept.'/', $res->content);
464 $this->assertEmpty($res->nextpageurl);
466 // User cannot see unapproved entries unless he is an author.
467 $this->assertDoesNotMatchRegularExpression('/'.$entry16->concept.'/', $res->content);
468 $this->assertMatchesRegularExpression('/'.$entry17->concept.'/', $res->content);
471 public function test_glossary_get_entries_search() {
472 $this->resetAfterTest();
473 $this->setAdminUser();
474 // Turn on glossary autolinking (usedynalink).
475 set_config('glossary_linkentries', 1);
476 $glossarygenerator = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
477 $course = $this->getDataGenerator()->create_course();
478 $glossary = $this->getDataGenerator()->create_module('glossary', array('course' => $course->id));
479 // Note this entry is not case sensitive by default (casesensitive = 0).
480 $entry = $glossarygenerator->create_content($glossary);
481 // Check that a search for the concept return the entry.
482 $concept = $entry->concept;
483 $search = glossary_get_entries_search($concept, $course->id);
484 $this->assertCount(1, $search);
485 $foundentry = array_shift($search);
486 $this->assertEquals($foundentry->concept, $entry->concept);
487 // Now try the same search but with a lowercase term.
488 $concept = strtolower($entry->concept);
489 $search = glossary_get_entries_search($concept, $course->id);
490 $this->assertCount(1, $search);
491 $foundentry = array_shift($search);
492 $this->assertEquals($foundentry->concept, $entry->concept);
494 // Make an entry that is case sensitive (casesensitive = 1).
495 set_config('glossary_casesensitive', 1);
496 $entry = $glossarygenerator->create_content($glossary);
497 $concept = $entry->concept;
498 $search = glossary_get_entries_search($concept, $course->id);
499 $this->assertCount(1, $search);
500 $foundentry = array_shift($search);
501 $this->assertEquals($foundentry->concept, $entry->concept);
502 // Now try the same search but with a lowercase term.
503 $concept = strtolower($entry->concept);
504 $search = glossary_get_entries_search($concept, $course->id);
505 $this->assertCount(0, $search);
508 public function test_mod_glossary_can_delete_entry_users() {
509 $this->resetAfterTest();
511 // Create required data.
512 $course = $this->getDataGenerator()->create_course();
513 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
514 $anotherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
515 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
516 $glossary = $this->getDataGenerator()->create_module('glossary', ['course' => $course->id]);
518 $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
519 $this->setUser($student);
520 $entry = $gg->create_content($glossary);
521 $context = \context_module::instance($glossary->cmid);
523 // Test student can delete.
524 $this->assertTrue(mod_glossary_can_delete_entry($entry, $glossary, $context));
526 // Test teacher can delete.
527 $this->setUser($teacher);
528 $this->assertTrue(mod_glossary_can_delete_entry($entry, $glossary, $context));
530 // Test admin can delete.
531 $this->setAdminUser();
532 $this->assertTrue(mod_glossary_can_delete_entry($entry, $glossary, $context));
534 // Test a different student is not able to delete.
535 $this->setUser($anotherstudent);
536 $this->assertFalse(mod_glossary_can_delete_entry($entry, $glossary, $context));
538 // Test exception.
539 $this->expectExceptionMessage(get_string('nopermissiontodelentry', 'error'));
540 mod_glossary_can_delete_entry($entry, $glossary, $context, false);
543 public function test_mod_glossary_can_delete_entry_edit_period() {
544 global $CFG;
545 $this->resetAfterTest();
547 // Create required data.
548 $course = $this->getDataGenerator()->create_course();
549 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
550 $glossary = $this->getDataGenerator()->create_module('glossary', ['course' => $course->id, 'editalways' => 1]);
552 $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
553 $this->setUser($student);
554 $entry = $gg->create_content($glossary);
555 $context = \context_module::instance($glossary->cmid);
557 // Test student can always delete when edit always is set to 1.
558 $entry->timecreated = time() - 2 * $CFG->maxeditingtime;
559 $this->assertTrue(mod_glossary_can_delete_entry($entry, $glossary, $context));
561 // Test student cannot delete old entries when edit always is set to 0.
562 $glossary->editalways = 0;
563 $this->assertFalse(mod_glossary_can_delete_entry($entry, $glossary, $context));
565 // Test student can delete recent entries when edit always is set to 0.
566 $entry->timecreated = time();
567 $this->assertTrue(mod_glossary_can_delete_entry($entry, $glossary, $context));
569 // Check exception.
570 $entry->timecreated = time() - 2 * $CFG->maxeditingtime;
571 $this->expectExceptionMessage(get_string('errdeltimeexpired', 'glossary'));
572 mod_glossary_can_delete_entry($entry, $glossary, $context, false);
575 public function test_mod_glossary_delete_entry() {
576 global $DB, $CFG;
577 $this->resetAfterTest();
578 require_once($CFG->dirroot . '/rating/lib.php');
580 // Create required data.
581 $course = $this->getDataGenerator()->create_course();
582 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
583 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
585 $record = new \stdClass();
586 $record->course = $course->id;
587 $record->assessed = RATING_AGGREGATE_AVERAGE;
588 $scale = $this->getDataGenerator()->create_scale(['scale' => 'A,B,C,D']);
589 $record->scale = "-$scale->id";
590 $glossary = $this->getDataGenerator()->create_module('glossary', $record);
591 $context = \context_module::instance($glossary->cmid);
592 $cm = get_coursemodule_from_instance('glossary', $glossary->id);
594 $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
595 $this->setUser($student1);
597 // Create entry with tags and rating.
598 $entry = $gg->create_content(
599 $glossary,
600 ['approved' => 1, 'userid' => $student1->id, 'tags' => ['Cats', 'Dogs']],
601 ['alias1', 'alias2']
604 // Rate the entry as user2.
605 $rating1 = new \stdClass();
606 $rating1->contextid = $context->id;
607 $rating1->component = 'mod_glossary';
608 $rating1->ratingarea = 'entry';
609 $rating1->itemid = $entry->id;
610 $rating1->rating = 1; // 1 is A.
611 $rating1->scaleid = "-$scale->id";
612 $rating1->userid = $student2->id;
613 $rating1->timecreated = time();
614 $rating1->timemodified = time();
615 $rating1->id = $DB->insert_record('rating', $rating1);
617 $sink = $this->redirectEvents();
618 mod_glossary_delete_entry(fullclone($entry), $glossary, $cm, $context, $course);
619 $events = $sink->get_events();
620 $event = array_pop($events);
622 // Check events.
623 $this->assertEquals('\mod_glossary\event\entry_deleted', $event->eventname);
624 $this->assertEquals($entry->id, $event->objectid);
625 $sink->close();
627 // No entry, no alias, no ratings, no tags.
628 $this->assertEquals(0, $DB->count_records('glossary_entries', ['id' => $entry->id]));
629 $this->assertEquals(0, $DB->count_records('glossary_alias', ['entryid' => $entry->id]));
630 $this->assertEquals(0, $DB->count_records('rating', ['component' => 'mod_glossary', 'itemid' => $entry->id]));
631 $this->assertEmpty(\core_tag_tag::get_by_name(0, 'Cats'));
634 public function test_mod_glossary_delete_entry_imported() {
635 global $DB;
636 $this->resetAfterTest();
638 // Create required data.
639 $course = $this->getDataGenerator()->create_course();
640 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
642 $glossary1 = $this->getDataGenerator()->create_module('glossary', ['course' => $course->id]);
643 $glossary2 = $this->getDataGenerator()->create_module('glossary', ['course' => $course->id]);
645 $context = \context_module::instance($glossary2->cmid);
646 $cm = get_coursemodule_from_instance('glossary', $glossary2->id);
648 $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
649 $this->setUser($student);
651 $entry1 = $gg->create_content($glossary1);
652 $entry2 = $gg->create_content(
653 $glossary2,
654 ['approved' => 1, 'userid' => $student->id, 'sourceglossaryid' => $glossary1->id, 'tags' => ['Cats', 'Dogs']]
657 $sink = $this->redirectEvents();
658 mod_glossary_delete_entry(fullclone($entry2), $glossary2, $cm, $context, $course);
659 $events = $sink->get_events();
660 $event = array_pop($events);
662 // Check events.
663 $this->assertEquals('\mod_glossary\event\entry_deleted', $event->eventname);
664 $this->assertEquals($entry2->id, $event->objectid);
665 $sink->close();
667 // Check source.
668 $this->assertEquals(0, $DB->get_field('glossary_entries', 'sourceglossaryid', ['id' => $entry2->id]));
669 $this->assertEquals($glossary1->id, $DB->get_field('glossary_entries', 'glossaryid', ['id' => $entry2->id]));
671 // Tags.
672 $this->assertEmpty(\core_tag_tag::get_by_name(0, 'Cats'));
675 public function test_mod_glossary_can_update_entry_users() {
676 $this->resetAfterTest();
678 // Create required data.
679 $course = $this->getDataGenerator()->create_course();
680 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
681 $anotherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
682 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
683 $glossary = $this->getDataGenerator()->create_module('glossary', array('course' => $course->id));
685 $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
686 $this->setUser($student);
687 $entry = $gg->create_content($glossary);
688 $context = \context_module::instance($glossary->cmid);
689 $cm = get_coursemodule_from_instance('glossary', $glossary->id);
691 // Test student can update.
692 $this->assertTrue(mod_glossary_can_update_entry($entry, $glossary, $context, $cm));
694 // Test teacher can update.
695 $this->setUser($teacher);
696 $this->assertTrue(mod_glossary_can_update_entry($entry, $glossary, $context, $cm));
698 // Test admin can update.
699 $this->setAdminUser();
700 $this->assertTrue(mod_glossary_can_update_entry($entry, $glossary, $context, $cm));
702 // Test a different student is not able to update.
703 $this->setUser($anotherstudent);
704 $this->assertFalse(mod_glossary_can_update_entry($entry, $glossary, $context, $cm));
706 // Test exception.
707 $this->expectExceptionMessage(get_string('errcannoteditothers', 'glossary'));
708 mod_glossary_can_update_entry($entry, $glossary, $context, $cm, false);
711 public function test_mod_glossary_can_update_entry_edit_period() {
712 global $CFG;
713 $this->resetAfterTest();
715 // Create required data.
716 $course = $this->getDataGenerator()->create_course();
717 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
718 $glossary = $this->getDataGenerator()->create_module('glossary', array('course' => $course->id, 'editalways' => 1));
720 $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
721 $this->setUser($student);
722 $entry = $gg->create_content($glossary);
723 $context = \context_module::instance($glossary->cmid);
724 $cm = get_coursemodule_from_instance('glossary', $glossary->id);
726 // Test student can always update when edit always is set to 1.
727 $entry->timecreated = time() - 2 * $CFG->maxeditingtime;
728 $this->assertTrue(mod_glossary_can_update_entry($entry, $glossary, $context, $cm));
730 // Test student cannot update old entries when edit always is set to 0.
731 $glossary->editalways = 0;
732 $this->assertFalse(mod_glossary_can_update_entry($entry, $glossary, $context, $cm));
734 // Test student can update recent entries when edit always is set to 0.
735 $entry->timecreated = time();
736 $this->assertTrue(mod_glossary_can_update_entry($entry, $glossary, $context, $cm));
738 // Check exception.
739 $entry->timecreated = time() - 2 * $CFG->maxeditingtime;
740 $this->expectExceptionMessage(get_string('erredittimeexpired', 'glossary'));
741 mod_glossary_can_update_entry($entry, $glossary, $context, $cm, false);
744 public function test_prepare_entry_for_edition() {
745 global $USER;
746 $this->resetAfterTest(true);
748 $course = $this->getDataGenerator()->create_course();
749 $glossary = $this->getDataGenerator()->create_module('glossary', ['course' => $course->id]);
750 $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
752 $this->setAdminUser();
753 $aliases = ['alias1', 'alias2'];
754 $entry = $gg->create_content(
755 $glossary,
756 ['approved' => 1, 'userid' => $USER->id],
757 $aliases
760 $cat1 = $gg->create_category($glossary, [], [$entry]);
761 $gg->create_category($glossary);
763 $entry = mod_glossary_prepare_entry_for_edition($entry);
764 $this->assertCount(1, $entry->categories);
765 $this->assertEquals($cat1->id, $entry->categories[0]);
766 $returnedaliases = array_values(explode("\n", trim($entry->aliases)));
767 sort($returnedaliases);
768 $this->assertEquals($aliases, $returnedaliases);