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 contains all the common stuff to be used in RSS System
23 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 defined('MOODLE_INTERNAL') || die();
30 * Build the URL for the RSS feed and add it as a header
32 * @param stdClass $context The context under which the URL should be created
33 * @param string $componentname The name of the component for which the RSS feed exists
34 * @param stdClass $componentinstance The instance of the component
35 * @param string $title Name for the link to be added to the page header
37 function rss_add_http_header($context, $componentname, $componentinstance, $title) {
41 if (is_object($componentinstance)) {
42 $componentid = $componentinstance->id;
44 $componentid = $componentinstance;
47 $rsspath = rss_get_url($context->id, $USER->id, $componentname, $componentid);
48 $PAGE->add_alternate_version($title, $rsspath, 'application/rss+xml');
52 * Print the link for the RSS feed with the correct RSS icon
54 * @param stdClass $contextid The id of the context under which the URL should be created
55 * @param int $userid The source of the RSS feed (site/course/group/user)
56 * @param string $componentname The name of the component for which the feed exists
57 * @param string $id The name by which to call the RSS File
58 * @param string $tooltiptext The tooltip to be displayed with the link
59 * @return string HTML output for the RSS link
61 function rss_get_link($contextid, $userid, $componentname, $id, $tooltiptext='') {
66 $rsspath = rss_get_url($contextid, $userid, $componentname, $id);
67 $rsspix = $OUTPUT->pix_url('i/rss');
69 return '<a href="'. $rsspath .'"><img src="'. $rsspix .'" title="'. strip_tags($tooltiptext) .'" alt="'.get_string('rss').'" /></a>';
73 * This function returns the URL for the RSS XML file.
75 * @param int $contextid the course id
76 * @param int $userid the current user id
77 * @param string $componentname the name of the current component. For example "forum"
78 * @param string $additionalargs For modules, module instance id
79 * @return string the url of the RSS feed
81 function rss_get_url($contextid, $userid, $componentname, $additionalargs) {
83 require_once($CFG->libdir.'/filelib.php');
84 $usertoken = rss_get_token($userid);
85 return get_file_url($contextid.'/'.$usertoken.'/'.$componentname.'/'.$additionalargs.'/rss.xml', null, 'rssfile');
89 * Print the link for the RSS feed with the correct RSS icon (Theme based)
91 * @param stdClass $contextid The id of the context under which the URL should be created
92 * @param int $userid The source of the RSS feed (site/course/group/user)
93 * @param string $componentname The name of the component for which the feed exists
94 * @param string $id The name by which to call the RSS File
95 * @param string $tooltiptext The tooltip to be displayed with the link
97 function rss_print_link($contextid, $userid, $componentname, $id, $tooltiptext='') {
98 print rss_get_link($contextid, $userid, $componentname, $id, $tooltiptext);
103 * Given an object, deletes all RSS files associated with it.
105 * @param string $componentname the name of the module ie 'forum'. Used to construct the cache path.
106 * @param stdClass $instance An object with an id member variable ie $forum, $glossary.
108 function rss_delete_file($componentname, $instance) {
111 $dirpath = "$CFG->cachedir/rss/$componentname";
112 if (is_dir($dirpath)) {
113 if (!$dh = opendir($dirpath)) {
114 error_log("Directory permission error. RSS directory store for component '{$componentname}' exists but cannot be opened.", DEBUG_DEVELOPER);
117 while (false !== ($filename = readdir($dh))) {
118 if ($filename!='.' && $filename!='..') {
119 if (preg_match("/{$instance->id}_/", $filename)) {
120 unlink("$dirpath/$filename");
128 * Are RSS feeds enabled for the supplied module instance?
130 * @param string $modname The name of the module to be checked
131 * @param stdClass $instance An instance of an activity module ie $forum, $glossary.
132 * @param bool $hasrsstype Should there be a rsstype member variable?
133 * @param bool $hasrssarticles Should there be a rssarticles member variable?
134 * @return bool whether or not RSS is enabled for the module
136 function rss_enabled_for_mod($modname, $instance=null, $hasrsstype=true, $hasrssarticles=true) {
138 if (empty($instance->rsstype) || $instance->rsstype==0) {
143 //have they set the RSS feed to return 0 results?
144 if ($hasrssarticles) {
145 if (empty($instance->rssarticles) || $instance->rssarticles==0) {
150 if (!empty($instance) && !instance_is_visible($modname,$instance)) {
158 * This function saves to file the rss feed specified in the parameters
160 * @param string $componentname the module name ie forum. Used to create a cache directory.
161 * @param string $filename the name of the file to be created ie "rss.xml"
162 * @param string $contents the data to be written to the file
163 * @param bool $expandfilename whether or not the fullname of the RSS file should be used
164 * @return bool whether the save was successful or not
166 function rss_save_file($componentname, $filename, $contents, $expandfilename=true) {
171 if (! $basedir = make_cache_directory ('rss/'. $componentname)) {
172 //Cannot be created, so error
177 $fullfilename = $filename;
178 if ($expandfilename) {
179 $fullfilename = rss_get_file_full_name($componentname, $filename);
182 $rss_file = fopen($fullfilename, "w");
184 $status = fwrite ($rss_file, $contents);
194 * Retrieve the location and file name of a cached RSS feed
196 * @param string $componentname the name of the component the RSS feed is being created for
197 * @param string $filename the name of the RSS FEED
198 * @return string The full name and path of the RSS file
200 function rss_get_file_full_name($componentname, $filename) {
202 return "$CFG->cachedir/rss/$componentname/$filename.xml";
206 * Construct the file name of the RSS File
208 * @param stdClass $instance the instance of the source of the RSS feed
209 * @param string $sql the SQL used to produce the RSS feed
210 * @param array $params the parameters used in the SQL query
211 * @return string the name of the RSS file
213 function rss_get_file_name($instance, $sql, $params = array()) {
215 // If a parameters array is passed, then we want to
216 // serialize it and then concatenate it with the sql.
217 // The reason for this is to generate a unique filename
218 // for queries using the same sql but different parameters.
220 $serializearray = serialize($params);
221 return $instance->id.'_'.md5($sql . $serializearray);
223 return $instance->id.'_'.md5($sql);
228 * This function return all the common headers for every rss feed in the site
230 * @param string $title the title for the RSS Feed
231 * @param string $link the link for the origin of the RSS feed
232 * @param string $description the description of the contents of the RSS feed
233 * @return bool|string the standard header for the RSS feed
235 function rss_standard_header($title = NULL, $link = NULL, $description = NULL) {
236 global $CFG, $USER, $OUTPUT;
245 //Calculate title, link and description
247 $title = format_string($site->fullname);
250 $link = $CFG->wwwroot;
252 if (empty($description)) {
253 $description = $site->summary;
257 $result .= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
258 $result .= "<rss version=\"2.0\">\n";
261 $result .= rss_start_tag('channel', 1, true);
264 $result .= rss_full_tag('title', 2, false, strip_tags($title));
265 $result .= rss_full_tag('link', 2, false, $link);
266 $result .= rss_full_tag('description', 2, false, $description);
267 $result .= rss_full_tag('generator', 2, false, 'Moodle');
268 if (!empty($USER->lang)) {
269 $result .= rss_full_tag('language', 2, false, substr($USER->lang,0,2));
272 $result .= rss_full_tag('copyright', 2, false, '© '. $today['year'] .' '. format_string($site->fullname));
274 if (!empty($USER->email)) {
275 $result .= rss_full_tag('managingEditor', 2, false, fullname($USER));
276 $result .= rss_full_tag('webMaster', 2, false, fullname($USER));
281 $rsspix = $OUTPUT->pix_url('i/rsssitelogo');
284 $result .= rss_start_tag('image', 2, true);
285 $result .= rss_full_tag('url', 3, false, $rsspix);
286 $result .= rss_full_tag('title', 3, false, 'moodle');
287 $result .= rss_full_tag('link', 3, false, $CFG->wwwroot);
288 $result .= rss_full_tag('width', 3, false, '140');
289 $result .= rss_full_tag('height', 3, false, '35');
290 $result .= rss_end_tag('image', 2, true);
302 * Generates the rss XML code for every item passed in the array
304 * item->title: The title of the item
305 * item->author: The author of the item. Optional !!
306 * item->pubdate: The pubdate of the item
307 * item->link: The link url of the item
308 * item->description: The content of the item
310 * @param array $items an array of item objects
311 * @return bool|string the rss XML code for every item passed in the array
313 function rss_add_items($items) {
318 if (!empty($items)) {
319 foreach ($items as $item) {
320 $result .= rss_start_tag('item',2,true);
321 //Include the category if exists (some rss readers will use it to group items)
322 if (isset($item->category)) {
323 $result .= rss_full_tag('category',3,false,$item->category);
325 if (isset($item->tags)) {
326 $attributes = array();
327 if (isset($item->tagscheme)) {
328 $attributes['domain'] = s($item->tagscheme);
330 foreach ($item->tags as $tag) {
331 $result .= rss_full_tag('category', 3, false, $tag, $attributes);
334 $result .= rss_full_tag('title',3,false,strip_tags($item->title));
335 $result .= rss_full_tag('link',3,false,$item->link);
336 $result .= rss_add_enclosures($item);
337 $result .= rss_full_tag('pubDate',3,false,gmdate('D, d M Y H:i:s',$item->pubdate).' GMT'); # MDL-12563
338 //Include the author if exists
339 if (isset($item->author) && !empty($item->author)) {
340 //$result .= rss_full_tag('author',3,false,$item->author);
341 //We put it in the description instead because it's more important
342 //for moodle than most other feeds, and most rss software seems to ignore
343 //the author field ...
344 $item->description = get_string('byname','',$item->author).'. <p>'.$item->description.'</p>';
346 $result .= rss_full_tag('description',3,false,$item->description);
347 $result .= rss_full_tag('guid',3,false,$item->link,array('isPermaLink' => 'true'));
348 $result .= rss_end_tag('item',2,true);
358 * This function return all the common footers for every rss feed in the site
360 * @param string $title Not used at all
361 * @param string $link Not used at all
362 * @param string $description Not used at all
363 * @todo MDL-31050 Fix/Remove this function
366 function rss_standard_footer($title = NULL, $link = NULL, $description = NULL) {
371 $result .= rss_end_tag('channel', 1, true);
372 ////Close the rss tag
380 * This function return an error xml file (string) to be sent when a rss is required (file.php) and something goes wrong
382 * @param string $errortype Type of error to send, default is rsserror
383 * @return stdClass returns a XML Feed with an error message in it
385 function rss_geterrorxmlfile($errortype = 'rsserror') {
391 $return = rss_standard_header();
395 $item = new stdClass();
396 $item->title = "RSS Error";
397 $item->link = $CFG->wwwroot;
398 $item->pubdate = time();
399 $item->description = get_string($errortype);
400 $return .= rss_add_items(array($item));
405 $return .= rss_standard_footer();
412 * Get the ID of the user from a given RSS Token
414 * @param string $token the RSS token you would like to use to find the user id
415 * @return int The user id
417 function rss_get_userid_from_token($token) {
420 $sql = 'SELECT u.id FROM {user} u
421 JOIN {user_private_key} k ON u.id = k.userid
422 WHERE u.deleted = 0 AND u.confirmed = 1
423 AND u.suspended = 0 AND k.value = ?';
424 return $DB->get_field_sql($sql, array($token), IGNORE_MISSING);
428 * Get the RSS Token from a given user id
430 * @param int $userid The user id
431 * @return string the RSS token for the user
433 function rss_get_token($userid) {
434 return get_user_key('rss', $userid);
438 * Removes the token for the given user from the DB
439 * @param int $userid The user id for the token you wish to delete
441 function rss_delete_token($userid) {
442 delete_user_key('rss', $userid);
446 * Return the xml start tag
448 * @param string $tag the xml tag name
449 * @param int $level the indentation level
450 * @param bool $endline whether or not to start new tags on a new line
451 * @param array $attributes the attributes of the xml tag
452 * @return string the xml start tag
454 function rss_start_tag($tag,$level=0,$endline=false,$attributes=null) {
461 if (!empty($attributes) && is_array($attributes)) {
462 foreach ($attributes as $key => $value) {
463 $attrstring .= " ".$key."=\"".$value."\"";
466 return str_repeat(" ",$level*2)."<".$tag.$attrstring.">".$endchar;
470 * Return the xml end tag
471 * @param string $tag the xml tag name
472 * @param int $level the indentation level
473 * @param bool $endline whether or not to start new tags on a new line
474 * @return string the xml end tag
476 function rss_end_tag($tag,$level=0,$endline=true) {
482 return str_repeat(" ",$level*2)."</".$tag.">".$endchar;
486 * Return the while xml element, including content
488 * @param string $tag the xml tag name
489 * @param int $level the indentation level
490 * @param bool $endline whether or not to start new tags on a new line
491 * @param string $content the text to go inside the tag
492 * @param array $attributes the attributes of the xml tag
493 * @return string the whole xml element
495 function rss_full_tag($tag,$level=0,$endline=true,$content,$attributes=null) {
496 $st = rss_start_tag($tag,$level,$endline,$attributes);
498 $co = preg_replace("/\r\n|\r/", "\n", htmlspecialchars($content));
499 $et = rss_end_tag($tag,0,true);
505 * Adds RSS Media Enclosures for "podcasting" by including attachments that
506 * are specified in the item->attachments field.
508 * @param stdClass $item representing an RSS item
509 * @return string RSS enclosure tags
511 function rss_add_enclosures($item){
516 // list of media file extensions and their respective mime types
517 include_once($CFG->libdir.'/filelib.php');
518 $mediafiletypes = get_mimetypes_array();
520 // take into account attachments (e.g. from forum) - with these, we are able to know the file size
521 if (isset($item->attachments) && is_array($item->attachments)) {
522 foreach ($item->attachments as $attachment){
523 $extension = strtolower(substr($attachment->url, strrpos($attachment->url, '.')+1));
524 if (isset($mediafiletypes[$extension]['type'])) {
525 $type = $mediafiletypes[$extension]['type'];
527 $type = 'document/unknown';
529 $returnstring .= "\n<enclosure url=\"$attachment->url\" length=\"$attachment->length\" type=\"$type\" />\n";
533 return $returnstring;