See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]
1 <?php 2 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 /** 19 * This file contains all the common stuff to be used in RSS System 20 * 21 * @package core_rss 22 * @category rss 23 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 */ 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 /** 30 * Build the URL for the RSS feed and add it as a header 31 * 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 36 */ 37 function rss_add_http_header($context, $componentname, $componentinstance, $title) { 38 global $PAGE, $USER; 39 40 $componentid = null; 41 if (is_object($componentinstance)) { 42 $componentid = $componentinstance->id; 43 } else { 44 $componentid = $componentinstance; 45 } 46 47 $rsspath = rss_get_url($context->id, $USER->id, $componentname, $componentid); 48 $PAGE->add_alternate_version($title, $rsspath, 'application/rss+xml'); 49 } 50 51 /** 52 * Print the link for the RSS feed with the correct RSS icon 53 * 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 60 */ 61 function rss_get_link($contextid, $userid, $componentname, $id, $tooltiptext='') { 62 global $OUTPUT; 63 64 static $rsspath = ''; 65 66 $rsspath = rss_get_url($contextid, $userid, $componentname, $id); 67 68 return '<a href="'. $rsspath .'">' . $OUTPUT->pix_icon('i/rss', $tooltiptext) . '</a>'; 69 } 70 71 /** 72 * This function returns the URL for the RSS XML file. 73 * 74 * @param int $contextid the course id 75 * @param int $userid the current user id 76 * @param string $componentname the name of the current component. For example "forum" 77 * @param string $additionalargs For modules, module instance id 78 * @return string the url of the RSS feed 79 */ 80 function rss_get_url($contextid, $userid, $componentname, $additionalargs) { 81 global $CFG; 82 if (empty($userid)) { 83 $userid = guest_user()->id; 84 } 85 $usertoken = rss_get_token($userid); 86 $url = '/rss/file.php'; 87 return moodle_url::make_file_url($url, '/'.$contextid.'/'.$usertoken.'/'.$componentname.'/'.$additionalargs.'/rss.xml'); 88 } 89 90 /** 91 * Print the link for the RSS feed with the correct RSS icon (Theme based) 92 * 93 * @param stdClass $contextid The id of the context under which the URL should be created 94 * @param int $userid The source of the RSS feed (site/course/group/user) 95 * @param string $componentname The name of the component for which the feed exists 96 * @param string $id The name by which to call the RSS File 97 * @param string $tooltiptext The tooltip to be displayed with the link 98 */ 99 function rss_print_link($contextid, $userid, $componentname, $id, $tooltiptext='') { 100 print rss_get_link($contextid, $userid, $componentname, $id, $tooltiptext); 101 102 } 103 104 /** 105 * Given an object, deletes all RSS files associated with it. 106 * 107 * @param string $componentname the name of the module ie 'forum'. Used to construct the cache path. 108 * @param stdClass $instance An object with an id member variable ie $forum, $glossary. 109 */ 110 function rss_delete_file($componentname, $instance) { 111 global $CFG; 112 113 $dirpath = "$CFG->cachedir/rss/$componentname"; 114 if (is_dir($dirpath)) { 115 if (!$dh = opendir($dirpath)) { 116 error_log("Directory permission error. RSS directory store for component '{$componentname}' exists but cannot be opened.", DEBUG_DEVELOPER); 117 return; 118 } 119 while (false !== ($filename = readdir($dh))) { 120 if ($filename!='.' && $filename!='..') { 121 if (preg_match("/{$instance->id}_/", $filename)) { 122 unlink("$dirpath/$filename"); 123 } 124 } 125 } 126 } 127 } 128 129 /** 130 * Are RSS feeds enabled for the supplied module instance? 131 * 132 * @param string $modname The name of the module to be checked 133 * @param stdClass $instance An instance of an activity module ie $forum, $glossary. 134 * @param bool $hasrsstype Should there be a rsstype member variable? 135 * @param bool $hasrssarticles Should there be a rssarticles member variable? 136 * @return bool whether or not RSS is enabled for the module 137 */ 138 function rss_enabled_for_mod($modname, $instance=null, $hasrsstype=true, $hasrssarticles=true) { 139 if ($hasrsstype) { 140 if (empty($instance->rsstype) || $instance->rsstype==0) { 141 return false; 142 } 143 } 144 145 //have they set the RSS feed to return 0 results? 146 if ($hasrssarticles) { 147 if (empty($instance->rssarticles) || $instance->rssarticles==0) { 148 return false; 149 } 150 } 151 152 if (!empty($instance) && !instance_is_visible($modname,$instance)) { 153 return false; 154 } 155 156 return true; 157 } 158 159 /** 160 * This function saves to file the rss feed specified in the parameters 161 * 162 * @param string $componentname the module name ie forum. Used to create a cache directory. 163 * @param string $filename the name of the file to be created ie "rss.xml" 164 * @param string $contents the data to be written to the file 165 * @param bool $expandfilename whether or not the fullname of the RSS file should be used 166 * @return bool whether the save was successful or not 167 */ 168 function rss_save_file($componentname, $filename, $contents, $expandfilename=true) { 169 global $CFG; 170 171 $status = true; 172 173 if (! $basedir = make_cache_directory ('rss/'. $componentname)) { 174 //Cannot be created, so error 175 $status = false; 176 } 177 178 if ($status) { 179 $fullfilename = $filename; 180 if ($expandfilename) { 181 $fullfilename = rss_get_file_full_name($componentname, $filename); 182 } 183 184 $rss_file = fopen($fullfilename, "w"); 185 if ($rss_file) { 186 $status = fwrite ($rss_file, $contents); 187 fclose($rss_file); 188 } else { 189 $status = false; 190 } 191 } 192 return $status; 193 } 194 195 /** 196 * Retrieve the location and file name of a cached RSS feed 197 * 198 * @param string $componentname the name of the component the RSS feed is being created for 199 * @param string $filename the name of the RSS FEED 200 * @return string The full name and path of the RSS file 201 */ 202 function rss_get_file_full_name($componentname, $filename) { 203 global $CFG; 204 return "$CFG->cachedir/rss/$componentname/$filename.xml"; 205 } 206 207 /** 208 * Construct the file name of the RSS File 209 * 210 * @param stdClass $instance the instance of the source of the RSS feed 211 * @param string $sql the SQL used to produce the RSS feed 212 * @param array $params the parameters used in the SQL query 213 * @return string the name of the RSS file 214 */ 215 function rss_get_file_name($instance, $sql, $params = array()) { 216 if ($params) { 217 // If a parameters array is passed, then we want to 218 // serialize it and then concatenate it with the sql. 219 // The reason for this is to generate a unique filename 220 // for queries using the same sql but different parameters. 221 asort($params); 222 $serializearray = serialize($params); 223 return $instance->id.'_'.md5($sql . $serializearray); 224 } else { 225 return $instance->id.'_'.md5($sql); 226 } 227 } 228 229 /** 230 * This function return all the common headers for every rss feed in the site 231 * 232 * @param string $title the title for the RSS Feed 233 * @param string $link the link for the origin of the RSS feed 234 * @param string $description the description of the contents of the RSS feed 235 * @return bool|string the standard header for the RSS feed 236 */ 237 function rss_standard_header($title = NULL, $link = NULL, $description = NULL) { 238 global $CFG, $USER, $OUTPUT; 239 240 $status = true; 241 $result = ""; 242 243 $site = get_site(); 244 245 if ($status) { 246 247 //Calculate title, link and description 248 if (empty($title)) { 249 $title = format_string($site->fullname); 250 } 251 if (empty($link)) { 252 $link = $CFG->wwwroot; 253 } 254 if (empty($description)) { 255 $description = $site->summary; 256 } 257 258 //xml headers 259 $result .= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; 260 $result .= "<rss version=\"2.0\">\n"; 261 262 //open the channel 263 $result .= rss_start_tag('channel', 1, true); 264 265 //write channel info 266 $result .= rss_full_tag('title', 2, false, strip_tags($title)); 267 $result .= rss_full_tag('link', 2, false, $link); 268 $result .= rss_full_tag('description', 2, false, $description); 269 $result .= rss_full_tag('generator', 2, false, 'Moodle'); 270 if (!empty($USER->lang)) { 271 $result .= rss_full_tag('language', 2, false, substr($USER->lang,0,2)); 272 } 273 $today = getdate(); 274 $result .= rss_full_tag('copyright', 2, false, '(c) '. $today['year'] .' '. format_string($site->fullname)); 275 /* 276 if (!empty($USER->email)) { 277 $result .= rss_full_tag('managingEditor', 2, false, fullname($USER)); 278 $result .= rss_full_tag('webMaster', 2, false, fullname($USER)); 279 } 280 */ 281 282 //write image info 283 $rsspix = $OUTPUT->image_url('i/rsssitelogo'); 284 285 //write the info 286 $result .= rss_start_tag('image', 2, true); 287 $result .= rss_full_tag('url', 3, false, $rsspix); 288 $result .= rss_full_tag('title', 3, false, 'moodle'); 289 $result .= rss_full_tag('link', 3, false, $CFG->wwwroot); 290 $result .= rss_full_tag('width', 3, false, '140'); 291 $result .= rss_full_tag('height', 3, false, '35'); 292 $result .= rss_end_tag('image', 2, true); 293 } 294 295 if (!$status) { 296 return false; 297 } else { 298 return $result; 299 } 300 } 301 302 303 /** 304 * Generates the rss XML code for every item passed in the array 305 * 306 * item->title: The title of the item 307 * item->author: The author of the item. Optional !! 308 * item->pubdate: The pubdate of the item 309 * item->link: The link url of the item 310 * item->description: The content of the item 311 * 312 * @param array $items an array of item objects 313 * @return bool|string the rss XML code for every item passed in the array 314 */ 315 function rss_add_items($items) { 316 global $CFG; 317 318 $result = ''; 319 320 if (!empty($items)) { 321 foreach ($items as $item) { 322 $result .= rss_start_tag('item',2,true); 323 //Include the category if exists (some rss readers will use it to group items) 324 if (isset($item->category)) { 325 $result .= rss_full_tag('category',3,false,$item->category); 326 } 327 if (isset($item->tags)) { 328 $attributes = array(); 329 if (isset($item->tagscheme)) { 330 $attributes['domain'] = s($item->tagscheme); 331 } 332 foreach ($item->tags as $tag) { 333 $result .= rss_full_tag('category', 3, false, $tag, $attributes); 334 } 335 } 336 $result .= rss_full_tag('title',3,false,strip_tags($item->title)); 337 $result .= rss_full_tag('link',3,false,$item->link); 338 $result .= rss_add_enclosures($item); 339 $result .= rss_full_tag('pubDate',3,false,gmdate('D, d M Y H:i:s',$item->pubdate).' GMT'); # MDL-12563 340 //Include the author if exists 341 if (isset($item->author) && !empty($item->author)) { 342 //$result .= rss_full_tag('author',3,false,$item->author); 343 //We put it in the description instead because it's more important 344 //for moodle than most other feeds, and most rss software seems to ignore 345 //the author field ... 346 $item->description = get_string('byname','',$item->author).'. <p>'.$item->description.'</p>'; 347 } 348 $result .= rss_full_tag('description',3,false,$item->description); 349 $result .= rss_full_tag('guid',3,false,$item->link,array('isPermaLink' => 'true')); 350 $result .= rss_end_tag('item',2,true); 351 352 } 353 } else { 354 $result = false; 355 } 356 return $result; 357 } 358 359 /** 360 * This function return all the common footers for every rss feed in the site. 361 * 362 * @return string 363 */ 364 function rss_standard_footer() { 365 $status = true; 366 $result = ''; 367 368 $result .= rss_end_tag('channel', 1, true); 369 $result .= '</rss>'; 370 371 return $result; 372 } 373 374 375 /** 376 * This function return an error xml file (string) to be sent when a rss is required (file.php) and something goes wrong 377 * 378 * @param string $errortype Type of error to send, default is rsserror 379 * @return stdClass returns a XML Feed with an error message in it 380 */ 381 function rss_geterrorxmlfile($errortype = 'rsserror') { 382 global $CFG; 383 384 $return = ''; 385 386 //XML Header 387 $return = rss_standard_header(); 388 389 //XML item 390 if ($return) { 391 $item = new stdClass(); 392 $item->title = "RSS Error"; 393 $item->link = $CFG->wwwroot; 394 $item->pubdate = time(); 395 $item->description = get_string($errortype); 396 $return .= rss_add_items(array($item)); 397 } 398 399 //XML Footer 400 if ($return) { 401 $return .= rss_standard_footer(); 402 } 403 404 return $return; 405 } 406 407 /** 408 * Get the ID of the user from a given RSS Token 409 * 410 * @param string $token the RSS token you would like to use to find the user id 411 * @return int The user id 412 */ 413 function rss_get_userid_from_token($token) { 414 global $DB; 415 416 $sql = "SELECT u.id 417 FROM {user} u 418 JOIN {user_private_key} k ON u.id = k.userid 419 WHERE u.deleted = 0 420 AND u.confirmed = 1 421 AND u.suspended = 0 422 AND k.script = 'rss' 423 AND k.value = ?"; 424 425 return $DB->get_field_sql($sql, array($token), IGNORE_MISSING); 426 } 427 428 /** 429 * Get the RSS Token from a given user id 430 * 431 * @param int $userid The user id 432 * @return string the RSS token for the user 433 */ 434 function rss_get_token($userid) { 435 return get_user_key('rss', $userid); 436 } 437 438 /** 439 * Removes the token for the given user from the DB 440 * @param int $userid The user id for the token you wish to delete 441 */ 442 function rss_delete_token($userid) { 443 delete_user_key('rss', $userid); 444 } 445 446 /** 447 * Return the xml start tag 448 * 449 * @param string $tag the xml tag name 450 * @param int $level the indentation level 451 * @param bool $endline whether or not to start new tags on a new line 452 * @param array $attributes the attributes of the xml tag 453 * @return string the xml start tag 454 */ 455 function rss_start_tag($tag,$level=0,$endline=false,$attributes=null) { 456 if ($endline) { 457 $endchar = "\n"; 458 } else { 459 $endchar = ""; 460 } 461 $attrstring = ''; 462 if (!empty($attributes) && is_array($attributes)) { 463 foreach ($attributes as $key => $value) { 464 $attrstring .= " ".$key."=\"".$value."\""; 465 } 466 } 467 return str_repeat(" ",$level*2)."<".$tag.$attrstring.">".$endchar; 468 } 469 470 /** 471 * Return the xml end tag 472 * @param string $tag the xml tag name 473 * @param int $level the indentation level 474 * @param bool $endline whether or not to start new tags on a new line 475 * @return string the xml end tag 476 */ 477 function rss_end_tag($tag,$level=0,$endline=true) { 478 if ($endline) { 479 $endchar = "\n"; 480 } else { 481 $endchar = ""; 482 } 483 return str_repeat(" ",$level*2)."</".$tag.">".$endchar; 484 } 485 486 /** 487 * Return the while xml element, including content 488 * 489 * @param string $tag the xml tag name 490 * @param int $level the indentation level 491 * @param bool $endline whether or not to start new tags on a new line 492 * @param string $content the text to go inside the tag 493 * @param array $attributes the attributes of the xml tag 494 * @return string the whole xml element 495 */ 496 function rss_full_tag($tag, $level, $endline, $content, $attributes = null) { 497 $st = rss_start_tag($tag,$level,$endline,$attributes); 498 $co=""; 499 $co = preg_replace("/\r\n|\r/", "\n", htmlspecialchars($content, ENT_COMPAT)); 500 $et = rss_end_tag($tag,0,true); 501 502 return $st.$co.$et; 503 } 504 505 /** 506 * Adds RSS Media Enclosures for "podcasting" by including attachments that 507 * are specified in the item->attachments field. 508 * 509 * @param stdClass $item representing an RSS item 510 * @return string RSS enclosure tags 511 */ 512 function rss_add_enclosures($item){ 513 global $CFG; 514 515 $returnstring = ''; 516 517 // list of media file extensions and their respective mime types 518 include_once($CFG->libdir.'/filelib.php'); 519 $mediafiletypes = get_mimetypes_array(); 520 521 // take into account attachments (e.g. from forum) - with these, we are able to know the file size 522 if (isset($item->attachments) && is_array($item->attachments)) { 523 foreach ($item->attachments as $attachment){ 524 $extension = strtolower(substr($attachment->url, strrpos($attachment->url, '.')+1)); 525 if (isset($mediafiletypes[$extension]['type'])) { 526 $type = $mediafiletypes[$extension]['type']; 527 } else { 528 $type = 'document/unknown'; 529 } 530 $returnstring .= "\n<enclosure url=\"$attachment->url\" length=\"$attachment->length\" type=\"$type\" />\n"; 531 } 532 } 533 534 return $returnstring; 535 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body