MDL-26285 block_rss_client - do not disable with enablerssfeeds
[moodle.git] / blocks / rss_client / block_rss_client.php
1 <?php
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/>.
18 /**
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
24  */
26  class block_rss_client extends block_base {
28     function init() {
29         $this->title = get_string('feedstitle', 'block_rss_client');
30     }
32     function preferred_width() {
33         return 210;
34     }
36     function applicable_formats() {
37         return array('all' => true, 'tag' => false);   // Needs work to make it work on tags MDL-11960
38     }
40     function specialization() {
41         // After the block has been loaded we customize the block's title display
42         if (!empty($this->config) && !empty($this->config->title)) {
43             // There is a customized block title, display it
44             $this->title = $this->config->title;
45         } else {
46             // No customized block title, use localized remote news feed string
47             $this->title = get_string('remotenewsfeed', 'block_rss_client');
48         }
49     }
51     function get_content() {
52         global $CFG, $DB;
54         if ($this->content !== NULL) {
55             return $this->content;
56         }
58         // initalise block content object
59         $this->content = new stdClass;
60         $this->content->text   = '';
61         $this->content->footer = '';
63         if (empty($this->instance)) {
64             return $this->content;
65         }
67         if (!isset($this->config)) {
68             // The block has yet to be configured - just display configure message in
69             // the block if user has permission to configure it
71             if (has_capability('block/rss_client:manageanyfeeds', $this->context)) {
72                 $this->content->text = get_string('feedsconfigurenewinstance2', 'block_rss_client');
73             }
75             return $this->content;
76         }
78         // How many feed items should we display?
79         $maxentries = 5;
80         if ( !empty($this->config->shownumentries) ) {
81             $maxentries = intval($this->config->shownumentries);
82         }elseif( isset($CFG->block_rss_client_num_entries) ) {
83             $maxentries = intval($CFG->block_rss_client_num_entries);
84         }
87         /* ---------------------------------
88          * Begin Normal Display of Block Content
89          * --------------------------------- */
91         $output = '';
94         if (!empty($this->config->rssid)) {
95             list($rss_ids_sql, $params) = $DB->get_in_or_equal($this->config->rssid);
97             $rss_feeds = $DB->get_records_select('block_rss_client', "id $rss_ids_sql", $params);
99             $showtitle = false;
100             if (count($rss_feeds) > 1) {
101                 // when many feeds show the title for each feed
102                 $showtitle = true;
103             }
105             foreach($rss_feeds as $feed){
106                 $output.= $this->get_feed_html($feed, $maxentries, $showtitle);
107             }
108         }
110         $this->content->text = $output;
112         return $this->content;
113     }
116     function instance_allow_multiple() {
117         return true;
118     }
120     function has_config() {
121         return true;
122     }
124     function instance_allow_config() {
125         return true;
126     }
128     /**
129      * Returns the html of a feed to be displaed in the block
130      *
131      * @param mixed feedrecord The feed record from the database
132      * @param int maxentries The maximum number of entries to be displayed
133      * @param boolean showtitle Should the feed title be displayed in html
134      * @return string html representing the rss feed content
135      */
136     function get_feed_html($feedrecord, $maxentries, $showtitle){
137         global $CFG;
138         require_once($CFG->libdir.'/simplepie/moodle_simplepie.php');
140         $feed = new moodle_simplepie($feedrecord->url);
142         if(isset($CFG->block_rss_client_timeout)){
143             $feed->set_cache_duration($CFG->block_rss_client_timeout*60);
144         }
146         if(debugging() && $feed->error()){
147             return '<p>'. $feedrecord->url .' Failed with code: '.$feed->error().'</p>';
148         }
150         $r = ''; // return string
152         if($this->config->block_rss_client_show_channel_image){
153             if($image = $feed->get_image_url()){
154                 $imagetitle = s($feed->get_image_title());
155                 $imagelink  = $feed->get_image_link();
157                 $r.='<div class="image" title="'.$imagetitle.'">'."\n";
158                 if($imagelink){
159                     $r.='<a href="'.$imagelink.'">';
160                 }
161                 $r.='<img src="'.$image.'" alt="'.$imagetitle.'" />'."\n";
162                 if($imagelink){
163                     $r.='</a>';
164                 }
165                 $r.= '</div>';
166             }
167         }
169         if(empty($feedrecord->preferredtitle)){
170             $feedtitle = $this->format_title($feed->get_title());
171         }else{
172             $feedtitle = $this->format_title($feedrecord->preferredtitle);
173         }
175         if($showtitle){
176             $r.='<div class="title">'.$feedtitle.'</div>';
177         }
180         $r.='<ul class="list no-overflow">'."\n";
182         $feeditems = $feed->get_items(0, $maxentries);
183         foreach($feeditems as $item){
184             $r.= $this->get_item_html($item);
185         }
187         $r.='</ul>';
190         if ($this->config->block_rss_client_show_channel_link) {
192             $channellink = $feed->get_link();
194             if (!empty($channellink)){
195                 //NOTE: this means the 'last feed' display wins the block title - but
196                 //this is exiting behaviour..
197                 $this->content->footer = '<a href="'.htmlspecialchars(clean_param($channellink,PARAM_URL)).'">'. get_string('clientchannellink', 'block_rss_client') .'</a>';
198             }
199         }
201         if (empty($this->config->title)){
202             //NOTE: this means the 'last feed' displayed wins the block title - but
203             //this is exiting behaviour..
204             $this->title = strip_tags($feedtitle);
205         }
207         return $r;
208     }
211     /**
212      * Returns the html list item of a feed item
213      *
214      * @param mixed item simplepie_item representing the feed item
215      * @return string html li representing the rss feed item
216      */
217     function get_item_html($item){
219         $link        = $item->get_link();
220         $title       = $item->get_title();
221         $description = $item->get_description();
224         if(empty($title)){
225             // no title present, use portion of description
226             $title = substr(strip_tags($description), 0, 20) . '...';
227         }else{
228             $title = break_up_long_words($title, 30);
229         }
231         if(empty($link)){
232             $link = $item->get_id();
233         } else {
234             //URLs in our RSS cache will be escaped (correctly as theyre store in XML)
235             //html_writer::link() will re-escape them. To prevent double escaping unescape here.
236             //This can by done using htmlspecialchars_decode() but moodle_url also has that effect
237             $link = new moodle_url($link);
238         }
240         $r = html_writer::start_tag('li');
241             $r.= html_writer::start_tag('div',array('class'=>'link'));
242                 $r.= html_writer::link(clean_param($link,PARAM_URL), s($title), array('onclick'=>'this.target="_blank"'));
243             $r.= html_writer::end_tag('div');
245             if($this->config->display_description && !empty($description)){
247                 $description = break_up_long_words($description, 30);
249                 $formatoptions = new stdClass();
250                 $formatoptions->para = false;
252                 $r.= html_writer::start_tag('div',array('class'=>'description'));
253                     $r.= format_text($description, FORMAT_HTML, $formatoptions, $this->page->course->id);
254                 $r.= html_writer::end_tag('div');
255             }
256         $r.= html_writer::end_tag('li');
258         return $r;
259     }
261     /**
262      * Strips a large title to size and adds ... if title too long
263      *
264      * @param string title to shorten
265      * @param int max character length of title
266      * @return string title s() quoted and shortened if necessary
267      */
268     function format_title($title,$max=64) {
270         // Loading the textlib singleton instance. We are going to need it.
271         $textlib = textlib_get_instance();
273         if ($textlib->strlen($title) <= $max) {
274             return s($title);
275         } else {
276             return s($textlib->substr($title,0,$max-3).'...');
277         }
278     }
280     /**
281      * cron - goes through all feeds and retrieves them with the cache
282      * duration set to 0 in order to force the retrieval of the item and
283      * refresh the cache
284      *
285      * @return boolean true if all feeds were retrieved succesfully
286      */
287     function cron() {
288         global $CFG, $DB;
289         require_once($CFG->libdir.'/simplepie/moodle_simplepie.php');
291         // We are going to measure execution times
292         $starttime =  microtime();
294         // And we have one initial $status
295         $status = true;
297         // Fetch all site feeds.
298         $rs = $DB->get_recordset('block_rss_client');
299         $counter = 0;
300         mtrace('');
301         foreach ($rs as $rec) {
302             mtrace('    ' . $rec->url . ' ', '');
303             // Fetch the rss feed, using standard simplepie caching
304             // so feeds will be renewed only if cache has expired
305             @set_time_limit(60);
307             $feed =  new moodle_simplepie();
308             // set timeout for longer than normal to be agressive at
309             // fetching feeds if possible..
310             $feed->set_timeout(40);
311             $feed->set_cache_duration(0);
312             $feed->set_feed_url($rec->url);
313             $feed->init();
315             if ($feed->error()) {
316                 mtrace ('error');
317                 mtrace ('SimplePie failed with error:'.$feed->error());
318                 $status = false;
319             } else {
320                 mtrace ('ok');
321             }
322             $counter ++;
323         }
324         $rs->close();
326         // Show times
327         mtrace($counter . ' feeds refreshed (took ' . microtime_diff($starttime, microtime()) . ' seconds)');
329         // And return $status
330         return $status;
331     }