Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 311 and 403] [Versions 400 and 403] [Versions 401 and 403]

   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   * External glossary functions unit tests
  19   *
  20   * @package    mod_glossary
  21   * @category   external
  22   * @copyright  2015 Costantino Cito <ccito@cvaconsulting.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  namespace mod_glossary\external;
  27  
  28  use core_external\external_api;
  29  use core_external\util as external_util;
  30  use externallib_advanced_testcase;
  31  use mod_glossary_external;
  32  
  33  defined('MOODLE_INTERNAL') || die();
  34  global $CFG;
  35  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
  36  
  37  /**
  38   * External glossary functions unit tests
  39   *
  40   * @package    mod_glossary
  41   * @category   external
  42   * @copyright  2015 Costantino Cito <ccito@cvaconsulting.com>
  43   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  44   */
  45  class external_test extends externallib_advanced_testcase {
  46  
  47      /**
  48       * Test get_glossaries_by_courses
  49       */
  50      public function test_get_glossaries_by_courses() {
  51          $this->resetAfterTest(true);
  52  
  53          // As admin.
  54          $this->setAdminUser();
  55          $c1 = self::getDataGenerator()->create_course();
  56          $c2 = self::getDataGenerator()->create_course();
  57          $g1 = self::getDataGenerator()->create_module('glossary', array('course' => $c1->id, 'name' => 'First Glossary'));
  58          $g2 = self::getDataGenerator()->create_module('glossary', array('course' => $c1->id, 'name' => 'Second Glossary'));
  59          $g3 = self::getDataGenerator()->create_module('glossary', array('course' => $c2->id, 'name' => 'Third Glossary'));
  60  
  61          $s1 = $this->getDataGenerator()->create_user();
  62          self::getDataGenerator()->enrol_user($s1->id,  $c1->id);
  63  
  64          // Check results where student is enrolled.
  65          $this->setUser($s1);
  66          $glossaries = mod_glossary_external::get_glossaries_by_courses(array());
  67          $glossaries = external_api::clean_returnvalue(mod_glossary_external::get_glossaries_by_courses_returns(), $glossaries);
  68  
  69          $this->assertCount(2, $glossaries['glossaries']);
  70          $this->assertEquals('First Glossary', $glossaries['glossaries'][0]['name']);
  71          $this->assertEquals('Second Glossary', $glossaries['glossaries'][1]['name']);
  72          $this->assertEquals(1, $glossaries['glossaries'][0]['canaddentry']);
  73          $this->assertEquals(1, $glossaries['glossaries'][1]['canaddentry']);
  74  
  75          // Check results with specific course IDs.
  76          $glossaries = mod_glossary_external::get_glossaries_by_courses(array($c1->id, $c2->id));
  77          $glossaries = external_api::clean_returnvalue(mod_glossary_external::get_glossaries_by_courses_returns(), $glossaries);
  78  
  79          $this->assertCount(2, $glossaries['glossaries']);
  80          $this->assertEquals('First Glossary', $glossaries['glossaries'][0]['name']);
  81          $this->assertEquals('Second Glossary', $glossaries['glossaries'][1]['name']);
  82  
  83          $this->assertEquals('course', $glossaries['warnings'][0]['item']);
  84          $this->assertEquals($c2->id, $glossaries['warnings'][0]['itemid']);
  85          $this->assertEquals('1', $glossaries['warnings'][0]['warningcode']);
  86          $this->assertEquals(1, $glossaries['glossaries'][0]['canaddentry']);
  87  
  88          // Now as admin.
  89          $this->setAdminUser();
  90  
  91          $glossaries = mod_glossary_external::get_glossaries_by_courses(array($c2->id));
  92          $glossaries = external_api::clean_returnvalue(mod_glossary_external::get_glossaries_by_courses_returns(), $glossaries);
  93  
  94          $this->assertCount(1, $glossaries['glossaries']);
  95          $this->assertEquals('Third Glossary', $glossaries['glossaries'][0]['name']);
  96          $this->assertEquals(1, $glossaries['glossaries'][0]['canaddentry']);
  97      }
  98  
  99      public function test_view_glossary() {
 100          $this->resetAfterTest(true);
 101  
 102          // Generate all the things.
 103          $c1 = $this->getDataGenerator()->create_course();
 104          $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 105          $u1 = $this->getDataGenerator()->create_user();
 106          $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
 107  
 108          $sink = $this->redirectEvents();
 109          $this->setUser($u1);
 110          $return = mod_glossary_external::view_glossary($g1->id, 'letter');
 111          $return = external_api::clean_returnvalue(mod_glossary_external::view_glossary_returns(), $return);
 112          $events = $sink->get_events();
 113  
 114          // Assertion.
 115          $this->assertTrue($return['status']);
 116          $this->assertEmpty($return['warnings']);
 117          $this->assertCount(1, $events);
 118          $this->assertEquals('\mod_glossary\event\course_module_viewed', $events[0]->eventname);
 119          $sink->close();
 120      }
 121  
 122      public function test_view_glossary_without_permission() {
 123          $this->resetAfterTest(true);
 124  
 125          // Generate all the things.
 126          $c1 = $this->getDataGenerator()->create_course();
 127          $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 128          $u1 = $this->getDataGenerator()->create_user();
 129          $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
 130          $ctx = \context_module::instance($g1->cmid);
 131  
 132          // Revoke permission.
 133          $roles = get_archetype_roles('user');
 134          $role = array_shift($roles);
 135          assign_capability('mod/glossary:view', CAP_PROHIBIT, $role->id, $ctx, true);
 136          accesslib_clear_all_caches_for_unit_testing();
 137  
 138          // Assertion.
 139          $this->setUser($u1);
 140          $this->expectException(\require_login_exception::class);
 141          $this->expectExceptionMessage('Activity is hidden');
 142          mod_glossary_external::view_glossary($g1->id, 'letter');
 143      }
 144  
 145      public function test_view_entry() {
 146          $this->resetAfterTest(true);
 147  
 148          // Generate all the things.
 149          $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
 150          $c1 = $this->getDataGenerator()->create_course();
 151          $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 152          $g2 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id, 'visible' => false));
 153          $u1 = $this->getDataGenerator()->create_user();
 154          $e1 = $gg->create_content($g1, array('approved' => 1));
 155          $e2 = $gg->create_content($g1, array('approved' => 0, 'userid' => $u1->id));
 156          $e3 = $gg->create_content($g1, array('approved' => 0, 'userid' => -1));
 157          $e4 = $gg->create_content($g2, array('approved' => 1));
 158          $ctx = \context_module::instance($g1->cmid);
 159          $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
 160          $this->setUser($u1);
 161  
 162          // Test readable entry.
 163          $sink = $this->redirectEvents();
 164          $return = mod_glossary_external::view_entry($e1->id);
 165          $return = external_api::clean_returnvalue(mod_glossary_external::view_entry_returns(), $return);
 166          $events = $sink->get_events();
 167          $this->assertTrue($return['status']);
 168          $this->assertEmpty($return['warnings']);
 169          $this->assertCount(1, $events);
 170          $this->assertEquals('\mod_glossary\event\entry_viewed', $events[0]->eventname);
 171          $sink->close();
 172  
 173          // Test non-approved of self.
 174          $return = mod_glossary_external::view_entry($e2->id);
 175          $return = external_api::clean_returnvalue(mod_glossary_external::view_entry_returns(), $return);
 176          $events = $sink->get_events();
 177          $this->assertTrue($return['status']);
 178          $this->assertEmpty($return['warnings']);
 179          $this->assertCount(1, $events);
 180          $this->assertEquals('\mod_glossary\event\entry_viewed', $events[0]->eventname);
 181          $sink->close();
 182  
 183          // Test non-approved of other.
 184          try {
 185              mod_glossary_external::view_entry($e3->id);
 186              $this->fail('Cannot view non-approved entries of others.');
 187          } catch (\invalid_parameter_exception $e) {
 188              // All good.
 189          }
 190  
 191          // Test non-readable entry.
 192          $this->expectException(\require_login_exception::class);
 193          $this->expectExceptionMessage('Activity is hidden');
 194          mod_glossary_external::view_entry($e4->id);
 195      }
 196  
 197      public function test_get_entries_by_letter() {
 198          $this->resetAfterTest(true);
 199  
 200          // Generate all the things.
 201          $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
 202          $c1 = $this->getDataGenerator()->create_course();
 203          $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 204          $g2 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 205          $u1 = $this->getDataGenerator()->create_user();
 206          $ctx = \context_module::instance($g1->cmid);
 207          $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
 208  
 209          $e1a = $gg->create_content($g1, array('approved' => 0, 'concept' => 'Bob', 'userid' => 2, 'tags' => array('Cats', 'Dogs')));
 210          $e1b = $gg->create_content($g1, array('approved' => 1, 'concept' => 'Jane', 'userid' => 2));
 211          $e1c = $gg->create_content($g1, array('approved' => 1, 'concept' => 'Alice', 'userid' => $u1->id));
 212          $e1d = $gg->create_content($g1, array('approved' => 0, 'concept' => '0-day', 'userid' => $u1->id));
 213          $e2a = $gg->create_content($g2);
 214  
 215          $this->setAdminUser();
 216  
 217          // Just a normal request from admin user.
 218          $return = mod_glossary_external::get_entries_by_letter($g1->id, 'ALL', 0, 20, array());
 219          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_letter_returns(), $return);
 220          $this->assertCount(3, $return['entries']);
 221          $this->assertEquals(3, $return['count']);
 222          $this->assertEquals($e1c->id, $return['entries'][0]['id']);
 223          $this->assertEquals($e1a->id, $return['entries'][1]['id']);
 224          $this->assertEquals('Cats', $return['entries'][1]['tags'][0]['rawname']);
 225          $this->assertEquals('Dogs', $return['entries'][1]['tags'][1]['rawname']);
 226          $this->assertEquals($e1b->id, $return['entries'][2]['id']);
 227  
 228          // An admin user requesting all the entries.
 229          $return = mod_glossary_external::get_entries_by_letter($g1->id, 'ALL', 0, 20, array('includenotapproved' => 1));
 230          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_letter_returns(), $return);
 231          $this->assertCount(4, $return['entries']);
 232          $this->assertEquals(4, $return['count']);
 233          $this->assertEquals($e1d->id, $return['entries'][0]['id']);
 234          $this->assertEquals($e1c->id, $return['entries'][1]['id']);
 235          $this->assertEquals($e1a->id, $return['entries'][2]['id']);
 236          $this->assertEquals($e1b->id, $return['entries'][3]['id']);
 237  
 238          // A normal user.
 239          $this->setUser($u1);
 240          $return = mod_glossary_external::get_entries_by_letter($g1->id, 'ALL', 0, 20, array());
 241          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_letter_returns(), $return);
 242          $this->assertCount(3, $return['entries']);
 243          $this->assertEquals(3, $return['count']);
 244          $this->assertEquals($e1d->id, $return['entries'][0]['id']);
 245          $this->assertEquals($e1c->id, $return['entries'][1]['id']);
 246          $this->assertEquals($e1b->id, $return['entries'][2]['id']);
 247  
 248          // A normal user requesting to view all non approved entries.
 249          $return = mod_glossary_external::get_entries_by_letter($g1->id, 'ALL', 0, 20, array('includenotapproved' => 1));
 250          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_letter_returns(), $return);
 251          $this->assertCount(3, $return['entries']);
 252          $this->assertEquals(3, $return['count']);
 253          $this->assertEquals($e1d->id, $return['entries'][0]['id']);
 254          $this->assertEquals($e1c->id, $return['entries'][1]['id']);
 255          $this->assertEquals($e1b->id, $return['entries'][2]['id']);
 256      }
 257  
 258      public function test_get_entries_by_letter_with_parameters() {
 259          $this->resetAfterTest(true);
 260  
 261          // Generate all the things.
 262          $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
 263          $c1 = $this->getDataGenerator()->create_course();
 264          $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 265          $u1 = $this->getDataGenerator()->create_user();
 266          $ctx = \context_module::instance($g1->cmid);
 267          $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
 268  
 269          $e1a = $gg->create_content($g1, array('approved' => 1, 'concept' => '0-day', 'userid' => $u1->id));
 270          $e1b = $gg->create_content($g1, array('approved' => 1, 'concept' => 'Bob', 'userid' => 2));
 271          $e1c = $gg->create_content($g1, array('approved' => 1, 'concept' => '1-dayb', 'userid' => $u1->id));
 272  
 273          $this->setUser($u1);
 274  
 275          // Requesting a single letter.
 276          $return = mod_glossary_external::get_entries_by_letter($g1->id, 'b', 0, 20, array());
 277          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_letter_returns(), $return);
 278          $this->assertCount(1, $return['entries']);
 279          $this->assertEquals(1, $return['count']);
 280          $this->assertEquals($e1b->id, $return['entries'][0]['id']);
 281  
 282          // Requesting special letters.
 283          $return = mod_glossary_external::get_entries_by_letter($g1->id, 'SPECIAL', 0, 20, array());
 284          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_letter_returns(), $return);
 285          $this->assertCount(2, $return['entries']);
 286          $this->assertEquals(2, $return['count']);
 287          $this->assertEquals($e1a->id, $return['entries'][0]['id']);
 288          $this->assertEquals($e1c->id, $return['entries'][1]['id']);
 289  
 290          // Requesting with limit.
 291          $return = mod_glossary_external::get_entries_by_letter($g1->id, 'ALL', 0, 1, array());
 292          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_letter_returns(), $return);
 293          $this->assertCount(1, $return['entries']);
 294          $this->assertEquals(3, $return['count']);
 295          $this->assertEquals($e1a->id, $return['entries'][0]['id']);
 296          $return = mod_glossary_external::get_entries_by_letter($g1->id, 'ALL', 1, 2, array());
 297          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_letter_returns(), $return);
 298          $this->assertCount(2, $return['entries']);
 299          $this->assertEquals(3, $return['count']);
 300          $this->assertEquals($e1c->id, $return['entries'][0]['id']);
 301          $this->assertEquals($e1b->id, $return['entries'][1]['id']);
 302      }
 303  
 304      public function test_get_entries_by_date() {
 305          global $DB;
 306          $this->resetAfterTest(true);
 307  
 308          // Generate all the things.
 309          $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
 310          $c1 = $this->getDataGenerator()->create_course();
 311          $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id, 'displayformat' => 'entrylist'));
 312          $g2 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 313          $u1 = $this->getDataGenerator()->create_user();
 314          $ctx = \context_module::instance($g1->cmid);
 315          $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
 316  
 317          $now = time();
 318          $e1a = $gg->create_content($g1, array('approved' => 1, 'concept' => 'Bob', 'userid' => $u1->id,
 319              'timecreated' => 1, 'timemodified' => $now + 3600, 'tags' => array('Cats', 'Dogs')));
 320          $e1b = $gg->create_content($g1, array('approved' => 1, 'concept' => 'Jane', 'userid' => $u1->id,
 321              'timecreated' => $now + 3600, 'timemodified' => 1));
 322          $e1c = $gg->create_content($g1, array('approved' => 1, 'concept' => 'Alice', 'userid' => $u1->id,
 323              'timecreated' => $now + 1, 'timemodified' => $now + 1));
 324          $e1d = $gg->create_content($g1, array('approved' => 0, 'concept' => '0-day', 'userid' => $u1->id,
 325              'timecreated' => $now + 2, 'timemodified' => $now + 2));
 326          $e2a = $gg->create_content($g2);
 327  
 328          $this->setAdminUser($u1);
 329  
 330          // Ordering by time modified descending.
 331          $return = mod_glossary_external::get_entries_by_date($g1->id, 'UPDATE', 'DESC', 0, 20, array());
 332          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_date_returns(), $return);
 333          $this->assertCount(3, $return['entries']);
 334          $this->assertEquals(3, $return['count']);
 335          $this->assertEquals($e1a->id, $return['entries'][0]['id']);
 336          $this->assertEquals('Cats', $return['entries'][0]['tags'][0]['rawname']);
 337          $this->assertEquals('Dogs', $return['entries'][0]['tags'][1]['rawname']);
 338          $this->assertEquals($e1c->id, $return['entries'][1]['id']);
 339          $this->assertEquals($e1b->id, $return['entries'][2]['id']);
 340  
 341          // Ordering by time modified ascending.
 342          $return = mod_glossary_external::get_entries_by_date($g1->id, 'UPDATE', 'ASC', 0, 20, array());
 343          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_date_returns(), $return);
 344          $this->assertCount(3, $return['entries']);
 345          $this->assertEquals(3, $return['count']);
 346          $this->assertEquals($e1b->id, $return['entries'][0]['id']);
 347          $this->assertEquals($e1c->id, $return['entries'][1]['id']);
 348          $this->assertEquals($e1a->id, $return['entries'][2]['id']);
 349  
 350          // Ordering by time created asc.
 351          $return = mod_glossary_external::get_entries_by_date($g1->id, 'CREATION', 'ASC', 0, 20, array());
 352          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_date_returns(), $return);
 353          $this->assertCount(3, $return['entries']);
 354          $this->assertEquals(3, $return['count']);
 355          $this->assertEquals($e1a->id, $return['entries'][0]['id']);
 356          $this->assertEquals($e1c->id, $return['entries'][1]['id']);
 357          $this->assertEquals($e1b->id, $return['entries'][2]['id']);
 358  
 359          // Ordering by time created descending.
 360          $return = mod_glossary_external::get_entries_by_date($g1->id, 'CREATION', 'DESC', 0, 20, array());
 361          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_date_returns(), $return);
 362          $this->assertCount(3, $return['entries']);
 363          $this->assertEquals(3, $return['count']);
 364          $this->assertEquals($e1b->id, $return['entries'][0]['id']);
 365          $this->assertEquals($e1c->id, $return['entries'][1]['id']);
 366          $this->assertEquals($e1a->id, $return['entries'][2]['id']);
 367  
 368          // Ordering including to approve.
 369          $return = mod_glossary_external::get_entries_by_date($g1->id, 'CREATION', 'ASC', 0, 20,
 370              array('includenotapproved' => true));
 371          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_date_returns(), $return);
 372          $this->assertCount(4, $return['entries']);
 373          $this->assertEquals(4, $return['count']);
 374          $this->assertEquals($e1a->id, $return['entries'][0]['id']);
 375          $this->assertEquals($e1c->id, $return['entries'][1]['id']);
 376          $this->assertEquals($e1d->id, $return['entries'][2]['id']);
 377          $this->assertEquals($e1b->id, $return['entries'][3]['id']);
 378  
 379          // Ordering including to approve and pagination.
 380          $return = mod_glossary_external::get_entries_by_date($g1->id, 'CREATION', 'ASC', 0, 2,
 381              array('includenotapproved' => true));
 382          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_date_returns(), $return);
 383          $this->assertCount(2, $return['entries']);
 384          $this->assertEquals(4, $return['count']);
 385          $this->assertEquals($e1a->id, $return['entries'][0]['id']);
 386          $this->assertEquals($e1c->id, $return['entries'][1]['id']);
 387          $return = mod_glossary_external::get_entries_by_date($g1->id, 'CREATION', 'ASC', 2, 2,
 388              array('includenotapproved' => true));
 389          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_date_returns(), $return);
 390          $this->assertCount(2, $return['entries']);
 391          $this->assertEquals(4, $return['count']);
 392          $this->assertEquals($e1d->id, $return['entries'][0]['id']);
 393          $this->assertEquals($e1b->id, $return['entries'][1]['id']);
 394      }
 395  
 396      public function test_get_categories() {
 397          $this->resetAfterTest(true);
 398          $this->setAdminUser();
 399  
 400          $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
 401          $c1 = $this->getDataGenerator()->create_course();
 402          $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 403          $g2 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 404          $cat1a = $gg->create_category($g1);
 405          $cat1b = $gg->create_category($g1);
 406          $cat1c = $gg->create_category($g1);
 407          $cat2a = $gg->create_category($g2);
 408  
 409          $return = mod_glossary_external::get_categories($g1->id, 0, 20);
 410          $return = external_api::clean_returnvalue(mod_glossary_external::get_categories_returns(), $return);
 411          $this->assertCount(3, $return['categories']);
 412          $this->assertEquals(3, $return['count']);
 413          $this->assertEquals($cat1a->id, $return['categories'][0]['id']);
 414          $this->assertEquals($cat1b->id, $return['categories'][1]['id']);
 415          $this->assertEquals($cat1c->id, $return['categories'][2]['id']);
 416  
 417          $return = mod_glossary_external::get_categories($g1->id, 1, 2);
 418          $return = external_api::clean_returnvalue(mod_glossary_external::get_categories_returns(), $return);
 419          $this->assertCount(2, $return['categories']);
 420          $this->assertEquals(3, $return['count']);
 421          $this->assertEquals($cat1b->id, $return['categories'][0]['id']);
 422          $this->assertEquals($cat1c->id, $return['categories'][1]['id']);
 423      }
 424  
 425      public function test_get_entries_by_category() {
 426          $this->resetAfterTest(true);
 427  
 428          $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
 429          $c1 = $this->getDataGenerator()->create_course();
 430          $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id, 'displayformat' => 'entrylist'));
 431          $g2 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id, 'displayformat' => 'entrylist'));
 432          $u1 = $this->getDataGenerator()->create_user();
 433          $ctx = \context_module::instance($g1->cmid);
 434  
 435          $e1a1 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id, 'tags' => array('Cats', 'Dogs')));
 436          $e1a2 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id));
 437          $e1a3 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id));
 438          $e1b1 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id));
 439          $e1b2 = $gg->create_content($g1, array('approved' => 0, 'userid' => $u1->id));
 440          $e1x1 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id));
 441          $e1x2 = $gg->create_content($g1, array('approved' => 0, 'userid' => $u1->id));
 442          $e2a1 = $gg->create_content($g2, array('approved' => 1, 'userid' => $u1->id));
 443          $e2a2 = $gg->create_content($g2, array('approved' => 1, 'userid' => $u1->id));
 444  
 445          $cat1a = $gg->create_category($g1, array('name' => 'Fish'), array($e1a1, $e1a2, $e1a3));
 446          $cat1b = $gg->create_category($g1, array('name' => 'Cat'), array($e1b1, $e1b2));
 447          $cat1c = $gg->create_category($g1, array('name' => 'Zebra'), array($e1b1));   // Entry $e1b1 is in two categories.
 448          $cat2a = $gg->create_category($g2, array(), array($e2a1, $e2a2));
 449  
 450          $this->setAdminUser();
 451  
 452          // Browse one category.
 453          $return = mod_glossary_external::get_entries_by_category($g1->id, $cat1a->id, 0, 20, array());
 454          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_category_returns(), $return);
 455          $this->assertCount(3, $return['entries']);
 456          $this->assertEquals(3, $return['count']);
 457          $this->assertEquals($e1a1->id, $return['entries'][0]['id']);
 458          $this->assertEquals('Cats', $return['entries'][0]['tags'][0]['rawname']);
 459          $this->assertEquals('Dogs', $return['entries'][0]['tags'][1]['rawname']);
 460          $this->assertEquals($e1a2->id, $return['entries'][1]['id']);
 461          $this->assertEquals($e1a3->id, $return['entries'][2]['id']);
 462  
 463          // Browse all categories.
 464          $return = mod_glossary_external::get_entries_by_category($g1->id, GLOSSARY_SHOW_ALL_CATEGORIES, 0, 20, array());
 465          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_category_returns(), $return);
 466          $this->assertCount(5, $return['entries']);
 467          $this->assertEquals(5, $return['count']);
 468          $this->assertEquals($e1b1->id, $return['entries'][0]['id']);
 469          $this->assertEquals($e1a1->id, $return['entries'][1]['id']);
 470          $this->assertEquals($e1a2->id, $return['entries'][2]['id']);
 471          $this->assertEquals($e1a3->id, $return['entries'][3]['id']);
 472          $this->assertEquals($e1b1->id, $return['entries'][4]['id']);
 473  
 474          // Browse uncategorised.
 475          $return = mod_glossary_external::get_entries_by_category($g1->id, GLOSSARY_SHOW_NOT_CATEGORISED, 0, 20, array());
 476          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_category_returns(), $return);
 477          $this->assertCount(1, $return['entries']);
 478          $this->assertEquals(1, $return['count']);
 479          $this->assertEquals($e1x1->id, $return['entries'][0]['id']);
 480  
 481          // Including to approve.
 482          $return = mod_glossary_external::get_entries_by_category($g1->id, $cat1b->id, 0, 20,
 483              array('includenotapproved' => true));
 484          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_category_returns(), $return);
 485          $this->assertCount(2, $return['entries']);
 486          $this->assertEquals(2, $return['count']);
 487          $this->assertEquals($e1b1->id, $return['entries'][0]['id']);
 488          $this->assertEquals($e1b2->id, $return['entries'][1]['id']);
 489  
 490          // Using limit.
 491          $return = mod_glossary_external::get_entries_by_category($g1->id, GLOSSARY_SHOW_ALL_CATEGORIES, 0, 3,
 492              array('includenotapproved' => true));
 493          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_category_returns(), $return);
 494          $this->assertCount(3, $return['entries']);
 495          $this->assertEquals(6, $return['count']);
 496          $this->assertEquals($e1b1->id, $return['entries'][0]['id']);
 497          $this->assertEquals($e1b2->id, $return['entries'][1]['id']);
 498          $this->assertEquals($e1a1->id, $return['entries'][2]['id']);
 499          $return = mod_glossary_external::get_entries_by_category($g1->id, GLOSSARY_SHOW_ALL_CATEGORIES, 3, 2,
 500              array('includenotapproved' => true));
 501          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_category_returns(), $return);
 502          $this->assertCount(2, $return['entries']);
 503          $this->assertEquals(6, $return['count']);
 504          $this->assertEquals($e1a2->id, $return['entries'][0]['id']);
 505          $this->assertEquals($e1a3->id, $return['entries'][1]['id']);
 506      }
 507  
 508      public function test_get_authors() {
 509          $this->resetAfterTest(true);
 510  
 511          $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
 512          $c1 = $this->getDataGenerator()->create_course();
 513          $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 514          $g2 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 515  
 516          $u1 = $this->getDataGenerator()->create_user(array('lastname' => 'Upsilon'));
 517          $u2 = $this->getDataGenerator()->create_user(array('lastname' => 'Alpha'));
 518          $u3 = $this->getDataGenerator()->create_user(array('lastname' => 'Omega'));
 519  
 520          $ctx = \context_module::instance($g1->cmid);
 521  
 522          $e1a = $gg->create_content($g1, array('userid' => $u1->id, 'approved' => 1));
 523          $e1b = $gg->create_content($g1, array('userid' => $u1->id, 'approved' => 1));
 524          $e1c = $gg->create_content($g1, array('userid' => $u1->id, 'approved' => 1));
 525          $e2a = $gg->create_content($g1, array('userid' => $u2->id, 'approved' => 1));
 526          $e3a = $gg->create_content($g1, array('userid' => $u3->id, 'approved' => 0));
 527  
 528          $this->setAdminUser();
 529  
 530          // Simple request.
 531          $return = mod_glossary_external::get_authors($g1->id, 0, 20, array());
 532          $return = external_api::clean_returnvalue(mod_glossary_external::get_authors_returns(), $return);
 533          $this->assertCount(2, $return['authors']);
 534          $this->assertEquals(2, $return['count']);
 535          $this->assertEquals($u2->id, $return['authors'][0]['id']);
 536          $this->assertEquals($u1->id, $return['authors'][1]['id']);
 537  
 538          // Include users with entries pending approval.
 539          $return = mod_glossary_external::get_authors($g1->id, 0, 20, array('includenotapproved' => true));
 540          $return = external_api::clean_returnvalue(mod_glossary_external::get_authors_returns(), $return);
 541          $this->assertCount(3, $return['authors']);
 542          $this->assertEquals(3, $return['count']);
 543          $this->assertEquals($u2->id, $return['authors'][0]['id']);
 544          $this->assertEquals($u3->id, $return['authors'][1]['id']);
 545          $this->assertEquals($u1->id, $return['authors'][2]['id']);
 546  
 547          // Pagination.
 548          $return = mod_glossary_external::get_authors($g1->id, 1, 1, array('includenotapproved' => true));
 549          $return = external_api::clean_returnvalue(mod_glossary_external::get_authors_returns(), $return);
 550          $this->assertCount(1, $return['authors']);
 551          $this->assertEquals(3, $return['count']);
 552          $this->assertEquals($u3->id, $return['authors'][0]['id']);
 553      }
 554  
 555      public function test_get_entries_by_author() {
 556          $this->resetAfterTest(true);
 557  
 558          // Generate all the things.
 559          $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
 560          $c1 = $this->getDataGenerator()->create_course();
 561          $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id, 'displayformat' => 'entrylist'));
 562          $g2 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id, 'displayformat' => 'entrylist'));
 563          $u1 = $this->getDataGenerator()->create_user(array('lastname' => 'Upsilon', 'firstname' => 'Zac'));
 564          $u2 = $this->getDataGenerator()->create_user(array('lastname' => 'Ultra', 'firstname' => '1337'));
 565          $u3 = $this->getDataGenerator()->create_user(array('lastname' => 'Alpha', 'firstname' => 'Omega'));
 566          $u4 = $this->getDataGenerator()->create_user(array('lastname' => '0-day', 'firstname' => 'Zoe'));
 567          $ctx = \context_module::instance($g1->cmid);
 568          $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
 569  
 570          $e1a1 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id));
 571          $e1a2 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id));
 572          $e1a3 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id));
 573          $e1b1 = $gg->create_content($g1, array('approved' => 0, 'userid' => $u2->id));
 574          $e1b2 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u2->id, 'tags' => array('Cats', 'Dogs')));
 575          $e1c1 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u3->id));
 576          $e1d1 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u4->id));
 577          $e2a = $gg->create_content($g2, array('approved' => 1, 'userid' => $u1->id));
 578  
 579          $this->setUser($u1);
 580  
 581          // Requesting a single letter.
 582          $return = mod_glossary_external::get_entries_by_author($g1->id, 'u', 'LASTNAME', 'ASC', 0, 20, array());
 583          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_returns(), $return);
 584          $this->assertCount(4, $return['entries']);
 585          $this->assertEquals(4, $return['count']);
 586          $this->assertEquals($e1b2->id, $return['entries'][0]['id']);
 587          $this->assertEquals('Cats', $return['entries'][0]['tags'][0]['rawname']);
 588          $this->assertEquals('Dogs', $return['entries'][0]['tags'][1]['rawname']);
 589          $this->assertEquals($e1a1->id, $return['entries'][1]['id']);
 590          $this->assertEquals($e1a2->id, $return['entries'][2]['id']);
 591          $this->assertEquals($e1a3->id, $return['entries'][3]['id']);
 592  
 593          // Requesting special letters.
 594          $return = mod_glossary_external::get_entries_by_author($g1->id, 'SPECIAL', 'LASTNAME', 'ASC', 0, 20, array());
 595          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_returns(), $return);
 596          $this->assertCount(1, $return['entries']);
 597          $this->assertEquals(1, $return['count']);
 598          $this->assertEquals($e1d1->id, $return['entries'][0]['id']);
 599  
 600          // Requesting with limit.
 601          $return = mod_glossary_external::get_entries_by_author($g1->id, 'ALL', 'LASTNAME', 'ASC', 0, 1, array());
 602          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_returns(), $return);
 603          $this->assertCount(1, $return['entries']);
 604          $this->assertEquals(6, $return['count']);
 605          $this->assertEquals($e1d1->id, $return['entries'][0]['id']);
 606          $return = mod_glossary_external::get_entries_by_author($g1->id, 'ALL', 'LASTNAME', 'ASC', 1, 2, array());
 607          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_returns(), $return);
 608          $this->assertCount(2, $return['entries']);
 609          $this->assertEquals(6, $return['count']);
 610          $this->assertEquals($e1c1->id, $return['entries'][0]['id']);
 611          $this->assertEquals($e1b2->id, $return['entries'][1]['id']);
 612  
 613          // Including non-approved.
 614          $this->setAdminUser();
 615          $return = mod_glossary_external::get_entries_by_author($g1->id, 'ALL', 'LASTNAME', 'ASC', 0, 20,
 616              array('includenotapproved' => true));
 617          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_returns(), $return);
 618          $this->assertCount(7, $return['entries']);
 619          $this->assertEquals(7, $return['count']);
 620          $this->assertEquals($e1d1->id, $return['entries'][0]['id']);
 621          $this->assertEquals($e1c1->id, $return['entries'][1]['id']);
 622          $this->assertEquals($e1b1->id, $return['entries'][2]['id']);
 623          $this->assertEquals($e1b2->id, $return['entries'][3]['id']);
 624          $this->assertEquals($e1a1->id, $return['entries'][4]['id']);
 625          $this->assertEquals($e1a2->id, $return['entries'][5]['id']);
 626          $this->assertEquals($e1a3->id, $return['entries'][6]['id']);
 627  
 628          // Changing order.
 629          $return = mod_glossary_external::get_entries_by_author($g1->id, 'ALL', 'LASTNAME', 'DESC', 0, 1, array());
 630          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_returns(), $return);
 631          $this->assertCount(1, $return['entries']);
 632          $this->assertEquals(6, $return['count']);
 633          $this->assertEquals($e1a1->id, $return['entries'][0]['id']);
 634  
 635          // Sorting by firstname.
 636          $return = mod_glossary_external::get_entries_by_author($g1->id, 'ALL', 'FIRSTNAME', 'ASC', 0, 1, array());
 637          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_returns(), $return);
 638          $this->assertCount(1, $return['entries']);
 639          $this->assertEquals(6, $return['count']);
 640          $this->assertEquals($e1b2->id, $return['entries'][0]['id']);
 641  
 642          // Sorting by firstname descending.
 643          $return = mod_glossary_external::get_entries_by_author($g1->id, 'ALL', 'FIRSTNAME', 'DESC', 0, 1, array());
 644          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_returns(), $return);
 645          $this->assertCount(1, $return['entries']);
 646          $this->assertEquals(6, $return['count']);
 647          $this->assertEquals($e1d1->id, $return['entries'][0]['id']);
 648  
 649          // Filtering by firstname descending.
 650          $return = mod_glossary_external::get_entries_by_author($g1->id, 'z', 'FIRSTNAME', 'DESC', 0, 20, array());
 651          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_returns(), $return);
 652          $this->assertCount(4, $return['entries']);
 653          $this->assertEquals(4, $return['count']);
 654          $this->assertEquals($e1d1->id, $return['entries'][0]['id']);
 655          $this->assertEquals($e1a1->id, $return['entries'][1]['id']);
 656          $this->assertEquals($e1a2->id, $return['entries'][2]['id']);
 657          $this->assertEquals($e1a3->id, $return['entries'][3]['id']);
 658  
 659          // Test with a deleted user.
 660          delete_user($u2);
 661          $return = mod_glossary_external::get_entries_by_author($g1->id, 'u', 'LASTNAME', 'ASC', 0, 20, array());
 662          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_returns(), $return);
 663          $this->assertCount(4, $return['entries']);
 664          $this->assertEquals(4, $return['count']);
 665          $this->assertEquals($e1b2->id, $return['entries'][0]['id']);
 666          $this->assertEquals($e1a1->id, $return['entries'][1]['id']);
 667          $this->assertEquals($e1a2->id, $return['entries'][2]['id']);
 668          $this->assertEquals($e1a3->id, $return['entries'][3]['id']);
 669      }
 670  
 671      public function test_get_entries_by_author_id() {
 672          $this->resetAfterTest(true);
 673  
 674          // Generate all the things.
 675          $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
 676          $c1 = $this->getDataGenerator()->create_course();
 677          $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id, 'displayformat' => 'entrylist'));
 678          $g2 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id, 'displayformat' => 'entrylist'));
 679          $u1 = $this->getDataGenerator()->create_user(array('lastname' => 'Upsilon', 'firstname' => 'Zac'));
 680          $u2 = $this->getDataGenerator()->create_user(array('lastname' => 'Ultra', 'firstname' => '1337'));
 681          $u3 = $this->getDataGenerator()->create_user(array('lastname' => 'Alpha', 'firstname' => 'Omega'));
 682          $u4 = $this->getDataGenerator()->create_user(array('lastname' => '0-day', 'firstname' => 'Zoe'));
 683          $ctx = \context_module::instance($g1->cmid);
 684          $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
 685  
 686          $e1a1 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id, 'concept' => 'Zoom',
 687              'timecreated' => 3600, 'timemodified' => time() - 3600));
 688          $e1a2 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id, 'concept' => 'Alpha'));
 689          $e1a3 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id, 'concept' => 'Dog',
 690              'timecreated' => 1, 'timemodified' => time() - 1800));
 691          $e1a4 = $gg->create_content($g1, array('approved' => 0, 'userid' => $u1->id, 'concept' => 'Bird'));
 692          $e1b1 = $gg->create_content($g1, array('approved' => 0, 'userid' => $u2->id));
 693          $e2a = $gg->create_content($g2, array('approved' => 1, 'userid' => $u1->id));
 694  
 695          $this->setAdminUser();
 696  
 697          // Standard request.
 698          $return = mod_glossary_external::get_entries_by_author_id($g1->id, $u1->id, 'CONCEPT', 'ASC', 0, 20, array());
 699          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_id_returns(), $return);
 700          $this->assertCount(3, $return['entries']);
 701          $this->assertEquals(3, $return['count']);
 702          $this->assertEquals($e1a2->id, $return['entries'][0]['id']);
 703          $this->assertEquals($e1a3->id, $return['entries'][1]['id']);
 704          $this->assertEquals($e1a1->id, $return['entries'][2]['id']);
 705  
 706          // Standard request descending.
 707          $return = mod_glossary_external::get_entries_by_author_id($g1->id, $u1->id, 'CONCEPT', 'DESC', 0, 20, array());
 708          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_id_returns(), $return);
 709          $this->assertCount(3, $return['entries']);
 710          $this->assertEquals(3, $return['count']);
 711          $this->assertEquals($e1a1->id, $return['entries'][0]['id']);
 712          $this->assertEquals($e1a3->id, $return['entries'][1]['id']);
 713          $this->assertEquals($e1a2->id, $return['entries'][2]['id']);
 714  
 715          // Requesting ordering by time created.
 716          $return = mod_glossary_external::get_entries_by_author_id($g1->id, $u1->id, 'CREATION', 'ASC', 0, 20, array());
 717          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_id_returns(), $return);
 718          $this->assertCount(3, $return['entries']);
 719          $this->assertEquals(3, $return['count']);
 720          $this->assertEquals($e1a3->id, $return['entries'][0]['id']);
 721          $this->assertEquals($e1a1->id, $return['entries'][1]['id']);
 722          $this->assertEquals($e1a2->id, $return['entries'][2]['id']);
 723  
 724          // Requesting ordering by time created descending.
 725          $return = mod_glossary_external::get_entries_by_author_id($g1->id, $u1->id, 'CREATION', 'DESC', 0, 20, array());
 726          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_id_returns(), $return);
 727          $this->assertCount(3, $return['entries']);
 728          $this->assertEquals(3, $return['count']);
 729          $this->assertEquals($e1a2->id, $return['entries'][0]['id']);
 730          $this->assertEquals($e1a1->id, $return['entries'][1]['id']);
 731          $this->assertEquals($e1a3->id, $return['entries'][2]['id']);
 732  
 733          // Requesting ordering by time modified.
 734          $return = mod_glossary_external::get_entries_by_author_id($g1->id, $u1->id, 'UPDATE', 'ASC', 0, 20, array());
 735          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_id_returns(), $return);
 736          $this->assertCount(3, $return['entries']);
 737          $this->assertEquals(3, $return['count']);
 738          $this->assertEquals($e1a1->id, $return['entries'][0]['id']);
 739          $this->assertEquals($e1a3->id, $return['entries'][1]['id']);
 740          $this->assertEquals($e1a2->id, $return['entries'][2]['id']);
 741  
 742          // Requesting ordering by time modified descending.
 743          $return = mod_glossary_external::get_entries_by_author_id($g1->id, $u1->id, 'UPDATE', 'DESC', 0, 20, array());
 744          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_id_returns(), $return);
 745          $this->assertCount(3, $return['entries']);
 746          $this->assertEquals(3, $return['count']);
 747          $this->assertEquals($e1a2->id, $return['entries'][0]['id']);
 748          $this->assertEquals($e1a3->id, $return['entries'][1]['id']);
 749          $this->assertEquals($e1a1->id, $return['entries'][2]['id']);
 750  
 751          // Including non approved.
 752          $return = mod_glossary_external::get_entries_by_author_id($g1->id, $u1->id, 'CONCEPT', 'ASC', 0, 20,
 753              array('includenotapproved' => true));
 754          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_id_returns(), $return);
 755          $this->assertCount(4, $return['entries']);
 756          $this->assertEquals(4, $return['count']);
 757          $this->assertEquals($e1a2->id, $return['entries'][0]['id']);
 758          $this->assertEquals($e1a4->id, $return['entries'][1]['id']);
 759          $this->assertEquals($e1a3->id, $return['entries'][2]['id']);
 760          $this->assertEquals($e1a1->id, $return['entries'][3]['id']);
 761  
 762          // Pagination.
 763          $return = mod_glossary_external::get_entries_by_author_id($g1->id, $u1->id, 'CONCEPT', 'ASC', 0, 2,
 764              array('includenotapproved' => true));
 765          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_id_returns(), $return);
 766          $this->assertCount(2, $return['entries']);
 767          $this->assertEquals(4, $return['count']);
 768          $this->assertEquals($e1a2->id, $return['entries'][0]['id']);
 769          $this->assertEquals($e1a4->id, $return['entries'][1]['id']);
 770          $return = mod_glossary_external::get_entries_by_author_id($g1->id, $u1->id, 'CONCEPT', 'ASC', 1, 2,
 771              array('includenotapproved' => true));
 772          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_author_id_returns(), $return);
 773          $this->assertCount(2, $return['entries']);
 774          $this->assertEquals(4, $return['count']);
 775          $this->assertEquals($e1a4->id, $return['entries'][0]['id']);
 776          $this->assertEquals($e1a3->id, $return['entries'][1]['id']);
 777      }
 778  
 779      public function test_get_entries_by_search() {
 780          $this->resetAfterTest(true);
 781  
 782          // Generate all the things.
 783          $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
 784          $c1 = $this->getDataGenerator()->create_course();
 785          $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 786          $g2 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 787          $u1 = $this->getDataGenerator()->create_user();
 788          $ctx = \context_module::instance($g1->cmid);
 789          $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
 790          $this->setUser($u1);
 791  
 792          $e1 = $gg->create_content($g1, array('approved' => 1, 'concept' => 'House', 'timecreated' => time() + 3600));
 793          $e2 = $gg->create_content($g1, array('approved' => 1, 'concept' => 'Mouse', 'timemodified' => 1));
 794          $e3 = $gg->create_content($g1, array('approved' => 1, 'concept' => 'Hero', 'tags' => array('Cats', 'Dogs')));
 795          $e4 = $gg->create_content($g1, array('approved' => 0, 'concept' => 'Toulouse'));
 796          $e5 = $gg->create_content($g1, array('approved' => 1, 'definition' => 'Heroes', 'concept' => 'Abcd'));
 797          $e6 = $gg->create_content($g1, array('approved' => 0, 'definition' => 'When used for Heroes'));
 798          $e7 = $gg->create_content($g1, array('approved' => 1, 'timecreated' => 1, 'timemodified' => time() + 3600,
 799              'concept' => 'Z'), array('Couscous'));
 800          $e8 = $gg->create_content($g1, array('approved' => 0), array('Heroes'));
 801          $e9 = $gg->create_content($g2, array('approved' => 0));
 802  
 803          $this->setAdminUser();
 804  
 805          // Test simple query.
 806          $query = 'hero';
 807          $return = mod_glossary_external::get_entries_by_search($g1->id, $query, false, 'CONCEPT', 'ASC', 0, 20, array());
 808          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_search_returns(), $return);
 809          $this->assertCount(1, $return['entries']);
 810          $this->assertEquals(1, $return['count']);
 811          $this->assertEquals($e3->id, $return['entries'][0]['id']);
 812          $this->assertEquals('Cats', $return['entries'][0]['tags'][0]['rawname']);
 813          $this->assertEquals('Dogs', $return['entries'][0]['tags'][1]['rawname']);
 814  
 815          // Enabling full search.
 816          $query = 'hero';
 817          $return = mod_glossary_external::get_entries_by_search($g1->id, $query, true, 'CONCEPT', 'ASC', 0, 20, array());
 818          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_search_returns(), $return);
 819          $this->assertCount(2, $return['entries']);
 820          $this->assertEquals(2, $return['count']);
 821          $this->assertEquals($e5->id, $return['entries'][0]['id']);
 822          $this->assertEquals($e3->id, $return['entries'][1]['id']);
 823  
 824          // Concept descending.
 825          $query = 'hero';
 826          $return = mod_glossary_external::get_entries_by_search($g1->id, $query, true, 'CONCEPT', 'DESC', 0, 20, array());
 827          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_search_returns(), $return);
 828          $this->assertCount(2, $return['entries']);
 829          $this->assertEquals(2, $return['count']);
 830          $this->assertEquals($e3->id, $return['entries'][0]['id']);
 831          $this->assertEquals($e5->id, $return['entries'][1]['id']);
 832  
 833          // Search on alias.
 834          $query = 'couscous';
 835          $return = mod_glossary_external::get_entries_by_search($g1->id, $query, false, 'CONCEPT', 'ASC', 0, 20, array());
 836          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_search_returns(), $return);
 837          $this->assertCount(1, $return['entries']);
 838          $this->assertEquals(1, $return['count']);
 839          $this->assertEquals($e7->id, $return['entries'][0]['id']);
 840          $return = mod_glossary_external::get_entries_by_search($g1->id, $query, true, 'CONCEPT', 'ASC', 0, 20, array());
 841          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_search_returns(), $return);
 842          $this->assertCount(1, $return['entries']);
 843          $this->assertEquals(1, $return['count']);
 844          $this->assertEquals($e7->id, $return['entries'][0]['id']);
 845  
 846          // Pagination and ordering on created date.
 847          $query = 'ou';
 848          $return = mod_glossary_external::get_entries_by_search($g1->id, $query, false, 'CREATION', 'ASC', 0, 1, array());
 849          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_search_returns(), $return);
 850          $this->assertCount(1, $return['entries']);
 851          $this->assertEquals(3, $return['count']);
 852          $this->assertEquals($e7->id, $return['entries'][0]['id']);
 853          $return = mod_glossary_external::get_entries_by_search($g1->id, $query, false, 'CREATION', 'DESC', 0, 1, array());
 854          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_search_returns(), $return);
 855          $this->assertCount(1, $return['entries']);
 856          $this->assertEquals(3, $return['count']);
 857          $this->assertEquals($e1->id, $return['entries'][0]['id']);
 858  
 859          // Ordering on updated date.
 860          $query = 'ou';
 861          $return = mod_glossary_external::get_entries_by_search($g1->id, $query, false, 'UPDATE', 'ASC', 0, 1, array());
 862          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_search_returns(), $return);
 863          $this->assertCount(1, $return['entries']);
 864          $this->assertEquals(3, $return['count']);
 865          $this->assertEquals($e2->id, $return['entries'][0]['id']);
 866          $return = mod_glossary_external::get_entries_by_search($g1->id, $query, false, 'UPDATE', 'DESC', 0, 1, array());
 867          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_search_returns(), $return);
 868          $this->assertCount(1, $return['entries']);
 869          $this->assertEquals(3, $return['count']);
 870          $this->assertEquals($e7->id, $return['entries'][0]['id']);
 871  
 872          // Including not approved.
 873          $query = 'ou';
 874          $return = mod_glossary_external::get_entries_by_search($g1->id, $query, false, 'CONCEPT', 'ASC', 0, 20,
 875              array('includenotapproved' => true));
 876          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_search_returns(), $return);
 877          $this->assertCount(4, $return['entries']);
 878          $this->assertEquals(4, $return['count']);
 879          $this->assertEquals($e1->id, $return['entries'][0]['id']);
 880          $this->assertEquals($e2->id, $return['entries'][1]['id']);
 881          $this->assertEquals($e4->id, $return['entries'][2]['id']);
 882          $this->assertEquals($e7->id, $return['entries'][3]['id']);
 883  
 884          // Advanced query string.
 885          $query = '+Heroes -Abcd';
 886          $return = mod_glossary_external::get_entries_by_search($g1->id, $query, true, 'CONCEPT', 'ASC', 0, 20,
 887              array('includenotapproved' => true));
 888          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_search_returns(), $return);
 889          $this->assertCount(2, $return['entries']);
 890          $this->assertEquals(2, $return['count']);
 891          $this->assertEquals($e6->id, $return['entries'][0]['id']);
 892          $this->assertEquals($e8->id, $return['entries'][1]['id']);
 893      }
 894  
 895      public function test_get_entries_by_term() {
 896          $this->resetAfterTest(true);
 897  
 898          // Generate all the things.
 899          $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
 900          $c1 = $this->getDataGenerator()->create_course();
 901          $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 902          $g2 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 903          $u1 = $this->getDataGenerator()->create_user();
 904          $ctx = \context_module::instance($g1->cmid);
 905          $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
 906  
 907          $this->setAdminUser();
 908  
 909          $e1 = $gg->create_content($g1, array('userid' => $u1->id, 'approved' => 1, 'concept' => 'cat',
 910              'tags' => array('Cats', 'Dogs')));
 911          $e2 = $gg->create_content($g1, array('userid' => $u1->id, 'approved' => 1), array('cat', 'dog'));
 912          $e3 = $gg->create_content($g1, array('userid' => $u1->id, 'approved' => 1), array('dog'));
 913          $e4 = $gg->create_content($g1, array('userid' => $u1->id, 'approved' => 0, 'concept' => 'dog'));
 914          $e5 = $gg->create_content($g2, array('userid' => $u1->id, 'approved' => 1, 'concept' => 'dog'), array('cat'));
 915  
 916          // Search concept + alias.
 917          $return = mod_glossary_external::get_entries_by_term($g1->id, 'cat', 0, 20, array('includenotapproved' => false));
 918          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_term_returns(), $return);
 919          $this->assertCount(2, $return['entries']);
 920          $this->assertEquals(2, $return['count']);
 921          // Compare ids, ignore ordering of array, using canonicalize parameter of assertEquals.
 922          $expected = array($e1->id, $e2->id);
 923          $actual = array($return['entries'][0]['id'], $return['entries'][1]['id']);
 924          $this->assertEqualsCanonicalizing($expected, $actual);
 925          // Compare rawnames of all expected tags, ignore ordering of array, using canonicalize parameter of assertEquals.
 926          $expected = array('Cats', 'Dogs'); // Only $e1 has 2 tags.
 927          $actual = array(); // Accumulate all tags returned.
 928          foreach ($return['entries'] as $entry) {
 929              foreach ($entry['tags'] as $tag) {
 930                  $actual[] = $tag['rawname'];
 931              }
 932          }
 933          $this->assertEqualsCanonicalizing($expected, $actual);
 934  
 935          // Search alias.
 936          $return = mod_glossary_external::get_entries_by_term($g1->id, 'dog', 0, 20, array('includenotapproved' => false));
 937          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_term_returns(), $return);
 938  
 939          $this->assertCount(2, $return['entries']);
 940          $this->assertEquals(2, $return['count']);
 941          // Compare ids, ignore ordering of array, using canonicalize parameter of assertEquals.
 942          $expected = array($e2->id, $e3->id);
 943          $actual = array($return['entries'][0]['id'], $return['entries'][1]['id']);
 944          $this->assertEqualsCanonicalizing($expected, $actual);
 945  
 946          // Search including not approved.
 947          $return = mod_glossary_external::get_entries_by_term($g1->id, 'dog', 0, 20, array('includenotapproved' => true));
 948          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_term_returns(), $return);
 949          $this->assertCount(3, $return['entries']);
 950          $this->assertEquals(3, $return['count']);
 951          // Compare ids, ignore ordering of array, using canonicalize parameter of assertEquals.
 952          $expected = array($e4->id, $e2->id, $e3->id);
 953          $actual = array($return['entries'][0]['id'], $return['entries'][1]['id'], $return['entries'][2]['id']);
 954          $this->assertEqualsCanonicalizing($expected, $actual);
 955  
 956          // Pagination.
 957          $return = mod_glossary_external::get_entries_by_term($g1->id, 'dog', 0, 1, array('includenotapproved' => true));
 958          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_term_returns(), $return);
 959          $this->assertCount(1, $return['entries']);
 960          // We don't compare the returned entry id because it may be different depending on the DBMS,
 961          // for example, Postgres does a random sorting in this case.
 962          $this->assertEquals(3, $return['count']);
 963          $return = mod_glossary_external::get_entries_by_term($g1->id, 'dog', 1, 1, array('includenotapproved' => true));
 964          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_by_term_returns(), $return);
 965          $this->assertCount(1, $return['entries']);
 966          $this->assertEquals(3, $return['count']);
 967      }
 968  
 969      public function test_get_entries_to_approve() {
 970          $this->resetAfterTest(true);
 971  
 972          // Generate all the things.
 973          $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
 974          $c1 = $this->getDataGenerator()->create_course();
 975          $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 976          $g2 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
 977          $u1 = $this->getDataGenerator()->create_user();
 978          $ctx = \context_module::instance($g1->cmid);
 979          $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
 980  
 981          $e1a = $gg->create_content($g1, array('approved' => 0, 'concept' => 'Bob', 'userid' => $u1->id,
 982              'timecreated' => time() + 3600));
 983          $e1b = $gg->create_content($g1, array('approved' => 0, 'concept' => 'Jane', 'userid' => $u1->id, 'timecreated' => 1));
 984          $e1c = $gg->create_content($g1, array('approved' => 0, 'concept' => 'Alice', 'userid' => $u1->id, 'timemodified' => 1));
 985          $e1d = $gg->create_content($g1, array('approved' => 0, 'concept' => '0-day', 'userid' => $u1->id,
 986              'timemodified' => time() + 3600));
 987          $e1e = $gg->create_content($g1, array('approved' => 1, 'concept' => '1-day', 'userid' => $u1->id));
 988          $e2a = $gg->create_content($g2);
 989  
 990          $this->setAdminUser(true);
 991  
 992          // Simple listing.
 993          $return = mod_glossary_external::get_entries_to_approve($g1->id, 'ALL', 'CONCEPT', 'ASC', 0, 20);
 994          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_to_approve_returns(), $return);
 995          $this->assertCount(4, $return['entries']);
 996          $this->assertEquals(4, $return['count']);
 997          $this->assertEquals($e1d->id, $return['entries'][0]['id']);
 998          $this->assertEquals($e1c->id, $return['entries'][1]['id']);
 999          $this->assertEquals($e1a->id, $return['entries'][2]['id']);
1000          $this->assertEquals($e1b->id, $return['entries'][3]['id']);
1001  
1002          // Revert ordering of concept.
1003          $return = mod_glossary_external::get_entries_to_approve($g1->id, 'ALL', 'CONCEPT', 'DESC', 0, 20);
1004          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_to_approve_returns(), $return);
1005          $this->assertCount(4, $return['entries']);
1006          $this->assertEquals(4, $return['count']);
1007          $this->assertEquals($e1b->id, $return['entries'][0]['id']);
1008          $this->assertEquals($e1a->id, $return['entries'][1]['id']);
1009          $this->assertEquals($e1c->id, $return['entries'][2]['id']);
1010          $this->assertEquals($e1d->id, $return['entries'][3]['id']);
1011  
1012          // Filtering by letter.
1013          $return = mod_glossary_external::get_entries_to_approve($g1->id, 'a', 'CONCEPT', 'ASC', 0, 20);
1014          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_to_approve_returns(), $return);
1015          $this->assertCount(1, $return['entries']);
1016          $this->assertEquals(1, $return['count']);
1017          $this->assertEquals($e1c->id, $return['entries'][0]['id']);
1018  
1019          // Filtering by special.
1020          $return = mod_glossary_external::get_entries_to_approve($g1->id, 'SPECIAL', 'CONCEPT', 'ASC', 0, 20);
1021          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_to_approve_returns(), $return);
1022          $this->assertCount(1, $return['entries']);
1023          $this->assertEquals(1, $return['count']);
1024          $this->assertEquals($e1d->id, $return['entries'][0]['id']);
1025  
1026          // Pagination.
1027          $return = mod_glossary_external::get_entries_to_approve($g1->id, 'ALL', 'CONCEPT', 'ASC', 0, 2);
1028          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_to_approve_returns(), $return);
1029          $this->assertCount(2, $return['entries']);
1030          $this->assertEquals(4, $return['count']);
1031          $this->assertEquals($e1d->id, $return['entries'][0]['id']);
1032          $this->assertEquals($e1c->id, $return['entries'][1]['id']);
1033          $return = mod_glossary_external::get_entries_to_approve($g1->id, 'ALL', 'CONCEPT', 'ASC', 1, 2);
1034          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_to_approve_returns(), $return);
1035          $this->assertCount(2, $return['entries']);
1036          $this->assertEquals(4, $return['count']);
1037          $this->assertEquals($e1c->id, $return['entries'][0]['id']);
1038          $this->assertEquals($e1a->id, $return['entries'][1]['id']);
1039  
1040          // Ordering by creation date.
1041          $return = mod_glossary_external::get_entries_to_approve($g1->id, 'ALL', 'CREATION', 'ASC', 0, 1);
1042          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_to_approve_returns(), $return);
1043          $this->assertCount(1, $return['entries']);
1044          $this->assertEquals(4, $return['count']);
1045          $this->assertEquals($e1b->id, $return['entries'][0]['id']);
1046  
1047          // Ordering by creation date desc.
1048          $return = mod_glossary_external::get_entries_to_approve($g1->id, 'ALL', 'CREATION', 'DESC', 0, 1);
1049          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_to_approve_returns(), $return);
1050          $this->assertCount(1, $return['entries']);
1051          $this->assertEquals(4, $return['count']);
1052          $this->assertEquals($e1a->id, $return['entries'][0]['id']);
1053  
1054          // Ordering by update date.
1055          $return = mod_glossary_external::get_entries_to_approve($g1->id, 'ALL', 'UPDATE', 'ASC', 0, 1);
1056          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_to_approve_returns(), $return);
1057          $this->assertCount(1, $return['entries']);
1058          $this->assertEquals(4, $return['count']);
1059          $this->assertEquals($e1c->id, $return['entries'][0]['id']);
1060  
1061          // Ordering by update date desc.
1062          $return = mod_glossary_external::get_entries_to_approve($g1->id, 'ALL', 'UPDATE', 'DESC', 0, 1);
1063          $return = external_api::clean_returnvalue(mod_glossary_external::get_entries_to_approve_returns(), $return);
1064          $this->assertCount(1, $return['entries']);
1065          $this->assertEquals(4, $return['count']);
1066          $this->assertEquals($e1d->id, $return['entries'][0]['id']);
1067  
1068          // Permissions are checked.
1069          $this->setUser($u1);
1070          $this->expectException('required_capability_exception');
1071          mod_glossary_external::get_entries_to_approve($g1->id, 'ALL', 'CONCEPT', 'ASC', 0, 1);
1072          $this->fail('Do not test anything else after this.');
1073      }
1074  
1075      public function test_get_entry_by_id() {
1076          $this->resetAfterTest(true);
1077  
1078          // Generate all the things.
1079          $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
1080          $c1 = $this->getDataGenerator()->create_course();
1081          $c2 = $this->getDataGenerator()->create_course();
1082          $g1 = $this->getDataGenerator()->create_module('glossary', array('course' => $c1->id));
1083          $g2 = $this->getDataGenerator()->create_module('glossary', array('course' => $c2->id, 'visible' => 0));
1084          $u1 = $this->getDataGenerator()->create_user();
1085          $u2 = $this->getDataGenerator()->create_user();
1086          $u3 = $this->getDataGenerator()->create_user();
1087          $ctx = \context_module::instance($g1->cmid);
1088          $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
1089          $this->getDataGenerator()->enrol_user($u2->id, $c1->id);
1090          $this->getDataGenerator()->enrol_user($u3->id, $c1->id);
1091  
1092          $e1 = $gg->create_content($g1, array('approved' => 1, 'userid' => $u1->id, 'tags' => array('Cats', 'Dogs')));
1093          // Add a fake inline image to the entry.
1094          $filename = 'shouldbeanimage.jpg';
1095          $filerecordinline = array(
1096              'contextid' => $ctx->id,
1097              'component' => 'mod_glossary',
1098              'filearea'  => 'entry',
1099              'itemid'    => $e1->id,
1100              'filepath'  => '/',
1101              'filename'  => $filename,
1102          );
1103          $fs = get_file_storage();
1104          $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
1105  
1106          $e2 = $gg->create_content($g1, array('approved' => 0, 'userid' => $u1->id));
1107          $e3 = $gg->create_content($g1, array('approved' => 0, 'userid' => $u2->id));
1108          $e4 = $gg->create_content($g2, array('approved' => 1));
1109  
1110          $this->setUser($u1);
1111          $return = mod_glossary_external::get_entry_by_id($e1->id);
1112          $return = external_api::clean_returnvalue(mod_glossary_external::get_entry_by_id_returns(), $return);
1113          $this->assertEquals($e1->id, $return['entry']['id']);
1114          $this->assertEquals('Cats', $return['entry']['tags'][0]['rawname']);
1115          $this->assertEquals('Dogs', $return['entry']['tags'][1]['rawname']);
1116          $this->assertEquals($filename, $return['entry']['definitioninlinefiles'][0]['filename']);
1117          $this->assertTrue($return['permissions']['candelete']);
1118  
1119          $return = mod_glossary_external::get_entry_by_id($e2->id);
1120          $return = external_api::clean_returnvalue(mod_glossary_external::get_entry_by_id_returns(), $return);
1121          $this->assertEquals($e2->id, $return['entry']['id']);
1122          $this->assertTrue($return['permissions']['candelete']);
1123  
1124          try {
1125              $return = mod_glossary_external::get_entry_by_id($e3->id);
1126              $this->fail('Cannot view unapproved entries of others.');
1127          } catch (\invalid_parameter_exception $e) {
1128              // All good.
1129          }
1130  
1131          try {
1132              $return = mod_glossary_external::get_entry_by_id($e4->id);
1133              $this->fail('Cannot view entries from another course.');
1134          } catch (\require_login_exception $e) {
1135              // All good.
1136          }
1137  
1138          // An admin can see other's entries to be approved.
1139          $this->setAdminUser();
1140          $return = mod_glossary_external::get_entry_by_id($e3->id);
1141          $return = external_api::clean_returnvalue(mod_glossary_external::get_entry_by_id_returns(), $return);
1142          $this->assertEquals($e3->id, $return['entry']['id']);
1143          $this->assertTrue($return['permissions']['candelete']);
1144  
1145          // Students can see other students approved entries but they will not be able to delete them.
1146          $this->setUser($u3);
1147          $return = mod_glossary_external::get_entry_by_id($e1->id);
1148          $return = external_api::clean_returnvalue(mod_glossary_external::get_entry_by_id_returns(), $return);
1149          $this->assertEquals($e1->id, $return['entry']['id']);
1150          $this->assertFalse($return['permissions']['candelete']);
1151      }
1152  
1153      public function test_add_entry_without_optional_settings() {
1154          global $CFG, $DB;
1155          $this->resetAfterTest(true);
1156  
1157          $course = $this->getDataGenerator()->create_course();
1158          $glossary = $this->getDataGenerator()->create_module('glossary', array('course' => $course->id));
1159  
1160          $this->setAdminUser();
1161          $concept = 'A concept';
1162          $definition = '<p>A definition</p>';
1163          $return = mod_glossary_external::add_entry($glossary->id, $concept, $definition, FORMAT_HTML);
1164          $return = external_api::clean_returnvalue(mod_glossary_external::add_entry_returns(), $return);
1165  
1166          // Get entry from DB.
1167          $entry = $DB->get_record('glossary_entries', array('id' => $return['entryid']));
1168  
1169          $this->assertEquals($concept, $entry->concept);
1170          $this->assertEquals($definition, $entry->definition);
1171          $this->assertEquals($CFG->glossary_linkentries, $entry->usedynalink);
1172          $this->assertEquals($CFG->glossary_casesensitive, $entry->casesensitive);
1173          $this->assertEquals($CFG->glossary_fullmatch, $entry->fullmatch);
1174          $this->assertEmpty($DB->get_records('glossary_alias', array('entryid' => $return['entryid'])));
1175          $this->assertEmpty($DB->get_records('glossary_entries_categories', array('entryid' => $return['entryid'])));
1176      }
1177  
1178      public function test_add_entry_with_aliases() {
1179          global $DB;
1180          $this->resetAfterTest(true);
1181  
1182          $course = $this->getDataGenerator()->create_course();
1183          $glossary = $this->getDataGenerator()->create_module('glossary', array('course' => $course->id));
1184  
1185          $this->setAdminUser();
1186          $concept = 'A concept';
1187          $definition = 'A definition';
1188          $paramaliases = 'abc, def, gez';
1189          $options = array(
1190              array(
1191                  'name' => 'aliases',
1192                  'value' => $paramaliases,
1193              )
1194          );
1195          $return = mod_glossary_external::add_entry($glossary->id, $concept, $definition, FORMAT_HTML, $options);
1196          $return = external_api::clean_returnvalue(mod_glossary_external::add_entry_returns(), $return);
1197  
1198          $aliases = $DB->get_records('glossary_alias', array('entryid' => $return['entryid']));
1199          $this->assertCount(3, $aliases);
1200          foreach ($aliases as $alias) {
1201              $this->assertStringContainsString($alias->alias, $paramaliases);
1202          }
1203      }
1204  
1205      public function test_add_entry_in_categories() {
1206          global $DB;
1207          $this->resetAfterTest(true);
1208  
1209          $course = $this->getDataGenerator()->create_course();
1210          $glossary = $this->getDataGenerator()->create_module('glossary', array('course' => $course->id));
1211          $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
1212          $cat1 = $gg->create_category($glossary);
1213          $cat2 = $gg->create_category($glossary);
1214  
1215          $this->setAdminUser();
1216          $concept = 'A concept';
1217          $definition = 'A definition';
1218          $paramcategories = "$cat1->id, $cat2->id";
1219          $options = array(
1220              array(
1221                  'name' => 'categories',
1222                  'value' => $paramcategories,
1223              )
1224          );
1225          $return = mod_glossary_external::add_entry($glossary->id, $concept, $definition, FORMAT_HTML, $options);
1226          $return = external_api::clean_returnvalue(mod_glossary_external::add_entry_returns(), $return);
1227  
1228          $categories = $DB->get_records('glossary_entries_categories', array('entryid' => $return['entryid']));
1229          $this->assertCount(2, $categories);
1230          foreach ($categories as $category) {
1231              $this->assertStringContainsString($category->categoryid, $paramcategories);
1232          }
1233      }
1234  
1235      public function test_add_entry_with_attachments() {
1236          global $DB, $USER;
1237          $this->resetAfterTest(true);
1238  
1239          $course = $this->getDataGenerator()->create_course();
1240          $glossary = $this->getDataGenerator()->create_module('glossary', array('course' => $course->id));
1241          $context = \context_module::instance($glossary->cmid);
1242  
1243          $this->setAdminUser();
1244          $concept = 'A concept';
1245          $definition = 'A definition';
1246  
1247          // Draft files.
1248          $draftidinlineattach = file_get_unused_draft_itemid();
1249          $draftidattach = file_get_unused_draft_itemid();
1250          $usercontext = \context_user::instance($USER->id);
1251          $filerecordinline = array(
1252              'contextid' => $usercontext->id,
1253              'component' => 'user',
1254              'filearea'  => 'draft',
1255              'itemid'    => $draftidinlineattach,
1256              'filepath'  => '/',
1257              'filename'  => 'shouldbeanimage.txt',
1258          );
1259          $fs = get_file_storage();
1260  
1261          // Create a file in a draft area for regular attachments.
1262          $filerecordattach = $filerecordinline;
1263          $attachfilename = 'attachment.txt';
1264          $filerecordattach['filename'] = $attachfilename;
1265          $filerecordattach['itemid'] = $draftidattach;
1266          $fs->create_file_from_string($filerecordinline, 'image contents (not really)');
1267          $fs->create_file_from_string($filerecordattach, 'simple text attachment');
1268  
1269          $options = array(
1270              array(
1271                  'name' => 'inlineattachmentsid',
1272                  'value' => $draftidinlineattach,
1273              ),
1274              array(
1275                  'name' => 'attachmentsid',
1276                  'value' => $draftidattach,
1277              )
1278          );
1279          $return = mod_glossary_external::add_entry($glossary->id, $concept, $definition, FORMAT_HTML, $options);
1280          $return = external_api::clean_returnvalue(mod_glossary_external::add_entry_returns(), $return);
1281  
1282          $editorfiles = external_util::get_area_files($context->id, 'mod_glossary', 'entry', $return['entryid']);
1283          $attachmentfiles = external_util::get_area_files($context->id, 'mod_glossary', 'attachment', $return['entryid']);
1284  
1285          $this->assertCount(1, $editorfiles);
1286          $this->assertCount(1, $attachmentfiles);
1287  
1288          $this->assertEquals('shouldbeanimage.txt', $editorfiles[0]['filename']);
1289          $this->assertEquals('attachment.txt', $attachmentfiles[0]['filename']);
1290      }
1291  
1292      /**
1293       *   Test get entry including rating information.
1294       */
1295      public function test_get_entry_rating_information() {
1296          $this->resetAfterTest(true);
1297  
1298          global $DB, $CFG;
1299          require_once($CFG->dirroot . '/rating/lib.php');
1300  
1301          $this->resetAfterTest(true);
1302  
1303          $user1 = self::getDataGenerator()->create_user();
1304          $user2 = self::getDataGenerator()->create_user();
1305          $user3 = self::getDataGenerator()->create_user();
1306          $teacher = self::getDataGenerator()->create_user();
1307  
1308          // Create course to add the module.
1309          $course = self::getDataGenerator()->create_course();
1310  
1311          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1312          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
1313          $this->getDataGenerator()->enrol_user($user1->id, $course->id, $studentrole->id, 'manual');
1314          $this->getDataGenerator()->enrol_user($user2->id, $course->id, $studentrole->id, 'manual');
1315          $this->getDataGenerator()->enrol_user($user3->id, $course->id, $studentrole->id, 'manual');
1316          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id, 'manual');
1317  
1318          // Create the glossary and contents.
1319          $record = new \stdClass();
1320          $record->course = $course->id;
1321          $record->assessed = RATING_AGGREGATE_AVERAGE;
1322          $scale = $this->getDataGenerator()->create_scale(array('scale' => 'A,B,C,D'));
1323          $record->scale = "-$scale->id";
1324          $glossary = $this->getDataGenerator()->create_module('glossary', $record);
1325          $context = \context_module::instance($glossary->cmid);
1326  
1327          $gg = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
1328          $entry = $gg->create_content($glossary, array('approved' => 1, 'userid' => $user1->id));
1329  
1330          // Rate the entry as user2.
1331          $rating1 = new \stdClass();
1332          $rating1->contextid = $context->id;
1333          $rating1->component = 'mod_glossary';
1334          $rating1->ratingarea = 'entry';
1335          $rating1->itemid = $entry->id;
1336          $rating1->rating = 1; // 1 is A.
1337          $rating1->scaleid = "-$scale->id";
1338          $rating1->userid = $user2->id;
1339          $rating1->timecreated = time();
1340          $rating1->timemodified = time();
1341          $rating1->id = $DB->insert_record('rating', $rating1);
1342  
1343          // Rate the entry as user3.
1344          $rating2 = new \stdClass();
1345          $rating2->contextid = $context->id;
1346          $rating2->component = 'mod_glossary';
1347          $rating2->ratingarea = 'entry';
1348          $rating2->itemid = $entry->id;
1349          $rating2->rating = 3; // 3 is C.
1350          $rating2->scaleid = "-$scale->id";
1351          $rating2->userid = $user3->id;
1352          $rating2->timecreated = time() + 1;
1353          $rating2->timemodified = time() + 1;
1354          $rating2->id = $DB->insert_record('rating', $rating2);
1355  
1356          // As student, retrieve ratings information.
1357          $this->setUser($user1);
1358          $result = mod_glossary_external::get_entry_by_id($entry->id);
1359          $result = external_api::clean_returnvalue(mod_glossary_external::get_entry_by_id_returns(), $result);
1360          $this->assertCount(1, $result['ratinginfo']['ratings']);
1361          $this->assertFalse($result['ratinginfo']['ratings'][0]['canviewaggregate']);
1362          $this->assertFalse($result['ratinginfo']['canviewall']);
1363          $this->assertFalse($result['ratinginfo']['ratings'][0]['canrate']);
1364          $this->assertTrue(!isset($result['ratinginfo']['ratings'][0]['count']));
1365  
1366          // Now, as teacher, I should see the info correctly.
1367          $this->setUser($teacher);
1368          $result = mod_glossary_external::get_entry_by_id($entry->id);
1369          $result = external_api::clean_returnvalue(mod_glossary_external::get_entry_by_id_returns(), $result);
1370          $this->assertCount(1, $result['ratinginfo']['ratings']);
1371          $this->assertTrue($result['ratinginfo']['ratings'][0]['canviewaggregate']);
1372          $this->assertTrue($result['ratinginfo']['canviewall']);
1373          $this->assertTrue($result['ratinginfo']['ratings'][0]['canrate']);
1374          $this->assertEquals(2, $result['ratinginfo']['ratings'][0]['count']);
1375          $this->assertEquals(2, $result['ratinginfo']['ratings'][0]['aggregate']);   // 2 is B, that is the average of A + C.
1376      }
1377  }