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