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  namespace core_search;
  18  
  19  use advanced_testcase;
  20  use context_course;
  21  use core_mocksearch\search\mock_search_area;
  22  use mock_search\engine;
  23  use testable_core_search;
  24  use stdClass;
  25  
  26  /**
  27   * Unit tests for search document.
  28   *
  29   * @package     core_search
  30   * @category    phpunit
  31   * @copyright   2016 Eric Merrill {@link http://www.merrilldigital.com}
  32   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  33   * @coversDefaultClass \core_search\document
  34   */
  35  class document_test extends advanced_testcase {
  36  
  37      /**
  38       * Setup to ensure that fixtures are loaded.
  39       */
  40      public static function setupBeforeClass(): void {
  41          global $CFG;
  42          require_once($CFG->dirroot . '/search/tests/fixtures/testable_core_search.php');
  43          require_once($CFG->dirroot . '/search/tests/fixtures/mock_search_area.php');
  44      }
  45  
  46      /**
  47       * @var Instace of core_search_generator.
  48       */
  49      protected $generator = null;
  50  
  51      public function setUp(): void {
  52          $this->resetAfterTest();
  53          set_config('enableglobalsearch', true);
  54  
  55          // Set \core_search::instance to the mock_search_engine as we don't require the search engine to be working to test this.
  56          $search = testable_core_search::instance();
  57  
  58          $this->generator = self::getDataGenerator()->get_plugin_generator('core_search');
  59          $this->generator->setup();
  60      }
  61  
  62      /**
  63       * Adding this test here as get_areas_user_accesses process is the same, results just depend on the context level.
  64       *
  65       * @covers ::export_for_template
  66       * @return void
  67       */
  68      public function test_search_user_accesses() {
  69          global $PAGE;
  70  
  71          $area = new mock_search_area();
  72          $renderer = $PAGE->get_renderer('core_search');
  73          $engine = new engine();
  74  
  75          $course = $this->getDataGenerator()->create_course(['fullname' => 'Course & Title']);
  76          $user = $this->getDataGenerator()->create_user(['firstname' => 'User', 'lastname' => 'Escape & Name']);
  77          $this->getDataGenerator()->enrol_user($user->id, $course->id, 'teacher');
  78          $this->setAdminUser();
  79  
  80          // Make a record to enter in the search area.
  81          $record = new stdClass();
  82          $record->title = 'Escape & Title';
  83          $record->content = 'Escape & Content';
  84          $record->description1 = 'Escape & Description1';
  85          $record->description2 = 'Escape & Description2';
  86          $record->userid = $user->id;
  87          $record->courseid = $course->id;
  88          $record = $this->generator->create_record($record);
  89  
  90          // Convert to a 'doc data' type format.
  91          $docdata = $area->convert_record_to_doc_array($record);
  92  
  93          // First see that the docuemnt has the right information, unescaped.
  94          $doc = $engine->to_document($area, $docdata);
  95          $this->assertEquals('Escape & Title', $doc->get('title'));
  96          $this->assertEquals('Escape & Content', $doc->get('content'));
  97          $this->assertEquals('Escape & Description1', $doc->get('description1'));
  98          $this->assertEquals('Escape & Description2', $doc->get('description2'));
  99          $this->assertEquals('User Escape & Name', $doc->get('userfullname'));
 100          $this->assertEquals('Course & Title', $doc->get('coursefullname'));
 101  
 102          // Export for template, and see if it is escaped.
 103          $export = $doc->export_for_template($renderer);
 104          $this->assertEquals('Escape &amp; Title', $export['title']);
 105          $this->assertEquals('Escape &amp; Content', $export['content']);
 106          $this->assertEquals('Escape &amp; Description1', $export['description1']);
 107          $this->assertEquals('Escape &amp; Description2', $export['description2']);
 108          $this->assertEquals('User Escape &amp; Name', $export['userfullname']);
 109          $this->assertEquals('Course &amp; Title', $export['coursefullname']);
 110      }
 111  
 112      /**
 113       * Test we can set and get document icon.
 114       *
 115       * @covers ::set_doc_icon
 116       */
 117      public function test_get_and_set_doc_icon() {
 118          $document = $this->getMockBuilder('\core_search\document')
 119              ->disableOriginalConstructor()
 120              ->getMockForAbstractClass();
 121  
 122          $this->assertNull($document->get_doc_icon());
 123  
 124          $docicon = new \core_search\document_icon('test_name', 'test_component');
 125          $document->set_doc_icon($docicon);
 126  
 127          $this->assertEquals($docicon, $document->get_doc_icon());
 128      }
 129  
 130      public function tearDown(): void {
 131          // For unit tests before PHP 7, teardown is called even on skip. So only do our teardown if we did setup.
 132          if ($this->generator) {
 133              // Moodle DML freaks out if we don't teardown the temp table after each run.
 134              $this->generator->teardown();
 135              $this->generator = null;
 136          }
 137      }
 138  
 139      /**
 140       * Test the document author visibility depending on the user capabilities.
 141       *
 142       * @covers ::export_for_template
 143       * @dataProvider document_author_visibility_provider
 144       * @param string $rolename the role name
 145       * @param array $capexceptions the capabilities exceptions
 146       * @param bool $expected the expected author visibility
 147       * @param bool $owndocument if the resulting document belongs to the current user
 148       */
 149      public function test_document_author_visibility(
 150          string $rolename = 'editingteacher',
 151          array $capexceptions = [],
 152          bool $expected = true,
 153          bool $owndocument = false
 154      ) {
 155          global $DB, $PAGE;
 156  
 157          $area = new mock_search_area();
 158          $renderer = $PAGE->get_renderer('core_search');
 159          $engine = new engine();
 160  
 161          $course = $this->getDataGenerator()->create_course(['fullname' => 'Course & Title']);
 162          $context = context_course::instance($course->id);
 163  
 164          $roleid = $DB->get_field('role', 'id', ['shortname' => $rolename]);
 165          foreach ($capexceptions as $capability) {
 166              assign_capability($capability, CAP_PROHIBIT, $roleid, $context->id);
 167          }
 168  
 169          $user = $this->getDataGenerator()->create_user(['firstname' => 'Test', 'lastname' => 'User']);
 170          $this->getDataGenerator()->enrol_user($user->id, $course->id, $rolename);
 171          $this->setUser($user);
 172  
 173          if ($owndocument) {
 174              $author = $user;
 175          } else {
 176              $author = $this->getDataGenerator()->create_user(['firstname' => 'User', 'lastname' => 'Escape & Name']);
 177              $this->getDataGenerator()->enrol_user($author->id, $course->id, 'student');
 178          }
 179  
 180          // Make a record to enter in the search area.
 181          $record = new stdClass();
 182          $record->title = 'Escape & Title';
 183          $record->content = 'Escape & Content';
 184          $record->description1 = 'Escape & Description1';
 185          $record->description2 = 'Escape & Description2';
 186          $record->userid = $author->id;
 187          $record->courseid = $course->id;
 188          $record->contextid = $context->id;
 189          $record = $this->generator->create_record($record);
 190  
 191          // Convert to a 'doc data' type format.
 192          $docdata = $area->convert_record_to_doc_array($record);
 193  
 194          // First see that the document has the user information.
 195          $doc = $engine->to_document($area, $docdata);
 196          $this->assertEquals(fullname($author), $doc->get('userfullname'));
 197  
 198          // Export for template, and see if it the user information is exported.
 199          $export = $doc->export_for_template($renderer);
 200  
 201          if ($expected) {
 202              $authorname = htmlentities(fullname($author));
 203              $this->assertEquals($authorname, $export['userfullname']);
 204          } else {
 205              $this->assertArrayNotHasKey('userfullname', $export);
 206          }
 207      }
 208  
 209      /**
 210       * Data provider for test_document_author_visibility().
 211       *
 212       * @return array
 213       */
 214      public function document_author_visibility_provider(): array {
 215          return [
 216              'Teacher' => [
 217                  'rolename' => 'editingteacher',
 218                  'capexceptions' => [],
 219                  'expected' => true,
 220                  'owndocument' => false,
 221              ],
 222              'Non editing teacher' => [
 223                  'rolename' => 'teacher',
 224                  'capexceptions' => [],
 225                  'expected' => true,
 226                  'owndocument' => false,
 227              ],
 228              'Student' => [
 229                  'rolename' => 'student',
 230                  'capexceptions' => [],
 231                  'expected' => true,
 232                  'owndocument' => false,
 233              ],
 234              // Adding capability exceptions.
 235              'Student without view profiles' => [
 236                  'rolename' => 'student',
 237                  'capexceptions' => ['moodle/user:viewdetails'],
 238                  'expected' => false,
 239                  'owndocument' => false,
 240              ],
 241              'Student without view participants' => [
 242                  'rolename' => 'student',
 243                  'capexceptions' => ['moodle/course:viewparticipants'],
 244                  'expected' => false,
 245                  'owndocument' => false,
 246              ],
 247              'Student without view participants or profiles' => [
 248                  'rolename' => 'student',
 249                  'capexceptions' => ['moodle/user:viewdetails', 'moodle/course:viewparticipants'],
 250                  'expected' => false,
 251                  'owndocument' => false,
 252              ],
 253              // Users should be able to see its own documents.
 254              'Student author without view profiles' => [
 255                  'rolename' => 'student',
 256                  'capexceptions' => ['moodle/user:viewdetails'],
 257                  'expected' => true,
 258                  'owndocument' => true,
 259              ],
 260              'Student author without view participants' => [
 261                  'rolename' => 'student',
 262                  'capexceptions' => ['moodle/course:viewparticipants'],
 263                  'expected' => true,
 264                  'owndocument' => true,
 265              ],
 266              'Student author without view participants or profiles' => [
 267                  'rolename' => 'student',
 268                  'capexceptions' => ['moodle/user:viewdetails', 'moodle/course:viewparticipants'],
 269                  'expected' => true,
 270                  'owndocument' => true,
 271              ],
 272  
 273          ];
 274      }
 275  }