MDL-36741 mod_forum: fixed the function forum_rss_newstuff that was using count_recor...
[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 }
941294ef 70 // Used to determine if we need to generate a new RSS feed.
da8ae44e 71 $dontrecheckcutoff = time()-60;
941294ef
MN
72 // If it hasn't been generated we will need to create it, otherwise only update
73 // if there is new stuff to show and it is older than the cut off date set above.
74 if (($cachedfilelastmodified == 0) || (($dontrecheckcutoff > $cachedfilelastmodified) &&
75 forum_rss_newstuff($forum, $cm, $cachedfilelastmodified))) {
76 // Need to regenerate the cached version.
5fe65fd4 77 $result = forum_rss_feed_contents($forum, $sql, $params, $modcontext);
941294ef 78 $status = rss_save_file('mod_forum', $filename, $result);
83da3d28 79 }
80
fcce139a
AD
81 //return the path to the cached version
82 return $cachedfilepath;
83}
83da3d28 84
fcce139a
AD
85/**
86 * Given a forum object, deletes all cached RSS files associated with it.
87 *
13d1c9ed 88 * @param stdClass $forum
fcce139a
AD
89 */
90function forum_rss_delete_file($forum) {
43b92251 91 rss_delete_file('mod_forum', $forum);
fcce139a
AD
92}
93
94///////////////////////////////////////////////////////
95//Utility functions
96
97/**
98 * If there is new stuff in the forum since $time this returns true
99 * Otherwise it returns false.
100 *
13d1c9ed
JF
101 * @param stdClass $forum the forum object
102 * @param stdClass $cm Course Module object
103 * @param int $time check for items since this epoch timestamp
104 * @return bool True for new items
fcce139a
AD
105 */
106function forum_rss_newstuff($forum, $cm, $time) {
107 global $DB;
108
5fe65fd4 109 list($sql, $params) = forum_rss_get_sql($forum, $cm, $time);
fcce139a 110
941294ef 111 return $DB->record_exists_sql($sql, $params);
fcce139a
AD
112}
113
13d1c9ed
JF
114/**
115 * Determines which type of SQL query is required, one for posts or one for discussions, and returns the appropriate query
116 *
117 * @param stdClass $forum the forum object
118 * @param stdClass $cm Course Module object
119 * @param int $time check for items since this epoch timestamp
120 * @return string the SQL query to be used to get the Discussion/Post details from the forum table of the database
121 */
fcce139a 122function forum_rss_get_sql($forum, $cm, $time=0) {
5fe65fd4
MN
123 if ($forum->rsstype == 1) { // Discussion RSS
124 return forum_rss_feed_discussions_sql($forum, $cm, $time);
125 } else { // Post RSS
126 return forum_rss_feed_posts_sql($forum, $cm, $time);
6069e206 127 }
fcce139a 128}
8f0cd6ef 129
13d1c9ed
JF
130/**
131 * Generates the SQL query used to get the Discussion details from the forum table of the database
132 *
133 * @param stdClass $forum the forum object
134 * @param stdClass $cm Course Module object
135 * @param int $newsince check for items since this epoch timestamp
136 * @return string the SQL query to be used to get the Discussion details from the forum table of the database
137 */
fcce139a
AD
138function forum_rss_feed_discussions_sql($forum, $cm, $newsince=0) {
139 global $CFG, $DB, $USER;
140
141 $timelimit = '';
142
143 $modcontext = null;
144
145 $now = round(time(), -2);
5fe65fd4 146 $params = array();
fcce139a 147
17ec4bf0 148 $modcontext = context_module::instance($cm->id);
fcce139a
AD
149
150 if (!empty($CFG->forum_enabletimedposts)) { /// Users must fulfill timed posts
151 if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
152 $timelimit = " AND ((d.timestart <= :now1 AND (d.timeend = 0 OR d.timeend > :now2))";
153 $params['now1'] = $now;
154 $params['now2'] = $now;
155 if (isloggedin()) {
156 $timelimit .= " OR d.userid = :userid";
157 $params['userid'] = $USER->id;
8adcb49f 158 }
fcce139a 159 $timelimit .= ")";
8adcb49f 160 }
8adcb49f 161 }
162
5fe65fd4 163 // Do we only want new posts?
fcce139a 164 if ($newsince) {
5fe65fd4
MN
165 $params['newsince'] = $newsince;
166 $newsince = " AND p.modified > :newsince";
fcce139a
AD
167 } else {
168 $newsince = '';
169 }
8adcb49f 170
5fe65fd4
MN
171 // Get group enforcing SQL.
172 $groupmode = groups_get_activity_groupmode($cm);
fcce139a 173 $currentgroup = groups_get_activity_group($cm);
5fe65fd4 174 list($groupselect, $groupparams) = forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext);
8adcb49f 175
5fe65fd4
MN
176 // Add the groupparams to the params array.
177 $params = array_merge($params, $groupparams);
8adcb49f 178
fcce139a 179 $forumsort = "d.timemodified DESC";
af89cdd4 180 $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
181
182 $sql = "SELECT $postdata, d.id as discussionid, d.name as discussionname, d.timemodified, d.usermodified, d.groupid, d.timestart, d.timeend,
183 u.firstname as userfirstname, u.lastname as userlastname, u.email, u.picture, u.imagealt
184 FROM {forum_discussions} d
185 JOIN {forum_posts} p ON p.discussion = d.id
186 JOIN {user} u ON p.userid = u.id
187 WHERE d.forum = {$forum->id} AND p.parent = 0
188 $timelimit $groupselect $newsince
189 ORDER BY $forumsort";
5fe65fd4 190 return array($sql, $params);
fcce139a
AD
191}
192
13d1c9ed
JF
193/**
194 * Generates the SQL query used to get the Post details from the forum table of the database
195 *
196 * @param stdClass $forum the forum object
197 * @param stdClass $cm Course Module object
198 * @param int $newsince check for items since this epoch timestamp
199 * @return string the SQL query to be used to get the Post details from the forum table of the database
200 */
fcce139a 201function forum_rss_feed_posts_sql($forum, $cm, $newsince=0) {
17ec4bf0 202 $modcontext = context_module::instance($cm->id);
46d39cf3 203
5fe65fd4
MN
204 // Get group enforcement SQL.
205 $groupmode = groups_get_activity_groupmode($cm);
fcce139a 206 $currentgroup = groups_get_activity_group($cm);
5fe65fd4 207 $params = array();
fcce139a 208
5fe65fd4 209 list($groupselect, $groupparams) = forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext);
fcce139a 210
5fe65fd4
MN
211 // Add the groupparams to the params array.
212 $params = array_merge($params, $groupparams);
6069e206 213
5fe65fd4 214 // Do we only want new posts?
fcce139a 215 if ($newsince) {
5fe65fd4
MN
216 $params['newsince'] = $newsince;
217 $newsince = " AND p.modified > :newsince";
fcce139a
AD
218 } else {
219 $newsince = '';
220 }
ec41cb3c 221
fcce139a
AD
222 $sql = "SELECT p.id AS postid,
223 d.id AS discussionid,
224 d.name AS discussionname,
225 u.id AS userid,
226 u.firstname AS userfirstname,
227 u.lastname AS userlastname,
228 p.subject AS postsubject,
229 p.message AS postmessage,
230 p.created AS postcreated,
231 p.messageformat AS postformat,
232 p.messagetrust AS posttrust
233 FROM {forum_discussions} d,
234 {forum_posts} p,
235 {user} u
236 WHERE d.forum = {$forum->id} AND
237 p.discussion = d.id AND
238 u.id = p.userid $newsince
239 $groupselect
240 ORDER BY p.created desc";
241
5fe65fd4 242 return array($sql, $params);
fcce139a
AD
243}
244
13d1c9ed
JF
245/**
246 * Retrieve the correct SQL snippet for group-only forums
247 *
248 * @param stdClass $cm Course Module object
249 * @param int $groupmode the mode in which the forum's groups are operating
250 * @param bool $currentgroup true if the user is from the a group enabled on the forum
251 * @param stdClass $modcontext The context instance of the forum module
252 * @return string SQL Query for group details of the forum
253 */
fcce139a
AD
254function forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext=null) {
255 $groupselect = '';
5fe65fd4 256 $params = array();
fcce139a
AD
257
258 if ($groupmode) {
259 if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
260 if ($currentgroup) {
261 $groupselect = "AND (d.groupid = :groupid OR d.groupid = -1)";
262 $params['groupid'] = $currentgroup;
263 }
264 } else {
5fe65fd4 265 // Separate groups without access all.
fcce139a
AD
266 if ($currentgroup) {
267 $groupselect = "AND (d.groupid = :groupid OR d.groupid = -1)";
268 $params['groupid'] = $currentgroup;
269 } else {
270 $groupselect = "AND d.groupid = -1";
8adcb49f 271 }
272 }
8adcb49f 273 }
8f0cd6ef 274
5fe65fd4 275 return array($groupselect, $params);
fcce139a 276}
8adcb49f 277
fcce139a
AD
278/**
279 * This function return the XML rss contents about the forum
280 * It returns false if something is wrong
281 *
13d1c9ed 282 * @param stdClass $forum the forum object
5fe65fd4
MN
283 * @param string $sql the SQL used to retrieve the contents from the database
284 * @param array $params the SQL parameters used
af89cdd4 285 * @param object $context the context this forum relates to
13d1c9ed
JF
286 * @return bool|string false if the contents is empty, otherwise the contents of the feed is returned
287 *
288 * @Todo MDL-31129 implement post attachment handling
fcce139a 289 */
7ea78d9f 290
5fe65fd4 291function forum_rss_feed_contents($forum, $sql, $params, $context) {
7ea78d9f
ARN
292 global $CFG, $DB, $USER;
293
fcce139a
AD
294 $status = true;
295
fcce139a
AD
296 $recs = $DB->get_recordset_sql($sql, $params, 0, $forum->rssarticles);
297
298 //set a flag. Are we displaying discussions or posts?
299 $isdiscussion = true;
300 if (!empty($forum->rsstype) && $forum->rsstype!=1) {
cc771939 301 $isdiscussion = false;
fcce139a 302 }
318f2100 303
7ea78d9f
ARN
304 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
305 print_error('invalidcoursemodule');
306 }
7ea78d9f 307
39790bd8 308 $formatoptions = new stdClass();
fcce139a
AD
309 $items = array();
310 foreach ($recs as $rec) {
39790bd8
PS
311 $item = new stdClass();
312 $user = new stdClass();
7ea78d9f
ARN
313
314 if ($isdiscussion && !forum_user_can_see_discussion($forum, $rec->discussionid, $context)) {
17ec4bf0 315 // This is a discussion which the user has no permission to view
7ea78d9f 316 $item->title = get_string('forumsubjecthidden', 'forum');
17ec4bf0 317 $message = get_string('forumbodyhidden', 'forum');
7ea78d9f
ARN
318 $item->author = get_string('forumauthorhidden', 'forum');
319 } else if (!$isdiscussion && !forum_user_can_see_post($forum, $rec->discussionid, $rec->postid, $USER, $cm)) {
17ec4bf0 320 // This is a post which the user has no permission to view
7ea78d9f 321 $item->title = get_string('forumsubjecthidden', 'forum');
17ec4bf0 322 $message = get_string('forumbodyhidden', 'forum');
7ea78d9f 323 $item->author = get_string('forumauthorhidden', 'forum');
7c810d07 324 } else {
7ea78d9f
ARN
325 // The user must have permission to view
326 if ($isdiscussion && !empty($rec->discussionname)) {
327 $item->title = format_string($rec->discussionname);
328 } else if (!empty($rec->postsubject)) {
329 $item->title = format_string($rec->postsubject);
330 } else {
331 //we should have an item title by now but if we dont somehow then substitute something somewhat meaningful
332 $item->title = format_string($forum->name.' '.userdate($rec->postcreated,get_string('strftimedatetimeshort', 'langconfig')));
333 }
334 $user->firstname = $rec->userfirstname;
335 $user->lastname = $rec->userlastname;
336 $item->author = fullname($user);
17ec4bf0
AG
337 $message = file_rewrite_pluginfile_urls($rec->postmessage, 'pluginfile.php', $context->id,
338 'mod_forum', 'post', $rec->postid);
7ea78d9f 339 $formatoptions->trusted = $rec->posttrust;
7c810d07 340 }
7ea78d9f 341
fcce139a
AD
342 if ($isdiscussion) {
343 $item->link = $CFG->wwwroot."/mod/forum/discuss.php?d=".$rec->discussionid;
344 } else {
8f0cd6ef 345 $item->link = $CFG->wwwroot."/mod/forum/discuss.php?d=".$rec->discussionid."&parent=".$rec->postid;
fcce139a 346 }
410a24c0 347
fcce139a 348 $formatoptions->trusted = $rec->posttrust;
af89cdd4 349 $item->description = format_text($message, $rec->postformat, $formatoptions, $forum->course);
410a24c0 350
13d1c9ed 351 //TODO: MDL-31129 implement post attachment handling
fcce139a 352 /*if (!$isdiscussion) {
318f2100 353 $post_file_area_name = str_replace('//', '/', "$forum->course/$CFG->moddata/forum/$forum->id/$rec->postid");
410a24c0 354 $post_files = get_directory_list("$CFG->dataroot/$post_file_area_name");
4e445355 355
356 if (!empty($post_files)) {
410a24c0 357 $item->attachments = array();
410a24c0 358 }
fcce139a 359 }*/
17ec4bf0 360 $item->pubdate = $rec->postcreated;
46d39cf3 361
fcce139a
AD
362 $items[] = $item;
363 }
364 $recs->close();
410a24c0 365
941294ef
MN
366 // Create the RSS header.
367 $header = rss_standard_header(strip_tags(format_string($forum->name,true)),
368 $CFG->wwwroot."/mod/forum/view.php?f=".$forum->id,
369 format_string($forum->intro,true)); // TODO: fix format
370 // Now all the RSS items, if there are any.
371 $articles = '';
fcce139a 372 if (!empty($items)) {
941294ef 373 $articles = rss_add_items($items);
fcce139a 374 }
941294ef
MN
375 // Create the RSS footer.
376 $footer = rss_standard_footer();
fcce139a 377
941294ef 378 return $header . $articles . $footer;
fcce139a 379}