MDL-36741 mod_forum: fixed SQL that was generated when either timed posts or groups...
[moodle.git] / mod / forum / rsslib.php
CommitLineData
1adbd2c3 1<?php
8adcb49f 2
8f685009
SH
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
18/**
13d1c9ed
JF
19 * This file adds support to rss feeds generation
20 *
21 * @package mod_forum
22 * @category rss
23 * @copyright 2001 Eloy Lafuente (stronk7) http://contiento.com
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 */
fcce139a
AD
26
27/**
28 * Returns the path to the cached rss feed contents. Creates/updates the cache if necessary.
13d1c9ed
JF
29 * @param stdClass $context the context
30 * @param array $args the arguments received in the url
fcce139a 31 * @return string the full path to the cached RSS feed directory. Null if there is a problem.
8f685009 32 */
274f9840 33function forum_rss_get_feed($context, $args) {
7ea78d9f 34 global $CFG, $DB, $USER;
8f685009 35
fcce139a 36 $status = true;
8adcb49f 37
fcce139a
AD
38 //are RSS feeds enabled?
39 if (empty($CFG->forum_enablerssfeeds)) {
40 debugging('DISABLED (module configuration)');
41 return null;
42 }
8adcb49f 43
e65ce4c1 44 $forumid = clean_param($args[3], PARAM_INT);
4df53223 45 $cm = get_coursemodule_from_instance('forum', $forumid, 0, false, MUST_EXIST);
17ec4bf0 46 $modcontext = context_module::instance($cm->id);
4df53223 47
af89cdd4
DP
48 //context id from db should match the submitted one
49 if ($context->id != $modcontext->id || !has_capability('mod/forum:viewdiscussion', $modcontext)) {
50 return null;
4df53223
AD
51 }
52
fcce139a 53 $forum = $DB->get_record('forum', array('id' => $forumid), '*', MUST_EXIST);
43b92251 54 if (!rss_enabled_for_mod('forum', $forum)) {
fcce139a 55 return null;
8adcb49f 56 }
57
fcce139a 58 //the sql that will retreive the data for the feed and be hashed to get the cache filename
5fe65fd4 59 list($sql, $params) = forum_rss_get_sql($forum, $cm);
83da3d28 60
17ec4bf0 61 // Hash the sql to get the cache file name.
5fe65fd4 62 $filename = rss_get_file_name($forum, $sql, $params);
43b92251 63 $cachedfilepath = rss_get_file_full_name('mod_forum', $filename);
fcce139a
AD
64
65 //Is the cache out of date?
66 $cachedfilelastmodified = 0;
67 if (file_exists($cachedfilepath)) {
68 $cachedfilelastmodified = filemtime($cachedfilepath);
69 }
da8ae44e
AD
70 //if the cache is more than 60 seconds old and there's new stuff
71 $dontrecheckcutoff = time()-60;
5fe65fd4 72 if ($dontrecheckcutoff > $cachedfilelastmodified && forum_rss_newstuff($forum, $cm, $cachedfilelastmodified)) {
fcce139a 73 //need to regenerate the cached version
5fe65fd4 74 $result = forum_rss_feed_contents($forum, $sql, $params, $modcontext);
fcce139a 75 if (!empty($result)) {
43b92251 76 $status = rss_save_file('mod_forum',$filename,$result);
83da3d28 77 }
78 }
79
fcce139a
AD
80 //return the path to the cached version
81 return $cachedfilepath;
82}
83da3d28 83
fcce139a
AD
84/**
85 * Given a forum object, deletes all cached RSS files associated with it.
86 *
13d1c9ed 87 * @param stdClass $forum
fcce139a
AD
88 */
89function forum_rss_delete_file($forum) {
43b92251 90 rss_delete_file('mod_forum', $forum);
fcce139a
AD
91}
92
93///////////////////////////////////////////////////////
94//Utility functions
95
96/**
97 * If there is new stuff in the forum since $time this returns true
98 * Otherwise it returns false.
99 *
13d1c9ed
JF
100 * @param stdClass $forum the forum object
101 * @param stdClass $cm Course Module object
102 * @param int $time check for items since this epoch timestamp
103 * @return bool True for new items
fcce139a
AD
104 */
105function forum_rss_newstuff($forum, $cm, $time) {
106 global $DB;
107
5fe65fd4
MN
108 list($sql, $params) = forum_rss_get_sql($forum, $cm, $time);
109 if ($DB->count_records_sql($sql, $params) > 0) {
110 return true;
111 }
fcce139a 112
5fe65fd4 113 return false;
fcce139a
AD
114}
115
13d1c9ed
JF
116/**
117 * Determines which type of SQL query is required, one for posts or one for discussions, and returns the appropriate query
118 *
119 * @param stdClass $forum the forum object
120 * @param stdClass $cm Course Module object
121 * @param int $time check for items since this epoch timestamp
122 * @return string the SQL query to be used to get the Discussion/Post details from the forum table of the database
123 */
fcce139a 124function forum_rss_get_sql($forum, $cm, $time=0) {
5fe65fd4
MN
125 if ($forum->rsstype == 1) { // Discussion RSS
126 return forum_rss_feed_discussions_sql($forum, $cm, $time);
127 } else { // Post RSS
128 return forum_rss_feed_posts_sql($forum, $cm, $time);
6069e206 129 }
fcce139a 130}
8f0cd6ef 131
13d1c9ed
JF
132/**
133 * Generates the SQL query used to get the Discussion details from the forum table of the database
134 *
135 * @param stdClass $forum the forum object
136 * @param stdClass $cm Course Module object
137 * @param int $newsince check for items since this epoch timestamp
138 * @return string the SQL query to be used to get the Discussion details from the forum table of the database
139 */
fcce139a
AD
140function forum_rss_feed_discussions_sql($forum, $cm, $newsince=0) {
141 global $CFG, $DB, $USER;
142
143 $timelimit = '';
144
145 $modcontext = null;
146
147 $now = round(time(), -2);
5fe65fd4 148 $params = array();
fcce139a 149
17ec4bf0 150 $modcontext = context_module::instance($cm->id);
fcce139a
AD
151
152 if (!empty($CFG->forum_enabletimedposts)) { /// Users must fulfill timed posts
153 if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
154 $timelimit = " AND ((d.timestart <= :now1 AND (d.timeend = 0 OR d.timeend > :now2))";
155 $params['now1'] = $now;
156 $params['now2'] = $now;
157 if (isloggedin()) {
158 $timelimit .= " OR d.userid = :userid";
159 $params['userid'] = $USER->id;
8adcb49f 160 }
fcce139a 161 $timelimit .= ")";
8adcb49f 162 }
8adcb49f 163 }
164
5fe65fd4 165 // Do we only want new posts?
fcce139a 166 if ($newsince) {
5fe65fd4
MN
167 $params['newsince'] = $newsince;
168 $newsince = " AND p.modified > :newsince";
fcce139a
AD
169 } else {
170 $newsince = '';
171 }
8adcb49f 172
5fe65fd4
MN
173 // Get group enforcing SQL.
174 $groupmode = groups_get_activity_groupmode($cm);
fcce139a 175 $currentgroup = groups_get_activity_group($cm);
5fe65fd4 176 list($groupselect, $groupparams) = forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext);
8adcb49f 177
5fe65fd4
MN
178 // Add the groupparams to the params array.
179 $params = array_merge($params, $groupparams);
8adcb49f 180
fcce139a 181 $forumsort = "d.timemodified DESC";
af89cdd4 182 $postdata = "p.id AS postid, p.subject, p.created as postcreated, p.modified, p.discussion, p.userid, p.message as postmessage, p.messageformat AS postformat, p.messagetrust AS posttrust";
fcce139a
AD
183
184 $sql = "SELECT $postdata, d.id as discussionid, d.name as discussionname, d.timemodified, d.usermodified, d.groupid, d.timestart, d.timeend,
185 u.firstname as userfirstname, u.lastname as userlastname, u.email, u.picture, u.imagealt
186 FROM {forum_discussions} d
187 JOIN {forum_posts} p ON p.discussion = d.id
188 JOIN {user} u ON p.userid = u.id
189 WHERE d.forum = {$forum->id} AND p.parent = 0
190 $timelimit $groupselect $newsince
191 ORDER BY $forumsort";
5fe65fd4 192 return array($sql, $params);
fcce139a
AD
193}
194
13d1c9ed
JF
195/**
196 * Generates the SQL query used to get the Post details from the forum table of the database
197 *
198 * @param stdClass $forum the forum object
199 * @param stdClass $cm Course Module object
200 * @param int $newsince check for items since this epoch timestamp
201 * @return string the SQL query to be used to get the Post details from the forum table of the database
202 */
fcce139a 203function forum_rss_feed_posts_sql($forum, $cm, $newsince=0) {
17ec4bf0 204 $modcontext = context_module::instance($cm->id);
46d39cf3 205
5fe65fd4
MN
206 // Get group enforcement SQL.
207 $groupmode = groups_get_activity_groupmode($cm);
fcce139a 208 $currentgroup = groups_get_activity_group($cm);
5fe65fd4 209 $params = array();
fcce139a 210
5fe65fd4 211 list($groupselect, $groupparams) = forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext);
fcce139a 212
5fe65fd4
MN
213 // Add the groupparams to the params array.
214 $params = array_merge($params, $groupparams);
6069e206 215
5fe65fd4 216 // Do we only want new posts?
fcce139a 217 if ($newsince) {
5fe65fd4
MN
218 $params['newsince'] = $newsince;
219 $newsince = " AND p.modified > :newsince";
fcce139a
AD
220 } else {
221 $newsince = '';
222 }
ec41cb3c 223
fcce139a
AD
224 $sql = "SELECT p.id AS postid,
225 d.id AS discussionid,
226 d.name AS discussionname,
227 u.id AS userid,
228 u.firstname AS userfirstname,
229 u.lastname AS userlastname,
230 p.subject AS postsubject,
231 p.message AS postmessage,
232 p.created AS postcreated,
233 p.messageformat AS postformat,
234 p.messagetrust AS posttrust
235 FROM {forum_discussions} d,
236 {forum_posts} p,
237 {user} u
238 WHERE d.forum = {$forum->id} AND
239 p.discussion = d.id AND
240 u.id = p.userid $newsince
241 $groupselect
242 ORDER BY p.created desc";
243
5fe65fd4 244 return array($sql, $params);
fcce139a
AD
245}
246
13d1c9ed
JF
247/**
248 * Retrieve the correct SQL snippet for group-only forums
249 *
250 * @param stdClass $cm Course Module object
251 * @param int $groupmode the mode in which the forum's groups are operating
252 * @param bool $currentgroup true if the user is from the a group enabled on the forum
253 * @param stdClass $modcontext The context instance of the forum module
254 * @return string SQL Query for group details of the forum
255 */
fcce139a
AD
256function forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext=null) {
257 $groupselect = '';
5fe65fd4 258 $params = array();
fcce139a
AD
259
260 if ($groupmode) {
261 if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
262 if ($currentgroup) {
263 $groupselect = "AND (d.groupid = :groupid OR d.groupid = -1)";
264 $params['groupid'] = $currentgroup;
265 }
266 } else {
5fe65fd4 267 // Separate groups without access all.
fcce139a
AD
268 if ($currentgroup) {
269 $groupselect = "AND (d.groupid = :groupid OR d.groupid = -1)";
270 $params['groupid'] = $currentgroup;
271 } else {
272 $groupselect = "AND d.groupid = -1";
8adcb49f 273 }
274 }
8adcb49f 275 }
8f0cd6ef 276
5fe65fd4 277 return array($groupselect, $params);
fcce139a 278}
8adcb49f 279
fcce139a
AD
280/**
281 * This function return the XML rss contents about the forum
282 * It returns false if something is wrong
283 *
13d1c9ed 284 * @param stdClass $forum the forum object
5fe65fd4
MN
285 * @param string $sql the SQL used to retrieve the contents from the database
286 * @param array $params the SQL parameters used
af89cdd4 287 * @param object $context the context this forum relates to
13d1c9ed
JF
288 * @return bool|string false if the contents is empty, otherwise the contents of the feed is returned
289 *
290 * @Todo MDL-31129 implement post attachment handling
fcce139a 291 */
7ea78d9f 292
5fe65fd4 293function forum_rss_feed_contents($forum, $sql, $params, $context) {
7ea78d9f
ARN
294 global $CFG, $DB, $USER;
295
fcce139a
AD
296 $status = true;
297
fcce139a
AD
298 $recs = $DB->get_recordset_sql($sql, $params, 0, $forum->rssarticles);
299
300 //set a flag. Are we displaying discussions or posts?
301 $isdiscussion = true;
302 if (!empty($forum->rsstype) && $forum->rsstype!=1) {
cc771939 303 $isdiscussion = false;
fcce139a 304 }
318f2100 305
7ea78d9f
ARN
306 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
307 print_error('invalidcoursemodule');
308 }
7ea78d9f 309
39790bd8 310 $formatoptions = new stdClass();
fcce139a
AD
311 $items = array();
312 foreach ($recs as $rec) {
39790bd8
PS
313 $item = new stdClass();
314 $user = new stdClass();
7ea78d9f
ARN
315
316 if ($isdiscussion && !forum_user_can_see_discussion($forum, $rec->discussionid, $context)) {
17ec4bf0 317 // This is a discussion which the user has no permission to view
7ea78d9f 318 $item->title = get_string('forumsubjecthidden', 'forum');
17ec4bf0 319 $message = get_string('forumbodyhidden', 'forum');
7ea78d9f
ARN
320 $item->author = get_string('forumauthorhidden', 'forum');
321 } else if (!$isdiscussion && !forum_user_can_see_post($forum, $rec->discussionid, $rec->postid, $USER, $cm)) {
17ec4bf0 322 // This is a post which the user has no permission to view
7ea78d9f 323 $item->title = get_string('forumsubjecthidden', 'forum');
17ec4bf0 324 $message = get_string('forumbodyhidden', 'forum');
7ea78d9f 325 $item->author = get_string('forumauthorhidden', 'forum');
7c810d07 326 } else {
7ea78d9f
ARN
327 // The user must have permission to view
328 if ($isdiscussion && !empty($rec->discussionname)) {
329 $item->title = format_string($rec->discussionname);
330 } else if (!empty($rec->postsubject)) {
331 $item->title = format_string($rec->postsubject);
332 } else {
333 //we should have an item title by now but if we dont somehow then substitute something somewhat meaningful
334 $item->title = format_string($forum->name.' '.userdate($rec->postcreated,get_string('strftimedatetimeshort', 'langconfig')));
335 }
336 $user->firstname = $rec->userfirstname;
337 $user->lastname = $rec->userlastname;
338 $item->author = fullname($user);
17ec4bf0
AG
339 $message = file_rewrite_pluginfile_urls($rec->postmessage, 'pluginfile.php', $context->id,
340 'mod_forum', 'post', $rec->postid);
7ea78d9f 341 $formatoptions->trusted = $rec->posttrust;
7c810d07 342 }
7ea78d9f 343
fcce139a
AD
344 if ($isdiscussion) {
345 $item->link = $CFG->wwwroot."/mod/forum/discuss.php?d=".$rec->discussionid;
346 } else {
8f0cd6ef 347 $item->link = $CFG->wwwroot."/mod/forum/discuss.php?d=".$rec->discussionid."&parent=".$rec->postid;
fcce139a 348 }
410a24c0 349
fcce139a 350 $formatoptions->trusted = $rec->posttrust;
af89cdd4 351 $item->description = format_text($message, $rec->postformat, $formatoptions, $forum->course);
410a24c0 352
13d1c9ed 353 //TODO: MDL-31129 implement post attachment handling
fcce139a 354 /*if (!$isdiscussion) {
318f2100 355 $post_file_area_name = str_replace('//', '/', "$forum->course/$CFG->moddata/forum/$forum->id/$rec->postid");
410a24c0 356 $post_files = get_directory_list("$CFG->dataroot/$post_file_area_name");
4e445355 357
358 if (!empty($post_files)) {
410a24c0 359 $item->attachments = array();
410a24c0 360 }
fcce139a 361 }*/
17ec4bf0 362 $item->pubdate = $rec->postcreated;
46d39cf3 363
fcce139a
AD
364 $items[] = $item;
365 }
366 $recs->close();
410a24c0 367
fcce139a
AD
368
369 if (!empty($items)) {
370 //First the RSS header
371 $header = rss_standard_header(strip_tags(format_string($forum->name,true)),
372 $CFG->wwwroot."/mod/forum/view.php?f=".$forum->id,
373 format_string($forum->intro,true)); // TODO: fix format
374 //Now all the rss items
375 if (!empty($header)) {
376 $articles = rss_add_items($items);
377 }
378 //Now the RSS footer
379 if (!empty($header) && !empty($articles)) {
380 $footer = rss_standard_footer();
8adcb49f 381 }
fcce139a
AD
382 //Now, if everything is ok, concatenate it
383 if (!empty($header) && !empty($articles) && !empty($footer)) {
384 $status = $header.$articles.$footer;
385 } else {
386 $status = false;
387 }
388 } else {
389 $status = false;
390 }
391
392 return $status;
393}