MDL-31989 search: New search areas
[moodle.git] / mod / forum / classes / search / post.php
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/>.
17 /**
18  * Forum posts search area
19  *
20  * @package    mod_forum
21  * @copyright  2015 David Monllao {@link http://www.davidmonllao.com}
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 namespace mod_forum\search;
27 defined('MOODLE_INTERNAL') || die();
29 require_once($CFG->dirroot . '/mod/forum/lib.php');
31 /**
32  * Forum posts search area.
33  *
34  * @package    mod_forum
35  * @copyright  2015 David Monllao {@link http://www.davidmonllao.com}
36  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37  */
38 class post extends \core_search\area\base_mod {
40     /**
41      * @var array Internal quick static cache.
42      */
43     protected $forumsdata = array();
45     /**
46      * @var array Internal quick static cache.
47      */
48     protected $discussionsdata = array();
50     /**
51      * @var array Internal quick static cache.
52      */
53     protected $postsdata = array();
55     /**
56      * Returns recordset containing required data for indexing forum posts.
57      *
58      * @param int $modifiedfrom timestamp
59      * @return moodle_recordset
60      */
61     public function get_recordset_by_timestamp($modifiedfrom = 0) {
62         global $DB;
64         $sql = 'SELECT fp.*, f.id AS forumid, f.course AS courseid
65                   FROM {forum_posts} fp
66                   JOIN {forum_discussions} fd ON fd.id = fp.discussion
67                   JOIN {forum} f ON f.id = fd.forum
68                 WHERE fp.modified >= ? ORDER BY fp.modified ASC';
69         return $DB->get_recordset_sql($sql, array($modifiedfrom));
70     }
72     /**
73      * Returns the document associated with this post id.
74      *
75      * @param stdClass $record Post info.
76      * @return \core_search\document
77      */
78     public function get_document($record) {
80         try {
81             $cm = $this->get_cm('forum', $record->forumid, $record->courseid);
82             $context = \context_module::instance($cm->id);
83         } catch (\dml_missing_record_exception $ex) {
84             // Notify it as we run here as admin, we should see everything.
85             debugging('Error retrieving ' . $this->areaid . ' ' . $record->id . ' document, not all required data is available: ' .
86                 $ex->getMessage(), DEBUG_DEVELOPER);
87             return false;
88         } catch (\dml_exception $ex) {
89             // Notify it as we run here as admin, we should see everything.
90             debugging('Error retrieving ' . $this->areaid . ' ' . $record->id . ' document: ' . $ex->getMessage(), DEBUG_DEVELOPER);
91             return false;
92         }
94         // Prepare associative array with data from DB.
95         $doc = \core_search\document_factory::instance($record->id, $this->componentname, $this->areaname);
96         $doc->set('title', $record->subject);
97         $doc->set('content', editor_input_to_text($record->message, $record->messageformat));
98         $doc->set('contextid', $context->id);
99         $doc->set('type', \core_search\manager::TYPE_TEXT);
100         $doc->set('courseid', $record->courseid);
101         $doc->set('userid', $record->userid);
102         $doc->set('modified', $record->modified);
104         return $doc;
105     }
107     /**
108      * Whether the user can access the document or not.
109      *
110      * @throws \dml_missing_record_exception
111      * @throws \dml_exception
112      * @param int $id Forum post id
113      * @return bool
114      */
115     public function check_access($id) {
116         global $USER;
118         try {
119             $post = $this->get_post($id);
120             $forum = $this->get_forum($post->forum);
121             $discussion = $this->get_discussion($post->discussion);
122             $cminfo = $this->get_cm('forum', $forum->id, $forum->course);
123             $cm = $cminfo->get_course_module_record();
124         } catch (\dml_missing_record_exception $ex) {
125             return \core_search\manager::ACCESS_DELETED;
126         } catch (\dml_exception $ex) {
127             return \core_search\manager::ACCESS_DENIED;
128         }
130         // Recheck uservisible although it should have already been checked in core_search.
131         if ($cminfo->uservisible === false) {
132             return \core_search\manager::ACCESS_DENIED;
133         }
135         if (!forum_user_can_see_post($forum, $discussion, $post, $USER, $cm)) {
136             return \core_search\manager::ACCESS_DENIED;
137         }
139         return \core_search\manager::ACCESS_GRANTED;
140     }
142     /**
143      * Link to the forum post discussion
144      *
145      * @param \core_search\document $doc
146      * @return \moodle_url
147      */
148     public function get_doc_url(\core_search\document $doc) {
149         // The post is already in static cache, we fetch it in self::search_access.
150         $post = $this->get_post($doc->get('itemid'));
151         return new \moodle_url('/mod/forum/discuss.php', array('d' => $post->discussion));
152     }
154     /**
155      * Link to the forum.
156      *
157      * @param \core_search\document $doc
158      * @return \moodle_url
159      */
160     public function get_context_url(\core_search\document $doc) {
161         $contextmodule = \context::instance_by_id($doc->get('contextid'));
162         return new \moodle_url('/mod/forum/view.php', array('id' => $contextmodule->instanceid));
163     }
165     /**
166      * Returns the specified forum post from its internal cache.
167      *
168      * @throws \dml_missing_record_exception
169      * @param int $postid
170      * @return stdClass
171      */
172     protected function get_post($postid) {
173         if (empty($this->postsdata[$postid])) {
174             $this->postsdata[$postid] = forum_get_post_full($postid);
175             if (!$this->postsdata[$postid]) {
176                 throw new \dml_missing_record_exception('forum_posts');
177             }
178         }
179         return $this->postsdata[$postid];
180     }
182     /**
183      * Returns the specified forum checking the internal cache.
184      *
185      * Store minimal information as this might grow.
186      *
187      * @throws \dml_exception
188      * @param int $forumid
189      * @return stdClass
190      */
191     protected function get_forum($forumid) {
192         global $DB;
194         if (empty($this->forumsdata[$forumid])) {
195             $this->forumsdata[$forumid] = $DB->get_record('forum', array('id' => $forumid), '*', MUST_EXIST);
196         }
197         return $this->forumsdata[$forumid];
198     }
200     /**
201      * Returns the discussion checking the internal cache.
202      *
203      * @throws \dml_missing_record_exception
204      * @param int $discussionid
205      * @return stdClass
206      */
207     protected function get_discussion($discussionid) {
208         global $DB;
210         if (empty($this->discussionsdata[$discussionid])) {
211             $this->discussionsdata[$discussionid] = $DB->get_record('forum_discussions',
212                 array('id' => $discussionid), '*', MUST_EXIST);
213         }
214         return $this->discussionsdata[$discussionid];
215     }