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.
   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   * Search area for mod_book chapters.
  19   *
  20   * @package    mod_book
  21   * @copyright  2016 Eric Merrill {@link http://www.merrilldigital.com}
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace mod_book\search;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  /**
  30   * Search area for mod_book chapters.
  31   *
  32   * @package    mod_book
  33   * @copyright  2016 Eric Merrill {@link http://www.merrilldigital.com}
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class chapter extends \core_search\base_mod {
  37      /**
  38       * @var array Cache of book records.
  39       */
  40      protected $bookscache = array();
  41  
  42      /**
  43       * Returns a recordset with all required chapter information.
  44       *
  45       * @param int $modifiedfrom
  46       * @param \context|null $context Optional context to restrict scope of returned results
  47       * @return moodle_recordset|null Recordset (or null if no results)
  48       */
  49      public function get_document_recordset($modifiedfrom = 0, \context $context = null) {
  50          global $DB;
  51  
  52          list ($contextjoin, $contextparams) = $this->get_context_restriction_sql(
  53                  $context, 'book', 'b');
  54          if ($contextjoin === null) {
  55              return null;
  56          }
  57  
  58          $sql = "SELECT c.*, b.id AS bookid, b.course AS courseid
  59                    FROM {book_chapters} c
  60                    JOIN {book} b ON b.id = c.bookid
  61            $contextjoin
  62                   WHERE c.timemodified >= ? ORDER BY c.timemodified ASC";
  63          return $DB->get_recordset_sql($sql, array_merge($contextparams, [$modifiedfrom]));
  64      }
  65  
  66      /**
  67       * Returns the document for a particular chapter.
  68       *
  69       * @param \stdClass $record A record containing, at least, the indexed document id and a modified timestamp
  70       * @param array     $options Options for document creation
  71       * @return \core_search\document
  72       */
  73      public function get_document($record, $options = array()) {
  74          try {
  75              $cm = $this->get_cm('book', $record->bookid, $record->courseid);
  76              $context = \context_module::instance($cm->id);
  77          } catch (\dml_missing_record_exception $ex) {
  78              // Notify it as we run here as admin, we should see everything.
  79              debugging('Error retrieving ' . $this->areaid . ' ' . $record->id . ' document, not all required data is available: ' .
  80                  $ex->getMessage(), DEBUG_DEVELOPER);
  81              return false;
  82          } catch (\dml_exception $ex) {
  83              // Notify it as we run here as admin, we should see everything.
  84              debugging('Error retrieving ' . $this->areaid . ' ' . $record->id . ' document: ' . $ex->getMessage(), DEBUG_DEVELOPER);
  85              return false;
  86          }
  87  
  88          // Prepare associative array with data from DB.
  89          $doc = \core_search\document_factory::instance($record->id, $this->componentname, $this->areaname);
  90          $doc->set('title', content_to_text($record->title, false));
  91          $doc->set('content', content_to_text($record->content, $record->contentformat));
  92          $doc->set('contextid', $context->id);
  93          $doc->set('courseid', $record->courseid);
  94          $doc->set('owneruserid', \core_search\manager::NO_OWNER_ID);
  95          $doc->set('modified', $record->timemodified);
  96  
  97          // Check if this document should be considered new.
  98          if (isset($options['lastindexedtime']) && ($options['lastindexedtime'] < $record->timecreated)) {
  99              // If the document was created after the last index time, it must be new.
 100              $doc->set_is_new(true);
 101          }
 102  
 103          return $doc;
 104      }
 105  
 106      /**
 107       * Can the current user see the document.
 108       *
 109       * @param int $id The internal search area entity id.
 110       * @return bool True if the user can see it, false otherwise
 111       */
 112      public function check_access($id) {
 113          global $DB;
 114  
 115          try {
 116              $chapter = $DB->get_record('book_chapters', array('id' => $id), '*', MUST_EXIST);
 117              if (!isset($this->bookscache[$chapter->bookid])) {
 118                  $this->bookscache[$chapter->bookid] = $DB->get_record('book', array('id' => $chapter->bookid), '*', MUST_EXIST);
 119              }
 120              $book = $this->bookscache[$chapter->bookid];
 121              $cminfo = $this->get_cm('book', $chapter->bookid, $book->course);
 122          } catch (\dml_missing_record_exception $ex) {
 123              return \core_search\manager::ACCESS_DELETED;
 124          } catch (\dml_exception $ex) {
 125              return \core_search\manager::ACCESS_DENIED;
 126          }
 127  
 128          // Recheck uservisible although it should have already been checked in core_search.
 129          if ($cminfo->uservisible === false) {
 130              return \core_search\manager::ACCESS_DENIED;
 131          }
 132  
 133          $context = \context_module::instance($cminfo->id);
 134  
 135          if (!has_capability('mod/book:read', $context)) {
 136              return \core_search\manager::ACCESS_DENIED;
 137          }
 138  
 139          // See if the user can see chapter if it is hidden.
 140          if ($chapter->hidden && !has_capability('mod/book:viewhiddenchapters', $context)) {
 141              return \core_search\manager::ACCESS_DENIED;
 142          }
 143  
 144          return \core_search\manager::ACCESS_GRANTED;
 145      }
 146  
 147      /**
 148       * Returns a url to the chapter.
 149       *
 150       * @param \core_search\document $doc
 151       * @return \moodle_url
 152       */
 153      public function get_doc_url(\core_search\document $doc) {
 154          $contextmodule = \context::instance_by_id($doc->get('contextid'));
 155          $params = array('id' => $contextmodule->instanceid, 'chapterid' => $doc->get('itemid'));
 156          return new \moodle_url('/mod/book/view.php', $params);
 157      }
 158  
 159      /**
 160       * Returns a url to the book.
 161       *
 162       * @param \core_search\document $doc
 163       * @return \moodle_url
 164       */
 165      public function get_context_url(\core_search\document $doc) {
 166          $contextmodule = \context::instance_by_id($doc->get('contextid'));
 167          return new \moodle_url('/mod/book/view.php', array('id' => $contextmodule->instanceid));
 168      }
 169  
 170      /**
 171       * Returns true if this area uses file indexing.
 172       *
 173       * @return bool
 174       */
 175      public function uses_file_indexing() {
 176          return true;
 177      }
 178  
 179      /**
 180       * Return the context info required to index files for
 181       * this search area.
 182       *
 183       * @return array
 184       */
 185      public function get_search_fileareas() {
 186          $fileareas = array('chapter'); // Filearea.
 187  
 188          return $fileareas;
 189      }
 190  }