Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

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