rss MDL-25563 made the rss block unescape item URLs to prevent double escaping
[moodle.git] / blocks / rss_client / block_rss_client.php
CommitLineData
4ca6cfbf 1<?php
0864a083 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/>.
340f8198 17
706dae5e 18/**
0864a083 19 * A block which displays Remote feeds
20 *
21 * @package rss_client
22 * @copyright Daryl Hawes
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL
706dae5e 24 */
0864a083 25
706dae5e 26 class block_rss_client extends block_base {
72035d79 27
28 function init() {
ab48bbb2 29 $this->title = get_string('feedstitle', 'block_rss_client');
72035d79 30 }
31
74528955 32 function preferred_width() {
33 return 210;
34 }
35
9a7a199f 36 function applicable_formats() {
2ace936b 37 return array('all' => true, 'tag' => false); // Needs work to make it work on tags MDL-11960
9a7a199f 38 }
39
72035d79 40 function specialization() {
92ce1eb2 41 // After the block has been loaded we customize the block's title display
72035d79 42 if (!empty($this->config) && !empty($this->config->title)) {
92ce1eb2 43 // There is a customized block title, display it
72035d79 44 $this->title = $this->config->title;
92ce1eb2 45 } else {
46 // No customized block title, use localized remote news feed string
ab48bbb2 47 $this->title = get_string('remotenewsfeed', 'block_rss_client');
72035d79 48 }
49 }
fd9576fb 50
72035d79 51 function get_content() {
0864a083 52 global $CFG, $DB;
839bbd4d 53
0864a083 54 if ($this->content !== NULL) {
72035d79 55 return $this->content;
56 }
57
b58961ac 58 if (!$CFG->enablerssfeeds) {
59 $this->content->text = '';
60 if ($this->page->user_is_editing()) {
61 $this->content->text = get_string('disabledrssfeeds', 'block_rss_client');
62 }
63 return $this->content;
64 }
65
0864a083 66 // initalise block content object
72035d79 67 $this->content = new stdClass;
c7d6e274 68 $this->content->text = '';
69 $this->content->footer = '';
70
839bbd4d 71 if (empty($this->instance)) {
72035d79 72 return $this->content;
73 }
74
0864a083 75 if (!isset($this->config)) {
76 // The block has yet to be configured - just display configure message in
77 // the block if user has permission to configure it
78
79 if (has_capability('block/rss_client:manageanyfeeds', $this->context)) {
80 $this->content->text = get_string('feedsconfigurenewinstance2', 'block_rss_client');
81 }
72035d79 82
f1a34d8f 83 return $this->content;
72035d79 84 }
88dffda5 85
0864a083 86 // How many feed items should we display?
87 $maxentries = 5;
88 if ( !empty($this->config->shownumentries) ) {
89 $maxentries = intval($this->config->shownumentries);
90 }elseif( isset($CFG->block_rss_client_num_entries) ) {
91 $maxentries = intval($CFG->block_rss_client_num_entries);
92 }
93
94
95 /* ---------------------------------
96 * Begin Normal Display of Block Content
97 * --------------------------------- */
98
99 $output = '';
100
101
f1a34d8f 102 if (!empty($this->config->rssid)) {
0864a083 103 list($rss_ids_sql, $params) = $DB->get_in_or_equal($this->config->rssid);
104
105 $rss_feeds = $DB->get_records_select('block_rss_client', "id $rss_ids_sql", $params);
106
107 $showtitle = false;
108 if (count($rss_feeds) > 1) {
109 // when many feeds show the title for each feed
110 $showtitle = true;
339c1d11 111 }
72035d79 112
0864a083 113 foreach($rss_feeds as $feed){
114 $output.= $this->get_feed_html($feed, $maxentries, $showtitle);
fd6360ec 115 }
92ce1eb2 116 }
fd9576fb 117
92ce1eb2 118 $this->content->text = $output;
0864a083 119
92ce1eb2 120 return $this->content;
121 }
fd9576fb 122
0864a083 123
92ce1eb2 124 function instance_allow_multiple() {
125 return true;
126 }
127
128 function has_config() {
129 return true;
130 }
131
132 function instance_allow_config() {
133 return true;
134 }
fd9576fb 135
53034e08 136 /**
0864a083 137 * Returns the html of a feed to be displaed in the block
138 *
139 * @param mixed feedrecord The feed record from the database
140 * @param int maxentries The maximum number of entries to be displayed
141 * @param boolean showtitle Should the feed title be displayed in html
142 * @return string html representing the rss feed content
53034e08 143 */
0864a083 144 function get_feed_html($feedrecord, $maxentries, $showtitle){
145 global $CFG;
146 require_once($CFG->libdir.'/simplepie/moodle_simplepie.php');
88dffda5 147
0864a083 148 $feed = new moodle_simplepie($feedrecord->url);
874da31f 149
0864a083 150 if(isset($CFG->block_rss_client_timeout)){
151 $feed->set_cache_duration($CFG->block_rss_client_timeout*60);
152 }
72035d79 153
0864a083 154 if(debugging() && $feed->error()){
155 return '<p>'. $feedrecord->url .' Failed with code: '.$feed->error().'</p>';
156 }
325744cb 157
0864a083 158 $r = ''; // return string
325744cb 159
0864a083 160 if($this->config->block_rss_client_show_channel_image){
161 if($image = $feed->get_image_url()){
162 $imagetitle = s($feed->get_image_title());
163 $imagelink = $feed->get_image_link();
325744cb 164
0864a083 165 $r.='<div class="image" title="'.$imagetitle.'">'."\n";
166 if($imagelink){
167 $r.='<a href="'.$imagelink.'">';
168 }
169 $r.='<img src="'.$image.'" alt="'.$imagetitle.'" />'."\n";
170 if($imagelink){
171 $r.='</a>';
172 }
173 $r.= '</div>';
81610483 174 }
0864a083 175 }
81610483 176
0864a083 177 if(empty($feedrecord->preferredtitle)){
178 $feedtitle = $this->format_title($feed->get_title());
179 }else{
180 $feedtitle = $this->format_title($feedrecord->preferredtitle);
181 }
cc9b80f9 182
0864a083 183 if($showtitle){
184 $r.='<div class="title">'.$feedtitle.'</div>';
185 }
dfedf5fd 186
fd9576fb 187
367a75fa 188 $r.='<ul class="list no-overflow">'."\n";
fd2339cf 189
0864a083 190 $feeditems = $feed->get_items(0, $maxentries);
191 foreach($feeditems as $item){
192 $r.= $this->get_item_html($item);
193 }
fd9576fb 194
0864a083 195 $r.='</ul>';
72035d79 196
90cec6d9 197
0864a083 198 if ($this->config->block_rss_client_show_channel_link) {
cc9b80f9 199
43ba7a54 200 $channellink = $feed->get_link();
0864a083 201
202 if (!empty($channellink)){
203 //NOTE: this means the 'last feed' display wins the block title - but
204 //this is exiting behaviour..
43ba7a54 205 $this->content->footer = '<a href="'.htmlspecialchars(clean_param($channellink,PARAM_URL)).'">'. get_string('clientchannellink', 'block_rss_client') .'</a>';
72035d79 206 }
0864a083 207 }
72035d79 208
0864a083 209 if (empty($this->config->title)){
210 //NOTE: this means the 'last feed' displayed wins the block title - but
211 //this is exiting behaviour..
212 $this->title = strip_tags($feedtitle);
213 }
88dffda5 214
0864a083 215 return $r;
216 }
217
218
219 /**
220 * Returns the html list item of a feed item
221 *
222 * @param mixed item simplepie_item representing the feed item
223 * @return string html li representing the rss feed item
224 */
225 function get_item_html($item){
226
227 $link = $item->get_link();
228 $title = $item->get_title();
229 $description = $item->get_description();
230
231
232 if(empty($title)){
233 // no title present, use portion of description
234 $title = substr(strip_tags($description), 0, 20) . '...';
235 }else{
236 $title = break_up_long_words($title, 30);
72035d79 237 }
238
0864a083 239 if(empty($link)){
240 $link = $item->get_id();
77fb0183
AD
241 } else {
242 //URLs in our RSS cache will be escaped (correctly as theyre store in XML)
243 //html_writer::link() will re-escape them. To prevent double escaping unescape here.
244 //This can by done using htmlspecialchars_decode() but moodle_url also has that effect
245 $link = new moodle_url($link);
72035d79 246 }
0864a083 247
cdb4a88d
AD
248 $r = html_writer::start_tag('li');
249 $r.= html_writer::start_tag('div',array('class'=>'link'));
43ba7a54 250 $r.= html_writer::link(clean_param($link,PARAM_URL), s($title), array('onclick'=>'this.target="_blank"'));
cdb4a88d 251 $r.= html_writer::end_tag('div');
0864a083 252
cdb4a88d 253 if($this->config->display_description && !empty($description)){
0864a083 254
cdb4a88d 255 $description = break_up_long_words($description, 30);
0864a083 256
dd4bee83 257 $formatoptions = new stdClass();
cdb4a88d 258 $formatoptions->para = false;
0864a083 259
cdb4a88d
AD
260 $r.= html_writer::start_tag('div',array('class'=>'description'));
261 $r.= format_text($description, FORMAT_HTML, $formatoptions, $this->page->course->id);
262 $r.= html_writer::end_tag('div');
263 }
264 $r.= html_writer::end_tag('li');
0864a083 265
266 return $r;
72035d79 267 }
79598cf0 268
0864a083 269 /**
270 * Strips a large title to size and adds ... if title too long
271 *
272 * @param string title to shorten
273 * @param int max character length of title
274 * @return string title s() quoted and shortened if necessary
275 */
6aad3d29 276 function format_title($title,$max=64) {
277
0864a083 278 // Loading the textlib singleton instance. We are going to need it.
6aad3d29 279 $textlib = textlib_get_instance();
280
281 if ($textlib->strlen($title) <= $max) {
282 return s($title);
283 } else {
284 return s($textlib->substr($title,0,$max-3).'...');
285 }
286 }
287
0864a083 288 /**
4ca6cfbf 289 * cron - goes through all feeds and retrieves them with the cache
0864a083 290 * duration set to 0 in order to force the retrieval of the item and
291 * refresh the cache
292 *
293 * @return boolean true if all feeds were retrieved succesfully
294 */
6aad3d29 295 function cron() {
f28f2d90 296 global $CFG, $DB;
0864a083 297 require_once($CFG->libdir.'/simplepie/moodle_simplepie.php');
6aad3d29 298
0864a083 299 // We are going to measure execution times
6aad3d29 300 $starttime = microtime();
301
0864a083 302 // And we have one initial $status
6aad3d29 303 $status = true;
304
0864a083 305 // Fetch all site feeds.
f28f2d90 306 $rs = $DB->get_recordset('block_rss_client');
6aad3d29 307 $counter = 0;
308 mtrace('');
f28f2d90 309 foreach ($rs as $rec) {
6aad3d29 310 mtrace(' ' . $rec->url . ' ', '');
0864a083 311 // Fetch the rss feed, using standard simplepie caching
312 // so feeds will be renewed only if cache has expired
df61da53 313 @set_time_limit(60);
0864a083 314
315 $feed = new moodle_simplepie();
4ca6cfbf 316 // set timeout for longer than normal to be agressive at
d175fe4c 317 // fetching feeds if possible..
318 $feed->set_timeout(40);
0864a083 319 $feed->set_cache_duration(0);
320 $feed->set_feed_url($rec->url);
321 $feed->init();
322
323 if ($feed->error()) {
6aad3d29 324 mtrace ('error');
0864a083 325 mtrace ('SimplePie failed with error:'.$feed->error());
6aad3d29 326 $status = false;
0864a083 327 } else {
328 mtrace ('ok');
6aad3d29 329 }
330 $counter ++;
331 }
f28f2d90 332 $rs->close();
6aad3d29 333
0864a083 334 // Show times
6aad3d29 335 mtrace($counter . ' feeds refreshed (took ' . microtime_diff($starttime, microtime()) . ' seconds)');
336
0864a083 337 // And return $status
6aad3d29 338 return $status;
339 }
72035d79 340}
808c2a69 341
4ca6cfbf 342