3 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
19 * This file adds support to rss feeds generation
23 * @copyright 2001 Eloy Lafuente (stronk7) http://contiento.com
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28 * Returns the path to the cached rss feed contents. Creates/updates the cache if necessary.
29 * @param stdClass $context the context
30 * @param array $args the arguments received in the url
31 * @return string the full path to the cached RSS feed directory. Null if there is a problem.
33 function forum_rss_get_feed($context, $args) {
34 global $CFG, $DB, $USER;
38 //are RSS feeds enabled?
39 if (empty($CFG->forum_enablerssfeeds)) {
40 debugging('DISABLED (module configuration)');
44 $forumid = clean_param($args[3], PARAM_INT);
45 $cm = get_coursemodule_from_instance('forum', $forumid, 0, false, MUST_EXIST);
46 $modcontext = context_module::instance($cm->id);
48 //context id from db should match the submitted one
49 if ($context->id != $modcontext->id || !has_capability('mod/forum:viewdiscussion', $modcontext)) {
53 $forum = $DB->get_record('forum', array('id' => $forumid), '*', MUST_EXIST);
54 if (!rss_enabled_for_mod('forum', $forum)) {
58 //the sql that will retreive the data for the feed and be hashed to get the cache filename
59 list($sql, $params) = forum_rss_get_sql($forum, $cm);
61 // Hash the sql to get the cache file name.
62 $filename = rss_get_file_name($forum, $sql, $params);
63 $cachedfilepath = rss_get_file_full_name('mod_forum', $filename);
65 //Is the cache out of date?
66 $cachedfilelastmodified = 0;
67 if (file_exists($cachedfilepath)) {
68 $cachedfilelastmodified = filemtime($cachedfilepath);
70 // Used to determine if we need to generate a new RSS feed.
71 $dontrecheckcutoff = time()-60;
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.
77 $result = forum_rss_feed_contents($forum, $sql, $params, $modcontext);
78 $status = rss_save_file('mod_forum', $filename, $result);
81 //return the path to the cached version
82 return $cachedfilepath;
86 * Given a forum object, deletes all cached RSS files associated with it.
88 * @param stdClass $forum
90 function forum_rss_delete_file($forum) {
91 rss_delete_file('mod_forum', $forum);
94 ///////////////////////////////////////////////////////
98 * If there is new stuff in the forum since $time this returns true
99 * Otherwise it returns false.
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
106 function forum_rss_newstuff($forum, $cm, $time) {
109 list($sql, $params) = forum_rss_get_sql($forum, $cm, $time);
111 return $DB->record_exists_sql($sql, $params);
115 * Determines which type of SQL query is required, one for posts or one for discussions, and returns the appropriate query
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
122 function forum_rss_get_sql($forum, $cm, $time=0) {
123 if ($forum->rsstype == 1) { // Discussion RSS
124 return forum_rss_feed_discussions_sql($forum, $cm, $time);
126 return forum_rss_feed_posts_sql($forum, $cm, $time);
131 * Generates the SQL query used to get the Discussion details from the forum table of the database
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
138 function forum_rss_feed_discussions_sql($forum, $cm, $newsince=0) {
139 global $CFG, $DB, $USER;
145 $now = round(time(), -2);
148 $modcontext = context_module::instance($cm->id);
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;
156 $timelimit .= " OR d.userid = :userid";
157 $params['userid'] = $USER->id;
163 // Do we only want new posts?
165 $params['newsince'] = $newsince;
166 $newsince = " AND p.modified > :newsince";
171 // Get group enforcing SQL.
172 $groupmode = groups_get_activity_groupmode($cm);
173 $currentgroup = groups_get_activity_group($cm);
174 list($groupselect, $groupparams) = forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext);
176 // Add the groupparams to the params array.
177 $params = array_merge($params, $groupparams);
179 $forumsort = "d.timemodified DESC";
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";
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";
190 return array($sql, $params);
194 * Generates the SQL query used to get the Post details from the forum table of the database
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
201 function forum_rss_feed_posts_sql($forum, $cm, $newsince=0) {
202 $modcontext = context_module::instance($cm->id);
204 // Get group enforcement SQL.
205 $groupmode = groups_get_activity_groupmode($cm);
206 $currentgroup = groups_get_activity_group($cm);
209 list($groupselect, $groupparams) = forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext);
211 // Add the groupparams to the params array.
212 $params = array_merge($params, $groupparams);
214 // Do we only want new posts?
216 $params['newsince'] = $newsince;
217 $newsince = " AND p.modified > :newsince";
222 $sql = "SELECT p.id AS postid,
223 d.id AS discussionid,
224 d.name AS discussionname,
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,
236 WHERE d.forum = {$forum->id} AND
237 p.discussion = d.id AND
238 u.id = p.userid $newsince
240 ORDER BY p.created desc";
242 return array($sql, $params);
246 * Retrieve the correct SQL snippet for group-only forums
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
254 function forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext=null) {
259 if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
261 $groupselect = "AND (d.groupid = :groupid OR d.groupid = -1)";
262 $params['groupid'] = $currentgroup;
265 // Separate groups without access all.
267 $groupselect = "AND (d.groupid = :groupid OR d.groupid = -1)";
268 $params['groupid'] = $currentgroup;
270 $groupselect = "AND d.groupid = -1";
275 return array($groupselect, $params);
279 * This function return the XML rss contents about the forum
280 * It returns false if something is wrong
282 * @param stdClass $forum the forum object
283 * @param string $sql the SQL used to retrieve the contents from the database
284 * @param array $params the SQL parameters used
285 * @param object $context the context this forum relates to
286 * @return bool|string false if the contents is empty, otherwise the contents of the feed is returned
288 * @Todo MDL-31129 implement post attachment handling
291 function forum_rss_feed_contents($forum, $sql, $params, $context) {
292 global $CFG, $DB, $USER;
296 $recs = $DB->get_recordset_sql($sql, $params, 0, $forum->rssarticles);
298 //set a flag. Are we displaying discussions or posts?
299 $isdiscussion = true;
300 if (!empty($forum->rsstype) && $forum->rsstype!=1) {
301 $isdiscussion = false;
304 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
305 print_error('invalidcoursemodule');
308 $formatoptions = new stdClass();
310 foreach ($recs as $rec) {
311 $item = new stdClass();
312 $user = new stdClass();
314 if ($isdiscussion && !forum_user_can_see_discussion($forum, $rec->discussionid, $context)) {
315 // This is a discussion which the user has no permission to view
316 $item->title = get_string('forumsubjecthidden', 'forum');
317 $message = get_string('forumbodyhidden', 'forum');
318 $item->author = get_string('forumauthorhidden', 'forum');
319 } else if (!$isdiscussion && !forum_user_can_see_post($forum, $rec->discussionid, $rec->postid, $USER, $cm)) {
320 // This is a post which the user has no permission to view
321 $item->title = get_string('forumsubjecthidden', 'forum');
322 $message = get_string('forumbodyhidden', 'forum');
323 $item->author = get_string('forumauthorhidden', 'forum');
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);
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')));
334 $user->firstname = $rec->userfirstname;
335 $user->lastname = $rec->userlastname;
336 $item->author = fullname($user);
337 $message = file_rewrite_pluginfile_urls($rec->postmessage, 'pluginfile.php', $context->id,
338 'mod_forum', 'post', $rec->postid);
339 $formatoptions->trusted = $rec->posttrust;
343 $item->link = $CFG->wwwroot."/mod/forum/discuss.php?d=".$rec->discussionid;
345 $item->link = $CFG->wwwroot."/mod/forum/discuss.php?d=".$rec->discussionid."&parent=".$rec->postid;
348 $formatoptions->trusted = $rec->posttrust;
349 $item->description = format_text($message, $rec->postformat, $formatoptions, $forum->course);
351 //TODO: MDL-31129 implement post attachment handling
352 /*if (!$isdiscussion) {
353 $post_file_area_name = str_replace('//', '/', "$forum->course/$CFG->moddata/forum/$forum->id/$rec->postid");
354 $post_files = get_directory_list("$CFG->dataroot/$post_file_area_name");
356 if (!empty($post_files)) {
357 $item->attachments = array();
360 $item->pubdate = $rec->postcreated;
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.
372 if (!empty($items)) {
373 $articles = rss_add_items($items);
375 // Create the RSS footer.
376 $footer = rss_standard_footer();
378 return $header . $articles . $footer;