Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 311 and 401]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Unit tests for data_get_all_recordsids(), data_get_advance_search_ids(), data_get_record_ids(),
  19   * and data_get_advanced_search_sql()
  20   *
  21   * @package    mod_data
  22   * @category   phpunit
  23   * @copyright  2012 Adrian Greeve
  24   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  
  27  namespace mod_data\search;
  28  
  29  defined('MOODLE_INTERNAL') || die();
  30  
  31  global $CFG;
  32  require_once($CFG->dirroot . '/mod/data/lib.php');
  33  require_once($CFG->dirroot . '/lib/datalib.php');
  34  require_once($CFG->dirroot . '/lib/csvlib.class.php');
  35  require_once($CFG->dirroot . '/search/tests/fixtures/testable_core_search.php');
  36  require_once($CFG->dirroot . '/mod/data/tests/generator/lib.php');
  37  
  38  /**
  39   * Unit tests for {@see data_get_all_recordids()}.
  40   *                {@see data_get_advanced_search_ids()}
  41   *                {@see data_get_record_ids()}
  42   *                {@see data_get_advanced_search_sql()}
  43   *
  44   * @package    mod_data
  45   * @copyright  2012 Adrian Greeve
  46   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  47   */
  48  class search_test extends \advanced_testcase {
  49      /**
  50       * @var stdObject $recorddata An object that holds information from the table data.
  51       */
  52      public $recorddata = null;
  53      /**
  54       * @var int $recordcontentid The content ID.
  55       */
  56      public $recordcontentid = null;
  57      /**
  58       * @var int $recordrecordid The record ID.
  59       */
  60      public $recordrecordid = null;
  61      /**
  62       * @var int $recordfieldid The field ID.
  63       */
  64      public $recordfieldid = null;
  65      /**
  66       * @var array $recordsearcharray An array of stdClass which contains search criteria.
  67       */
  68      public $recordsearcharray = null;
  69  
  70      // CONSTANTS
  71  
  72      /**
  73       * @var int $datarecordcount   The number of records in the database.
  74       */
  75      public $datarecordcount = 100;
  76  
  77      /**
  78       * @var int $groupdatarecordcount  The number of records in the database in groups 0 and 1.
  79       */
  80      public $groupdatarecordcount = 75;
  81  
  82      /**
  83       * @var array $datarecordset   Expected record IDs.
  84       */
  85      public $datarecordset = array('0' => '6');
  86  
  87      /**
  88       * @var array $finalrecord   Final record for comparison with test four.
  89       */
  90      public $finalrecord = array();
  91  
  92      /**
  93       * @var int $approvedatarecordcount  The number of approved records in the database.
  94       */
  95      public $approvedatarecordcount = 89;
  96  
  97      /**
  98       * @var string Area id
  99       */
 100      protected $databaseentryareaid = null;
 101  
 102      /**
 103       * Set up function. In this instance we are setting up database
 104       * records to be used in the unit tests.
 105       */
 106      protected function setUp(): void {
 107          global $DB, $CFG;
 108          parent::setUp();
 109  
 110          $this->resetAfterTest(true);
 111  
 112          set_config('enableglobalsearch', true);
 113  
 114          $this->databaseentryareaid = \core_search\manager::generate_areaid('mod_data', 'entry');
 115  
 116          // Set \core_search::instance to the mock_search_engine as we don't require the search engine to be working to test this.
 117          $search = \testable_core_search::instance();
 118  
 119      }
 120  
 121      /**
 122       * Test 1: The function data_get_all_recordids.
 123       *
 124       * Test 2: This tests the data_get_advance_search_ids() function. The function takes a set
 125       * of all the record IDs in the database and then with the search details ($this->recordsearcharray)
 126       * returns a comma seperated string of record IDs that match the search criteria.
 127       *
 128       * Test 3: This function tests data_get_recordids(). This is the function that is nested in the last
 129       * function (see data_get_advance_search_ids). This function takes a couple of
 130       * extra parameters. $alias is the field alias used in the sql query and $commaid
 131       * is a comma seperated string of record IDs.
 132       *
 133       * Test 3.1: This tests that if no recordids are provided (In a situation where a search is done on an empty database)
 134       * That an empty array is returned.
 135       *
 136       * Test 4: data_get_advanced_search_sql provides an array which contains an sql string to be used for displaying records
 137       * to the user when they use the advanced search criteria and the parameters that go with the sql statement. This test
 138       * takes that information and does a search on the database, returning a record.
 139       *
 140       * Test 5: Returning to data_get_all_recordids(). Here we are ensuring that the total amount of record ids is reduced to
 141       * match the group conditions that are provided. There are 25 entries which relate to group 2. They are removed
 142       * from the total so we should only have 75 records total.
 143       *
 144       * Test 6: data_get_all_recordids() again. This time we are testing approved database records. We only want to
 145       * display the records that have been approved. In this record set we have 89 approved records.
 146       */
 147      public function test_advanced_search_sql_section() {
 148          global $DB;
 149  
 150          // we already have 2 users, we need 98 more - let's ignore the fact that guest can not post anywhere
 151          // We reset the user sequence here to ensure we get the expected numbers.
 152          // TODO: Invent a better way for managing data file input against database sequence id's.
 153          $DB->get_manager()->reset_sequence('user');
 154          for($i=3;$i<=100;$i++) {
 155              $this->getDataGenerator()->create_user();
 156          }
 157  
 158          // create database module - there should be more of these I guess
 159          $course = $this->getDataGenerator()->create_course();
 160          $data = $this->getDataGenerator()->create_module('data', array('course'=>$course->id));
 161          $this->recorddata = $data;
 162  
 163          // Set up data for the test database.
 164          $files = array(
 165                  'data_fields'  => __DIR__.'/../fixtures/test_data_fields.csv',
 166                  'data_records' => __DIR__.'/../fixtures/test_data_records.csv',
 167                  'data_content' => __DIR__.'/../fixtures/test_data_content.csv',
 168          );
 169          $this->dataset_from_files($files)->to_database();
 170          // Set dataid to the correct value now the data has been inserted by csv file.
 171          $DB->execute('UPDATE {data_fields} SET dataid = ?', array($data->id));
 172          $DB->execute('UPDATE {data_records} SET dataid = ?', array($data->id));
 173  
 174          // Create the search array which contains our advanced search criteria.
 175          $fieldinfo = array('0' => new \stdClass(),
 176                  '1' => new \stdClass(),
 177                  '2' => new \stdClass(),
 178                  '3' => new \stdClass(),
 179                  '4' => new \stdClass());
 180          $fieldinfo['0']->id = 1;
 181          $fieldinfo['0']->data = '3.721,46.6126';
 182          $fieldinfo['1']->id = 2;
 183          $fieldinfo['1']->data = 'Hahn Premium';
 184          $fieldinfo['2']->id = 5;
 185          $fieldinfo['2']->data = 'Female';
 186          $fieldinfo['3']->id = 7;
 187          $fieldinfo['3']->data = 'kel';
 188          $fieldinfo['4']->id = 9;
 189          $fieldinfo['4']->data = 'VIC';
 190  
 191          foreach($fieldinfo as $field) {
 192              $searchfield = data_get_field_from_id($field->id, $data);
 193              if ($field->id == 2) {
 194                  $searchfield->field->param1 = 'Hahn Premium';
 195                  $val = array();
 196                  $val['selected'] = array('0' => 'Hahn Premium');
 197                  $val['allrequired'] = 0;
 198              } else {
 199                  $val = $field->data;
 200              }
 201              $search_array[$field->id] = new \stdClass();
 202              list($search_array[$field->id]->sql, $search_array[$field->id]->params) = $searchfield->generate_sql('c' . $field->id, $val);
 203          }
 204  
 205          $this->recordsearcharray = $search_array;
 206  
 207          // Setting up the comparison stdClass for the last test.
 208          $user = $DB->get_record('user', array('id'=>6));
 209          $this->finalrecord[6] = new \stdClass();
 210          $this->finalrecord[6]->id = 6;
 211          $this->finalrecord[6]->approved = 1;
 212          $this->finalrecord[6]->timecreated = 1234567891;
 213          $this->finalrecord[6]->timemodified = 1234567892;
 214          $this->finalrecord[6]->userid = 6;
 215          $this->finalrecord[6]->firstname = $user->firstname;
 216          $this->finalrecord[6]->lastname = $user->lastname;
 217          $this->finalrecord[6]->firstnamephonetic = $user->firstnamephonetic;
 218          $this->finalrecord[6]->lastnamephonetic = $user->lastnamephonetic;
 219          $this->finalrecord[6]->middlename = $user->middlename;
 220          $this->finalrecord[6]->alternatename = $user->alternatename;
 221          $this->finalrecord[6]->picture = $user->picture;
 222          $this->finalrecord[6]->imagealt = $user->imagealt;
 223          $this->finalrecord[6]->email = $user->email;
 224  
 225          // Test 1
 226          $recordids = data_get_all_recordids($this->recorddata->id);
 227          $this->assertEquals(count($recordids), $this->datarecordcount);
 228  
 229          // Test 2
 230          $key = array_keys($this->recordsearcharray);
 231          $alias = $key[0];
 232          $newrecordids = data_get_recordids($alias, $this->recordsearcharray, $this->recorddata->id, $recordids);
 233          $this->assertEquals($this->datarecordset, $newrecordids);
 234  
 235          // Test 3
 236          $newrecordids = data_get_advance_search_ids($recordids, $this->recordsearcharray, $this->recorddata->id);
 237          $this->assertEquals($this->datarecordset, $newrecordids);
 238  
 239          // Test 3.1
 240          $resultrecordids = data_get_advance_search_ids(array(), $this->recordsearcharray, $this->recorddata->id);
 241          $this->assertEmpty($resultrecordids);
 242  
 243          // Test 4
 244          $sortorder = 'ORDER BY r.timecreated ASC , r.id ASC';
 245          $html = data_get_advanced_search_sql('0', $this->recorddata, $newrecordids, '', $sortorder);
 246          $allparams = array_merge($html['params'], array('dataid' => $this->recorddata->id));
 247          $records = $DB->get_records_sql($html['sql'], $allparams);
 248          $this->assertEquals($records, $this->finalrecord);
 249  
 250          // Test 5
 251          $groupsql = " AND (r.groupid = :currentgroup OR r.groupid = 0)";
 252          $params = array('currentgroup' => 1);
 253          $recordids = data_get_all_recordids($this->recorddata->id, $groupsql, $params);
 254          $this->assertEquals($this->groupdatarecordcount, count($recordids));
 255  
 256          // Test 6
 257          $approvesql = ' AND r.approved=1 ';
 258          $recordids = data_get_all_recordids($this->recorddata->id, $approvesql, $params);
 259          $this->assertEquals($this->approvedatarecordcount, count($recordids));
 260      }
 261  
 262      public function test_advanced_search_tags() {
 263          $this->resetAfterTest();
 264          $this->setAdminUser();
 265  
 266          // Setup test data.
 267          $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_data');
 268          $course1 = $this->getDataGenerator()->create_course();
 269  
 270          $fieldrecord = new \stdClass();
 271          $fieldrecord->name = 'field-1';
 272          $fieldrecord->type = 'text';
 273          $fieldrecord->titlefield = true;
 274  
 275          $data1 = $this->getDataGenerator()->create_module('data', array('course' => $course1->id, 'approval' => true));
 276          $field1 = $datagenerator->create_field($fieldrecord, $data1);
 277  
 278          $record11 = $datagenerator->create_entry($data1, [$field1->field->id => 'value11'], 0, ['Cats', 'Dogs']);
 279          $record12 = $datagenerator->create_entry($data1, [$field1->field->id => 'value12'], 0, ['Cats', 'mice']);
 280          $record13 = $datagenerator->create_entry($data1, [$field1->field->id => 'value13'], 0, ['Bats']);
 281  
 282          $searcharray = [];
 283          $searcharray[DATA_TAGS] = new \stdClass();
 284          $searcharray[DATA_TAGS]->params = [];
 285          $searcharray[DATA_TAGS]->rawtagnames = ['Cats'];
 286          $searcharray[DATA_TAGS]->sql = '';
 287  
 288          $recordids = data_get_all_recordids($data1->id);
 289          $newrecordids = data_get_advance_search_ids($recordids, $searcharray, $data1->id);
 290  
 291          $this->assertContainsEquals($record11, $newrecordids);
 292          $this->assertContainsEquals($record12, $newrecordids);
 293          $this->assertNotContainsEquals($record13, $newrecordids);
 294      }
 295  
 296      /**
 297       * Indexing database entries contents.
 298       *
 299       * @return void
 300       */
 301      public function test_data_entries_indexing() {
 302          global $DB;
 303  
 304          // Returns the instance as long as the area is supported.
 305          $searcharea = \core_search\manager::get_search_area($this->databaseentryareaid);
 306          $this->assertInstanceOf('\mod_data\search\entry', $searcharea);
 307  
 308          $user1 = self::getDataGenerator()->create_user();
 309  
 310          $course1 = self::getDataGenerator()->create_course();
 311  
 312          $this->getDataGenerator()->enrol_user($user1->id, $course1->id, 'student');
 313  
 314          $record = new \stdClass();
 315          $record->course = $course1->id;
 316  
 317          $this->setUser($user1);
 318  
 319          // Available for both student and teacher.
 320          $data1 = $this->getDataGenerator()->create_module('data', $record);
 321  
 322          // Excluding LatLong and Picture as we aren't indexing LatLong and Picture fields any way
 323          // ...and they're complex and not of any use to consider for this test.
 324          // Excluding File as we are indexing files seperately and its complex to implement.
 325          $fieldtypes = array( 'checkbox', 'date', 'menu', 'multimenu', 'number', 'radiobutton', 'text', 'textarea', 'url' );
 326  
 327          $this->create_default_data_fields($fieldtypes, $data1);
 328  
 329          $data1record1id = $this->create_default_data_record($data1);
 330          // All records.
 331          $recordset = $searcharea->get_recordset_by_timestamp(0);
 332  
 333          $this->assertTrue($recordset->valid());
 334  
 335          $nrecords = 0;
 336          foreach ($recordset as $record) {
 337              $this->assertInstanceOf('stdClass', $record);
 338              $doc = $searcharea->get_document($record);
 339              $this->assertInstanceOf('\core_search\document', $doc);
 340              $nrecords++;
 341          }
 342  
 343          // If there would be an error/failure in the foreach above the recordset would be closed on shutdown.
 344          $recordset->close();
 345          $this->assertEquals(1, $nrecords);
 346  
 347          // The +2 is to prevent race conditions.
 348          $recordset = $searcharea->get_recordset_by_timestamp(time() + 2);
 349  
 350          // No new records.
 351          $this->assertFalse($recordset->valid());
 352          $recordset->close();
 353  
 354          // Create a second database, also with one record.
 355          $data2 = $this->getDataGenerator()->create_module('data', ['course' => $course1->id]);
 356          $this->create_default_data_fields($fieldtypes, $data2);
 357          $this->create_default_data_record($data2);
 358  
 359          // Test indexing with contexts.
 360          $rs = $searcharea->get_document_recordset(0, \context_module::instance($data1->cmid));
 361          $this->assertEquals(1, iterator_count($rs));
 362          $rs->close();
 363          $rs = $searcharea->get_document_recordset(0, \context_module::instance($data2->cmid));
 364          $this->assertEquals(1, iterator_count($rs));
 365          $rs->close();
 366          $rs = $searcharea->get_document_recordset(0, \context_course::instance($course1->id));
 367          $this->assertEquals(2, iterator_count($rs));
 368          $rs->close();
 369      }
 370  
 371      /**
 372       * Document contents.
 373       *
 374       * @return void
 375       */
 376      public function test_data_entries_document() {
 377          global $DB;
 378  
 379          // Returns the instance as long as the area is supported.
 380          $searcharea = \core_search\manager::get_search_area($this->databaseentryareaid);
 381          $this->assertInstanceOf('\mod_data\search\entry', $searcharea);
 382  
 383          $user1 = self::getDataGenerator()->create_user();
 384  
 385          $course = self::getDataGenerator()->create_course();
 386  
 387          $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
 388  
 389          $record = new \stdClass();
 390          $record->course = $course->id;
 391  
 392          $this->setAdminUser();
 393  
 394          // First Case.
 395          $data1 = $this->getDataGenerator()->create_module('data', $record);
 396  
 397          $fieldtypes = array( 'checkbox', 'date', 'menu', 'multimenu', 'number', 'radiobutton', 'text', 'textarea', 'url' );
 398  
 399          $this->create_default_data_fields($fieldtypes, $data1);
 400  
 401          $data1record1id = $this->create_default_data_record($data1);
 402  
 403          $data1entry1 = $this->get_entry_for_id($data1record1id);
 404  
 405          $data1doc = $searcharea->get_document($data1entry1);
 406  
 407          $this->assertEquals($data1doc->get('courseid'), $course->id);
 408          $this->assertEquals($data1doc->get('title'), 'text for testing');
 409          $this->assertEquals($data1doc->get('content'), 'menu1');
 410          $this->assertEquals($data1doc->get('description1'), 'radioopt1');
 411          $this->assertEquals($data1doc->get('description2'), 'opt1 opt2 opt3 opt4 multimenu1 multimenu2 multimenu3 multimenu4 text area testing http://example.url');
 412  
 413          // Second Case.
 414          $data2 = $this->getDataGenerator()->create_module('data', $record);
 415  
 416          $fieldtypes = array(
 417                  array('checkbox', 1),
 418                  array('textarea', 0),
 419                  array('menu', 0),
 420                  array('number', 1),
 421                  array('url', 0),
 422                  array('text', 0)
 423          );
 424  
 425          $this->create_default_data_fields($fieldtypes, $data2);
 426  
 427          $data2record1id = $this->create_default_data_record($data2);
 428  
 429          $data2entry1 = $this->get_entry_for_id($data2record1id);
 430  
 431          $data2doc = $searcharea->get_document($data2entry1);
 432  
 433          $this->assertEquals($data2doc->get('courseid'), $course->id);
 434          $this->assertEquals($data2doc->get('title'), 'opt1 opt2 opt3 opt4');
 435          $this->assertEquals($data2doc->get('content'), 'text for testing');
 436          $this->assertEquals($data2doc->get('description1'), 'menu1');
 437          $this->assertEquals($data2doc->get('description2'), 'text area testing http://example.url');
 438  
 439          // Third Case.
 440          $data3 = $this->getDataGenerator()->create_module('data', $record);
 441  
 442          $fieldtypes = array( 'url' );
 443  
 444          $this->create_default_data_fields($fieldtypes, $data3);
 445  
 446          $data3record1id = $this->create_default_data_record($data3);
 447  
 448          $data3entry1 = $this->get_entry_for_id($data3record1id);
 449  
 450          $this->assertFalse($searcharea->get_document($data3entry1));
 451  
 452          // Fourth Case.
 453          $data4 = $this->getDataGenerator()->create_module('data', $record);
 454  
 455          $fieldtypes = array( array('date', 1), array('text', 1));
 456  
 457          $this->create_default_data_fields($fieldtypes, $data4);
 458  
 459          $data4record1id = $this->create_default_data_record($data4);
 460  
 461          $data4entry1 = $this->get_entry_for_id($data4record1id);
 462  
 463          $this->assertFalse($searcharea->get_document($data4entry1));
 464  
 465          // Fifth Case.
 466          $data5 = $this->getDataGenerator()->create_module('data', $record);
 467  
 468          $fieldtypes = array(
 469                  array('checkbox', 0),
 470                  array('number', 1),
 471                  array('text', 0),
 472                  array('date', 1),
 473                  array('textarea', 0),
 474                  array('url', 1));
 475  
 476          $this->create_default_data_fields($fieldtypes, $data5);
 477  
 478          $data5record1id = $this->create_default_data_record($data5);
 479  
 480          $data5entry1 = $this->get_entry_for_id($data5record1id);
 481  
 482          $data5doc = $searcharea->get_document($data5entry1);
 483  
 484          $this->assertEquals($data5doc->get('courseid'), $course->id);
 485          $this->assertEquals($data5doc->get('title'), 'http://example.url');
 486          $this->assertEquals($data5doc->get('content'), 'text for testing');
 487          $this->assertEquals($data5doc->get('description1'), 'opt1 opt2 opt3 opt4');
 488          $this->assertEquals($data5doc->get('description2'), 'text area testing');
 489  
 490          // Sixth Case.
 491          $data6 = $this->getDataGenerator()->create_module('data', $record);
 492  
 493          $fieldtypes = array( array('date', 1), array('number', 1));
 494  
 495          $this->create_default_data_fields($fieldtypes, $data6);
 496  
 497          $data6record1id = $this->create_default_data_record($data6);
 498  
 499          $data6entry1 = $this->get_entry_for_id($data6record1id);
 500  
 501          $data6doc = $searcharea->get_document($data6entry1);
 502  
 503          $this->assertFalse($data6doc);
 504  
 505          // Seventh Case.
 506          $data7 = $this->getDataGenerator()->create_module('data', $record);
 507  
 508          $fieldtypes = array( array('date', 1), array('number', 1),
 509                  array('text', 0), array('textarea', 0));
 510  
 511          $this->create_default_data_fields($fieldtypes, $data7);
 512  
 513          $data7record1id = $this->create_default_data_record($data7);
 514  
 515          $data7entry1 = $this->get_entry_for_id($data7record1id);
 516  
 517          $data7doc = $searcharea->get_document($data7entry1);
 518  
 519          $this->assertEquals($data7doc->get('courseid'), $course->id);
 520          $this->assertEquals($data7doc->get('title'), 'text for testing');
 521          $this->assertEquals($data7doc->get('content'), 'text area testing');
 522  
 523          // Eight Case.
 524          $data8 = $this->getDataGenerator()->create_module('data', $record);
 525  
 526          $fieldtypes = array('url', 'url', 'url', 'text');
 527  
 528          $this->create_default_data_fields($fieldtypes, $data8);
 529  
 530          $data8record1id = $this->create_default_data_record($data8);
 531  
 532          $data8entry1 = $this->get_entry_for_id($data8record1id);
 533  
 534          $data8doc = $searcharea->get_document($data8entry1);
 535  
 536          $this->assertEquals($data8doc->get('courseid'), $course->id);
 537          $this->assertEquals($data8doc->get('title'), 'text for testing');
 538          $this->assertEquals($data8doc->get('content'), 'http://example.url');
 539          $this->assertEquals($data8doc->get('description1'), 'http://example.url');
 540          $this->assertEquals($data8doc->get('description2'), 'http://example.url');
 541  
 542          // Ninth Case.
 543          $data9 = $this->getDataGenerator()->create_module('data', $record);
 544  
 545          $fieldtypes = array('radiobutton', 'menu', 'multimenu');
 546  
 547          $this->create_default_data_fields($fieldtypes, $data9);
 548  
 549          $data9record1id = $this->create_default_data_record($data9);
 550  
 551          $data9entry1 = $this->get_entry_for_id($data9record1id);
 552  
 553          $data9doc = $searcharea->get_document($data9entry1);
 554  
 555          $this->assertEquals($data9doc->get('courseid'), $course->id);
 556          $this->assertEquals($data9doc->get('title'), 'radioopt1');
 557          $this->assertEquals($data9doc->get('content'), 'menu1');
 558          $this->assertEquals($data9doc->get('description1'), 'multimenu1 multimenu2 multimenu3 multimenu4');
 559  
 560          // Tenth Case.
 561          $data10 = $this->getDataGenerator()->create_module('data', $record);
 562  
 563          $fieldtypes = array('checkbox', 'textarea', 'multimenu');
 564  
 565          $this->create_default_data_fields($fieldtypes, $data10);
 566  
 567          $data10record1id = $this->create_default_data_record($data10);
 568  
 569          $data10entry1 = $this->get_entry_for_id($data10record1id);
 570  
 571          $data10doc = $searcharea->get_document($data10entry1);
 572  
 573          $this->assertEquals($data10doc->get('courseid'), $course->id);
 574          $this->assertEquals($data10doc->get('title'), 'opt1 opt2 opt3 opt4');
 575          $this->assertEquals($data10doc->get('content'), 'text area testing');
 576          $this->assertEquals($data10doc->get('description1'), 'multimenu1 multimenu2 multimenu3 multimenu4');
 577  
 578      }
 579  
 580      /**
 581       * Group support for data entries.
 582       */
 583      public function test_data_entries_group_support() {
 584          global $DB;
 585  
 586          // Get the search area and test generators.
 587          $searcharea = \core_search\manager::get_search_area($this->databaseentryareaid);
 588          $generator = $this->getDataGenerator();
 589          $datagenerator = $generator->get_plugin_generator('mod_data');
 590  
 591          // Create a course, a user, and two groups.
 592          $course = $generator->create_course();
 593          $user = $generator->create_user();
 594          $generator->enrol_user($user->id, $course->id, 'teacher');
 595          $group1 = $generator->create_group(['courseid' => $course->id]);
 596          $group2 = $generator->create_group(['courseid' => $course->id]);
 597  
 598          // Separate groups database.
 599          $data = self::getDataGenerator()->create_module('data', ['course' => $course->id,
 600                  'groupmode' => SEPARATEGROUPS]);
 601          $fieldtypes = ['text', 'textarea'];
 602          $this->create_default_data_fields($fieldtypes, $data);
 603          $fields = $DB->get_records('data_fields', array('dataid' => $data->id));
 604          foreach ($fields as $field) {
 605              switch ($field->type) {
 606                  case 'text' :
 607                      $textid = $field->id;
 608                      break;
 609                  case 'textarea' :
 610                      $textareaid = $field->id;
 611                      break;
 612              }
 613          }
 614  
 615          // As admin, create entries with each group and all groups.
 616          $this->setAdminUser();
 617          $fieldvalues = [$textid => 'Title', $textareaid => 'Content'];
 618          $e1 = $datagenerator->create_entry($data, $fieldvalues, $group1->id);
 619          $e2 = $datagenerator->create_entry($data, $fieldvalues, $group2->id);
 620          $e3 = $datagenerator->create_entry($data, $fieldvalues);
 621  
 622          // Do the indexing of all 3 entries.
 623          $rs = $searcharea->get_recordset_by_timestamp(0);
 624          $results = [];
 625          foreach ($rs as $rec) {
 626              $results[$rec->id] = $rec;
 627          }
 628          $rs->close();
 629          $this->assertCount(3, $results);
 630  
 631          // Check each has the correct groupid.
 632          $doc = $searcharea->get_document($results[$e1]);
 633          $this->assertTrue($doc->is_set('groupid'));
 634          $this->assertEquals($group1->id, $doc->get('groupid'));
 635          $doc = $searcharea->get_document($results[$e2]);
 636          $this->assertTrue($doc->is_set('groupid'));
 637          $this->assertEquals($group2->id, $doc->get('groupid'));
 638          $doc = $searcharea->get_document($results[$e3]);
 639          $this->assertFalse($doc->is_set('groupid'));
 640  
 641          // While we're here, also test that the search area requests restriction by group.
 642          $modinfo = get_fast_modinfo($course);
 643          $this->assertTrue($searcharea->restrict_cm_access_by_group($modinfo->get_cm($data->cmid)));
 644  
 645          // In visible groups mode, it won't request restriction by group.
 646          set_coursemodule_groupmode($data->cmid, VISIBLEGROUPS);
 647          $modinfo = get_fast_modinfo($course);
 648          $this->assertFalse($searcharea->restrict_cm_access_by_group($modinfo->get_cm($data->cmid)));
 649      }
 650  
 651      /**
 652       * Document accesses.
 653       *
 654       * @return void
 655       */
 656      public function test_data_entries_access() {
 657          global $DB;
 658  
 659          // Returns the instance as long as the area is supported.
 660          $searcharea = \core_search\manager::get_search_area($this->databaseentryareaid);
 661          $this->assertInstanceOf('\mod_data\search\entry', $searcharea);
 662  
 663          $user1 = self::getDataGenerator()->create_user();
 664          $user2 = self::getDataGenerator()->create_user();
 665          $user3 = self::getDataGenerator()->create_user();
 666          $userteacher1 = self::getDataGenerator()->create_user();
 667  
 668          $course1 = self::getDataGenerator()->create_course();
 669          $course2 = self::getDataGenerator()->create_course();
 670  
 671          $this->getDataGenerator()->enrol_user($user1->id, $course1->id, 'student');
 672          $this->getDataGenerator()->enrol_user($user2->id, $course1->id, 'student');
 673          $this->getDataGenerator()->enrol_user($userteacher1->id, $course1->id, 'teacher');
 674  
 675          $this->getDataGenerator()->enrol_user($user3->id, $course2->id, 'student');
 676  
 677          $record = new \stdClass();
 678          $record->course = $course1->id;
 679  
 680          $this->setUser($userteacher1);
 681  
 682          $data1 = $this->getDataGenerator()->create_module('data', $record);
 683  
 684          $fieldtypes = array( 'checkbox', 'date', 'menu', 'multimenu', 'number', 'radiobutton', 'text', 'textarea', 'url' );
 685  
 686          $this->create_default_data_fields($fieldtypes, $data1);
 687  
 688          $this->setUser($user1);
 689          $data1record1id = $this->create_default_data_record($data1);
 690  
 691          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data1record1id));
 692          $this->assertEquals(\core_search\manager::ACCESS_DELETED, $searcharea->check_access(-1));
 693  
 694          $this->setUser($user2);
 695          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data1record1id));
 696  
 697          $this->setUser($user3);
 698          $this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($data1record1id));
 699  
 700          $this->setUser($userteacher1);
 701          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data1record1id));
 702  
 703          $this->setAdminUser();
 704          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data1record1id));
 705  
 706          $this->setGuestUser();
 707          $this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($data1record1id));
 708  
 709          // Case with groups.
 710          $user1 = self::getDataGenerator()->create_user();
 711          $user2 = self::getDataGenerator()->create_user();
 712          $user3 = self::getDataGenerator()->create_user();
 713          $userteacher1 = self::getDataGenerator()->create_user();
 714  
 715          $course = self::getDataGenerator()->create_course(array('groupmode' => 1, 'groupmodeforce' => 1));
 716  
 717          $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
 718          $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
 719          $this->getDataGenerator()->enrol_user($userteacher1->id, $course->id, 'teacher');
 720          $this->getDataGenerator()->enrol_user($user3->id, $course->id, 'student');
 721  
 722          $groupa = $this->getDataGenerator()->create_group(array('courseid' => $course->id, 'name' => 'groupA'));
 723          $groupb = $this->getDataGenerator()->create_group(array('courseid' => $course->id, 'name' => 'groupB'));
 724  
 725          $this->getDataGenerator()->create_group_member(array('userid' => $user1->id, 'groupid' => $groupa->id));
 726          $this->getDataGenerator()->create_group_member(array('userid' => $user2->id, 'groupid' => $groupa->id));
 727          $this->getDataGenerator()->create_group_member(array('userid' => $userteacher1->id, 'groupid' => $groupa->id));
 728  
 729          $this->getDataGenerator()->create_group_member(array('userid' => $user3->id, 'groupid' => $groupb->id));
 730  
 731          $record = new \stdClass();
 732          $record->course = $course->id;
 733  
 734          $this->setUser($userteacher1);
 735  
 736          $data2 = $this->getDataGenerator()->create_module('data', $record);
 737  
 738          $cm = get_coursemodule_from_instance('data', $data2->id, $course->id);
 739          $cm->groupmode = '1';
 740          $cm->effectivegroupmode = '1';
 741  
 742          $fieldtypes = array( 'checkbox', 'date', 'menu', 'multimenu', 'number', 'radiobutton', 'text', 'textarea', 'url' );
 743  
 744          $this->create_default_data_fields($fieldtypes, $data2);
 745  
 746          $this->setUser($user1);
 747  
 748          $data2record1id = $this->create_default_data_record($data2, $groupa->id);
 749  
 750          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data2record1id));
 751          $this->assertEquals(\core_search\manager::ACCESS_DELETED, $searcharea->check_access(-1));
 752  
 753          $this->setUser($user2);
 754          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data2record1id));
 755  
 756          $this->setUser($user3);
 757          $this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($data2record1id));
 758  
 759          $this->setUser($userteacher1);
 760          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data2record1id));
 761  
 762          $this->setAdminUser();
 763          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data2record1id));
 764  
 765          $this->setGuestUser();
 766          $this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($data2record1id));
 767  
 768          // Case with approval.
 769          $user1 = self::getDataGenerator()->create_user();
 770          $user2 = self::getDataGenerator()->create_user();
 771          $userteacher1 = self::getDataGenerator()->create_user();
 772  
 773          $course = self::getDataGenerator()->create_course();
 774  
 775          $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
 776          $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
 777          $this->getDataGenerator()->enrol_user($userteacher1->id, $course->id, 'teacher');
 778  
 779          $record = new \stdClass();
 780          $record->course = $course->id;
 781  
 782          $this->setUser($userteacher1);
 783  
 784          $data3 = $this->getDataGenerator()->create_module('data', $record);
 785  
 786          $DB->update_record('data', array('id' => $data3->id, 'approval' => 1));
 787  
 788          $fieldtypes = array( 'checkbox', 'date', 'menu', 'multimenu', 'number', 'radiobutton', 'text', 'textarea', 'url' );
 789  
 790          $this->create_default_data_fields($fieldtypes, $data3);
 791  
 792          $this->setUser($user1);
 793  
 794          $data3record1id = $this->create_default_data_record($data3);
 795  
 796          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data3record1id));
 797          $this->assertEquals(\core_search\manager::ACCESS_DELETED, $searcharea->check_access(-1));
 798  
 799          $this->setUser($user2);
 800          $this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($data3record1id));
 801  
 802          $this->setUser($userteacher1);
 803          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data3record1id));
 804  
 805          $this->setAdminUser();
 806          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data3record1id));
 807  
 808          $this->setGuestUser();
 809          $this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($data3record1id));
 810  
 811          $DB->update_record('data_records', array('id' => $data3record1id, 'approved' => 1));
 812  
 813          $this->setUser($user1);
 814          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data3record1id));
 815          $this->assertEquals(\core_search\manager::ACCESS_DELETED, $searcharea->check_access(-1));
 816  
 817          $this->setUser($user2);
 818          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data3record1id));
 819  
 820          $this->setUser($userteacher1);
 821          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data3record1id));
 822  
 823          $this->setAdminUser();
 824          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data3record1id));
 825  
 826          $this->setGuestUser();
 827          $this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($data3record1id));
 828  
 829          // Case with requiredentriestoview.
 830          $this->setAdminUser();
 831  
 832          $record->requiredentriestoview = 2;
 833  
 834          $data4 = $this->getDataGenerator()->create_module('data', $record);
 835          $fieldtypes = array( 'checkbox', 'date', 'menu', 'multimenu', 'number', 'radiobutton', 'text', 'textarea', 'url' );
 836  
 837          $this->create_default_data_fields($fieldtypes, $data4);
 838  
 839          $data4record1id = $this->create_default_data_record($data4);
 840  
 841          $this->setUser($user1);
 842          $this->assertEquals(\core_search\manager::ACCESS_DENIED, $searcharea->check_access($data4record1id));
 843  
 844          $data4record2id = $this->create_default_data_record($data4);
 845          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data4record1id));
 846          $this->assertEquals(\core_search\manager::ACCESS_GRANTED, $searcharea->check_access($data4record2id));
 847      }
 848  
 849      /**
 850       * Test for file contents.
 851       *
 852       * @return void
 853       */
 854      public function test_attach_files() {
 855          global $DB, $USER;
 856  
 857          $fs = get_file_storage();
 858  
 859          // Returns the instance as long as the area is supported.
 860          $searcharea = \core_search\manager::get_search_area($this->databaseentryareaid);
 861          $this->assertInstanceOf('\mod_data\search\entry', $searcharea);
 862  
 863          $user1 = self::getDataGenerator()->create_user();
 864  
 865          $course = self::getDataGenerator()->create_course();
 866  
 867          $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
 868  
 869          $record = new \stdClass();
 870          $record->course = $course->id;
 871  
 872          $this->setAdminUser();
 873  
 874          // Creating database activity instance.
 875          $data1 = $this->getDataGenerator()->create_module('data', $record);
 876  
 877          // Creating file field.
 878          $record = new \stdClass;
 879          $record->type = 'file';
 880          $record->dataid = $data1->id;
 881          $record->required = 0;
 882          $record->name = 'FileFld';
 883          $record->description = 'Just another file field';
 884          $record->param3 = 0;
 885          $record->param1 = '';
 886          $record->param2 = '';
 887  
 888          $data1filefieldid = $DB->insert_record('data_fields', $record);
 889  
 890          // Creating text field.
 891          $record = new \stdClass;
 892          $record->type = 'text';
 893          $record->dataid = $data1->id;
 894          $record->required = 0;
 895          $record->name = 'TextFld';
 896          $record->description = 'Just another text field';
 897          $record->param3 = 0;
 898          $record->param1 = '';
 899          $record->param2 = '';
 900  
 901          $data1textfieldid = $DB->insert_record('data_fields', $record);
 902  
 903          // Creating textarea field.
 904          $record = new \stdClass;
 905          $record->type = 'textarea';
 906          $record->dataid = $data1->id;
 907          $record->required = 0;
 908          $record->name = 'TextAreaFld';
 909          $record->description = 'Just another textarea field';
 910          $record->param1 = '';
 911          $record->param2 = 60;
 912          $record->param3 = 35;
 913          $record->param3 = 1;
 914          $record->param3 = 0;
 915  
 916          $data1textareafieldid = $DB->insert_record('data_fields', $record);
 917  
 918          // Creating 1st entry.
 919          $record = new \stdClass;
 920          $record->userid = $USER->id;
 921          $record->dataid = $data1->id;
 922          $record->groupid = 0;
 923  
 924          $data1record1id = $DB->insert_record('data_records', $record);
 925  
 926          $record = new \stdClass;
 927          $record->fieldid = $data1filefieldid;
 928          $record->recordid = $data1record1id;
 929          $record->content = 'myfile1.txt';
 930          $data1contentfileid1 = $DB->insert_record('data_content', $record);
 931  
 932          $filerecord = [
 933              'contextid' => \context_module::instance($data1->cmid)->id,
 934              'component' => 'mod_data',
 935              'filearea'  => 'content',
 936              'itemid'    => $data1contentfileid1,
 937              'filepath'  => '/',
 938              'filename'  => 'myfile1.txt'
 939          ];
 940  
 941          $data1record1file = $fs->create_file_from_string($filerecord, 'Some contents 1');
 942  
 943          $record = new \stdClass;
 944          $record->fieldid = $data1textfieldid;
 945          $record->recordid = $data1record1id;
 946          $record->content = 'sample text';
 947          $DB->insert_record('data_content', $record);
 948  
 949          $record = new \stdClass;
 950          $record->fieldid = $data1textareafieldid;
 951          $record->recordid = $data1record1id;
 952          $record->content = '<br>sample text<p /><br/>';
 953          $record->content1 = 1;
 954          $DB->insert_record('data_content', $record);
 955  
 956          // Creating 2nd entry.
 957          $record = new \stdClass;
 958          $record->userid = $USER->id;
 959          $record->dataid = $data1->id;
 960          $record->groupid = 0;
 961          $data1record2id = $DB->insert_record('data_records', $record);
 962  
 963          $record = new \stdClass;
 964          $record->fieldid = $data1filefieldid;
 965          $record->recordid = $data1record2id;
 966          $record->content = 'myfile2.txt';
 967          $data1contentfileid2 = $DB->insert_record('data_content', $record);
 968  
 969          $filerecord['itemid'] = $data1contentfileid2;
 970          $filerecord['filename'] = 'myfile2.txt';
 971          $data1record2file = $fs->create_file_from_string($filerecord, 'Some contents 2');
 972  
 973          $record = new \stdClass;
 974          $record->fieldid = $data1textfieldid;
 975          $record->recordid = $data1record2id;
 976          $record->content = 'sample text';
 977          $DB->insert_record('data_content', $record);
 978  
 979          $record = new \stdClass;
 980          $record->fieldid = $data1textareafieldid;
 981          $record->recordid = $data1record2id;
 982          $record->content = '<br>sample text<p /><br/>';
 983          $record->content1 = 1;
 984          $DB->insert_record('data_content', $record);
 985  
 986          // Now get all the posts and see if they have the right files attached.
 987          $searcharea = \core_search\manager::get_search_area($this->databaseentryareaid);
 988          $recordset = $searcharea->get_recordset_by_timestamp(0);
 989          $nrecords = 0;
 990          foreach ($recordset as $record) {
 991              $doc = $searcharea->get_document($record);
 992              $searcharea->attach_files($doc);
 993              $files = $doc->get_files();
 994              // Now check that each doc has the right files on it.
 995              switch ($doc->get('itemid')) {
 996                  case ($data1record1id):
 997                      $this->assertCount(1, $files);
 998                      $this->assertEquals($data1record1file->get_id(), $files[$data1record1file->get_id()]->get_id());
 999                      break;
1000                  case ($data1record2id):
1001                      $this->assertCount(1, $files);
1002                      $this->assertEquals($data1record2file->get_id(), $files[$data1record2file->get_id()]->get_id());
1003                      break;
1004                  default:
1005                      $this->fail('Unexpected entry returned');
1006                      break;
1007              }
1008              $nrecords++;
1009          }
1010          $recordset->close();
1011          $this->assertEquals(2, $nrecords);
1012      }
1013  
1014      /**
1015       * Creates default fields for a database instance
1016       *
1017       * @param array $fieldtypes
1018       * @param mod_data $data
1019       * @return void
1020       */
1021      protected function create_default_data_fields($fieldtypes, $data) {
1022          $count = 1;
1023  
1024          // Creating test Fields with default parameter values.
1025          foreach ($fieldtypes as $fieldtype) {
1026  
1027              // Creating variables dynamically.
1028              $fieldname = 'field-'.$count;
1029              $record = new \stdClass();
1030              $record->name = $fieldname;
1031  
1032              if (is_array($fieldtype)) {
1033                  $record->type = $fieldtype[0];
1034                  $record->required = $fieldtype[1];
1035              } else {
1036                  $record->type = $fieldtype;
1037                  $record->required = 0;
1038              }
1039  
1040              ${$fieldname} = $this->getDataGenerator()->get_plugin_generator('mod_data')->create_field($record, $data);
1041              $count++;
1042          }
1043      }
1044  
1045      /**
1046       * Creates default database entry content values for default field param values
1047       *
1048       * @param mod_data $data
1049       * @param int $groupid
1050       * @return int
1051       */
1052      protected function create_default_data_record($data, $groupid = 0) {
1053          global $DB;
1054  
1055          $fields = $DB->get_records('data_fields', array('dataid' => $data->id));
1056  
1057          $fieldcontents = array();
1058          foreach ($fields as $fieldrecord) {
1059              switch ($fieldrecord->type) {
1060                  case 'checkbox':
1061                      $fieldcontents[$fieldrecord->id] = array('opt1', 'opt2', 'opt3', 'opt4');
1062                      break;
1063  
1064                  case 'multimenu':
1065                      $fieldcontents[$fieldrecord->id] = array('multimenu1', 'multimenu2', 'multimenu3', 'multimenu4');
1066                      break;
1067  
1068                  case 'date':
1069                      $fieldcontents[$fieldrecord->id] = '27-07-2016';
1070                      break;
1071  
1072                  case 'menu':
1073                      $fieldcontents[$fieldrecord->id] = 'menu1';
1074                      break;
1075  
1076                  case 'radiobutton':
1077                      $fieldcontents[$fieldrecord->id] = 'radioopt1';
1078                      break;
1079  
1080                  case 'number':
1081                      $fieldcontents[$fieldrecord->id] = '12345';
1082                      break;
1083  
1084                  case 'text':
1085                      $fieldcontents[$fieldrecord->id] = 'text for testing';
1086                      break;
1087  
1088                  case 'textarea':
1089                      $fieldcontents[$fieldrecord->id] = '<p>text area testing<br /></p>';
1090                      break;
1091  
1092                  case 'url':
1093                      $fieldcontents[$fieldrecord->id] = array('example.url', 'sampleurl');
1094                      break;
1095  
1096                  default:
1097                      $this->fail('Unexpected field type');
1098                      break;
1099              }
1100  
1101          }
1102  
1103          return $this->getDataGenerator()->get_plugin_generator('mod_data')->create_entry($data, $fieldcontents, $groupid);
1104      }
1105  
1106      /**
1107       * Creates default database entry content values for default field param values
1108       *
1109       * @param int $recordid
1110       * @return stdClass
1111       */
1112      protected function get_entry_for_id($recordid ) {
1113          global $DB;
1114  
1115          $sql = "SELECT dr.*, d.course
1116                    FROM {data_records} dr
1117                    JOIN {data} d ON d.id = dr.dataid
1118                   WHERE dr.id = :drid";
1119          return $DB->get_record_sql($sql, array('drid' => $recordid));
1120      }
1121  
1122  }