MDL-61309 mod_forum: Implement the Privacy API
[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 *
01030f1b 21 * @package mod_forum
13d1c9ed
JF
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
098f4337 149 $now = floor(time() / 60) * 60; // DB Cache Friendly.
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,
8f6e47d9
AD
231 d.groupid,
232 d.timestart,
233 d.timeend,
fcce139a 234 u.id AS userid,
5450ed43 235 $usernamefields,
fcce139a
AD
236 p.subject AS postsubject,
237 p.message AS postmessage,
238 p.created AS postcreated,
239 p.messageformat AS postformat,
8f6e47d9
AD
240 p.messagetrust AS posttrust,
241 p.parent as postparent
fcce139a
AD
242 FROM {forum_discussions} d,
243 {forum_posts} p,
244 {user} u
245 WHERE d.forum = {$forum->id} AND
246 p.discussion = d.id AND
247 u.id = p.userid $newsince
248 $groupselect
249 ORDER BY p.created desc";
250
5fe65fd4 251 return array($sql, $params);
fcce139a
AD
252}
253
13d1c9ed
JF
254/**
255 * Retrieve the correct SQL snippet for group-only forums
256 *
257 * @param stdClass $cm Course Module object
258 * @param int $groupmode the mode in which the forum's groups are operating
259 * @param bool $currentgroup true if the user is from the a group enabled on the forum
260 * @param stdClass $modcontext The context instance of the forum module
261 * @return string SQL Query for group details of the forum
262 */
fcce139a
AD
263function forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext=null) {
264 $groupselect = '';
5fe65fd4 265 $params = array();
fcce139a
AD
266
267 if ($groupmode) {
268 if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
269 if ($currentgroup) {
270 $groupselect = "AND (d.groupid = :groupid OR d.groupid = -1)";
271 $params['groupid'] = $currentgroup;
272 }
273 } else {
5fe65fd4 274 // Separate groups without access all.
fcce139a
AD
275 if ($currentgroup) {
276 $groupselect = "AND (d.groupid = :groupid OR d.groupid = -1)";
277 $params['groupid'] = $currentgroup;
278 } else {
279 $groupselect = "AND d.groupid = -1";
8adcb49f 280 }
281 }
8adcb49f 282 }
8f0cd6ef 283
5fe65fd4 284 return array($groupselect, $params);
fcce139a 285}
8adcb49f 286
fcce139a
AD
287/**
288 * This function return the XML rss contents about the forum
289 * It returns false if something is wrong
290 *
13d1c9ed 291 * @param stdClass $forum the forum object
5fe65fd4
MN
292 * @param string $sql the SQL used to retrieve the contents from the database
293 * @param array $params the SQL parameters used
af89cdd4 294 * @param object $context the context this forum relates to
13d1c9ed
JF
295 * @return bool|string false if the contents is empty, otherwise the contents of the feed is returned
296 *
297 * @Todo MDL-31129 implement post attachment handling
fcce139a 298 */
7ea78d9f 299
5fe65fd4 300function forum_rss_feed_contents($forum, $sql, $params, $context) {
7ea78d9f
ARN
301 global $CFG, $DB, $USER;
302
fcce139a
AD
303 $status = true;
304
fcce139a
AD
305 $recs = $DB->get_recordset_sql($sql, $params, 0, $forum->rssarticles);
306
307 //set a flag. Are we displaying discussions or posts?
308 $isdiscussion = true;
309 if (!empty($forum->rsstype) && $forum->rsstype!=1) {
cc771939 310 $isdiscussion = false;
fcce139a 311 }
318f2100 312
7ea78d9f
ARN
313 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
314 print_error('invalidcoursemodule');
315 }
7ea78d9f 316
39790bd8 317 $formatoptions = new stdClass();
fcce139a
AD
318 $items = array();
319 foreach ($recs as $rec) {
39790bd8 320 $item = new stdClass();
7ea78d9f 321
8f6e47d9
AD
322 $discussion = new stdClass();
323 $discussion->id = $rec->discussionid;
324 $discussion->groupid = $rec->groupid;
325 $discussion->timestart = $rec->timestart;
326 $discussion->timeend = $rec->timeend;
327
328 $post = null;
329 if (!$isdiscussion) {
330 $post = new stdClass();
331 $post->id = $rec->postid;
332 $post->parent = $rec->postparent;
333 $post->userid = $rec->userid;
334 }
335
336 if ($isdiscussion && !forum_user_can_see_discussion($forum, $discussion, $context)) {
17ec4bf0 337 // This is a discussion which the user has no permission to view
7ea78d9f 338 $item->title = get_string('forumsubjecthidden', 'forum');
17ec4bf0 339 $message = get_string('forumbodyhidden', 'forum');
7ea78d9f 340 $item->author = get_string('forumauthorhidden', 'forum');
8f6e47d9 341 } else if (!$isdiscussion && !forum_user_can_see_post($forum, $discussion, $post, $USER, $cm)) {
17ec4bf0 342 // This is a post which the user has no permission to view
7ea78d9f 343 $item->title = get_string('forumsubjecthidden', 'forum');
17ec4bf0 344 $message = get_string('forumbodyhidden', 'forum');
7ea78d9f 345 $item->author = get_string('forumauthorhidden', 'forum');
7c810d07 346 } else {
7ea78d9f
ARN
347 // The user must have permission to view
348 if ($isdiscussion && !empty($rec->discussionname)) {
349 $item->title = format_string($rec->discussionname);
350 } else if (!empty($rec->postsubject)) {
351 $item->title = format_string($rec->postsubject);
352 } else {
353 //we should have an item title by now but if we dont somehow then substitute something somewhat meaningful
354 $item->title = format_string($forum->name.' '.userdate($rec->postcreated,get_string('strftimedatetimeshort', 'langconfig')));
355 }
5450ed43 356 $item->author = fullname($rec);
17ec4bf0
AG
357 $message = file_rewrite_pluginfile_urls($rec->postmessage, 'pluginfile.php', $context->id,
358 'mod_forum', 'post', $rec->postid);
7ea78d9f 359 $formatoptions->trusted = $rec->posttrust;
7c810d07 360 }
7ea78d9f 361
fcce139a
AD
362 if ($isdiscussion) {
363 $item->link = $CFG->wwwroot."/mod/forum/discuss.php?d=".$rec->discussionid;
364 } else {
8f0cd6ef 365 $item->link = $CFG->wwwroot."/mod/forum/discuss.php?d=".$rec->discussionid."&parent=".$rec->postid;
fcce139a 366 }
410a24c0 367
fcce139a 368 $formatoptions->trusted = $rec->posttrust;
af89cdd4 369 $item->description = format_text($message, $rec->postformat, $formatoptions, $forum->course);
410a24c0 370
13d1c9ed 371 //TODO: MDL-31129 implement post attachment handling
fcce139a 372 /*if (!$isdiscussion) {
318f2100 373 $post_file_area_name = str_replace('//', '/', "$forum->course/$CFG->moddata/forum/$forum->id/$rec->postid");
410a24c0 374 $post_files = get_directory_list("$CFG->dataroot/$post_file_area_name");
4e445355 375
376 if (!empty($post_files)) {
410a24c0 377 $item->attachments = array();
410a24c0 378 }
fcce139a 379 }*/
17ec4bf0 380 $item->pubdate = $rec->postcreated;
46d39cf3 381
fcce139a
AD
382 $items[] = $item;
383 }
384 $recs->close();
410a24c0 385
941294ef
MN
386 // Create the RSS header.
387 $header = rss_standard_header(strip_tags(format_string($forum->name,true)),
388 $CFG->wwwroot."/mod/forum/view.php?f=".$forum->id,
389 format_string($forum->intro,true)); // TODO: fix format
390 // Now all the RSS items, if there are any.
391 $articles = '';
fcce139a 392 if (!empty($items)) {
941294ef 393 $articles = rss_add_items($items);
fcce139a 394 }
941294ef
MN
395 // Create the RSS footer.
396 $footer = rss_standard_footer();
fcce139a 397
941294ef 398 return $header . $articles . $footer;
fcce139a 399}