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