weekly release 2.7dev
[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";
5450ed43 185 $userpicturefields = user_picture::fields('u', null, 'userid');
fcce139a 186
5450ed43
AG
187 $sql = "SELECT $postdata, d.id as discussionid, d.name as discussionname, d.timemodified, d.usermodified, d.groupid,
188 d.timestart, d.timeend, $userpicturefields
fcce139a
AD
189 FROM {forum_discussions} d
190 JOIN {forum_posts} p ON p.discussion = d.id
191 JOIN {user} u ON p.userid = u.id
192 WHERE d.forum = {$forum->id} AND p.parent = 0
193 $timelimit $groupselect $newsince
194 ORDER BY $forumsort";
5fe65fd4 195 return array($sql, $params);
fcce139a
AD
196}
197
13d1c9ed
JF
198/**
199 * Generates the SQL query used to get the Post details from the forum table of the database
200 *
201 * @param stdClass $forum the forum object
202 * @param stdClass $cm Course Module object
203 * @param int $newsince check for items since this epoch timestamp
204 * @return string the SQL query to be used to get the Post details from the forum table of the database
205 */
fcce139a 206function forum_rss_feed_posts_sql($forum, $cm, $newsince=0) {
17ec4bf0 207 $modcontext = context_module::instance($cm->id);
46d39cf3 208
5fe65fd4
MN
209 // Get group enforcement SQL.
210 $groupmode = groups_get_activity_groupmode($cm);
fcce139a 211 $currentgroup = groups_get_activity_group($cm);
5fe65fd4 212 $params = array();
fcce139a 213
5fe65fd4 214 list($groupselect, $groupparams) = forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext);
fcce139a 215
5fe65fd4
MN
216 // Add the groupparams to the params array.
217 $params = array_merge($params, $groupparams);
6069e206 218
5fe65fd4 219 // Do we only want new posts?
fcce139a 220 if ($newsince) {
5fe65fd4
MN
221 $params['newsince'] = $newsince;
222 $newsince = " AND p.modified > :newsince";
fcce139a
AD
223 } else {
224 $newsince = '';
225 }
ec41cb3c 226
5450ed43 227 $usernamefields = get_all_user_name_fields(true, 'u');
fcce139a
AD
228 $sql = "SELECT p.id AS postid,
229 d.id AS discussionid,
230 d.name AS discussionname,
231 u.id AS userid,
5450ed43 232 $usernamefields,
fcce139a
AD
233 p.subject AS postsubject,
234 p.message AS postmessage,
235 p.created AS postcreated,
236 p.messageformat AS postformat,
237 p.messagetrust AS posttrust
238 FROM {forum_discussions} d,
239 {forum_posts} p,
240 {user} u
241 WHERE d.forum = {$forum->id} AND
242 p.discussion = d.id AND
243 u.id = p.userid $newsince
244 $groupselect
245 ORDER BY p.created desc";
246
5fe65fd4 247 return array($sql, $params);
fcce139a
AD
248}
249
13d1c9ed
JF
250/**
251 * Retrieve the correct SQL snippet for group-only forums
252 *
253 * @param stdClass $cm Course Module object
254 * @param int $groupmode the mode in which the forum's groups are operating
255 * @param bool $currentgroup true if the user is from the a group enabled on the forum
256 * @param stdClass $modcontext The context instance of the forum module
257 * @return string SQL Query for group details of the forum
258 */
fcce139a
AD
259function forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext=null) {
260 $groupselect = '';
5fe65fd4 261 $params = array();
fcce139a
AD
262
263 if ($groupmode) {
264 if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
265 if ($currentgroup) {
266 $groupselect = "AND (d.groupid = :groupid OR d.groupid = -1)";
267 $params['groupid'] = $currentgroup;
268 }
269 } else {
5fe65fd4 270 // Separate groups without access all.
fcce139a
AD
271 if ($currentgroup) {
272 $groupselect = "AND (d.groupid = :groupid OR d.groupid = -1)";
273 $params['groupid'] = $currentgroup;
274 } else {
275 $groupselect = "AND d.groupid = -1";
8adcb49f 276 }
277 }
8adcb49f 278 }
8f0cd6ef 279
5fe65fd4 280 return array($groupselect, $params);
fcce139a 281}
8adcb49f 282
fcce139a
AD
283/**
284 * This function return the XML rss contents about the forum
285 * It returns false if something is wrong
286 *
13d1c9ed 287 * @param stdClass $forum the forum object
5fe65fd4
MN
288 * @param string $sql the SQL used to retrieve the contents from the database
289 * @param array $params the SQL parameters used
af89cdd4 290 * @param object $context the context this forum relates to
13d1c9ed
JF
291 * @return bool|string false if the contents is empty, otherwise the contents of the feed is returned
292 *
293 * @Todo MDL-31129 implement post attachment handling
fcce139a 294 */
7ea78d9f 295
5fe65fd4 296function forum_rss_feed_contents($forum, $sql, $params, $context) {
7ea78d9f
ARN
297 global $CFG, $DB, $USER;
298
fcce139a
AD
299 $status = true;
300
fcce139a
AD
301 $recs = $DB->get_recordset_sql($sql, $params, 0, $forum->rssarticles);
302
303 //set a flag. Are we displaying discussions or posts?
304 $isdiscussion = true;
305 if (!empty($forum->rsstype) && $forum->rsstype!=1) {
cc771939 306 $isdiscussion = false;
fcce139a 307 }
318f2100 308
7ea78d9f
ARN
309 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
310 print_error('invalidcoursemodule');
311 }
7ea78d9f 312
39790bd8 313 $formatoptions = new stdClass();
fcce139a
AD
314 $items = array();
315 foreach ($recs as $rec) {
39790bd8 316 $item = 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 }
5450ed43 338 $item->author = fullname($rec);
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
941294ef
MN
368 // Create the RSS header.
369 $header = rss_standard_header(strip_tags(format_string($forum->name,true)),
370 $CFG->wwwroot."/mod/forum/view.php?f=".$forum->id,
371 format_string($forum->intro,true)); // TODO: fix format
372 // Now all the RSS items, if there are any.
373 $articles = '';
fcce139a 374 if (!empty($items)) {
941294ef 375 $articles = rss_add_items($items);
fcce139a 376 }
941294ef
MN
377 // Create the RSS footer.
378 $footer = rss_standard_footer();
fcce139a 379
941294ef 380 return $header . $articles . $footer;
fcce139a 381}