3 * Generic RSS Widget Class
5 * Copyright (c) Xerox Corporation, Codendi Team, 2001-2009. All rights reserved
6 * Copyright 2012,2014,2019, Franck Villaume - TrivialDev
7 * http://fusionforge.org
9 * This file is a part of Fusionforge.
11 * Fusionforge is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * Fusionforge is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with Fusionforge. If not, see <http://www.gnu.org/licenses/>.
25 require_once 'Widget.class.php';
32 /* abstract */ class Widget_Rss extends Widget {
35 function __construct($id, $owner_id, $owner_type) {
36 parent::__construct($id);
37 $this->setOwner($owner_id, $owner_type);
40 $hp = Codendi_HTMLPurifier::instance();
41 return $this->rss_title ? $hp->purify($this->rss_title, CODENDI_PURIFIER_CONVERT_HTML) : _('RSS Reader');
43 function getContent() {
45 $hp = Codendi_HTMLPurifier::instance();
48 if (function_exists('idn_to_utf8()')) {
49 function idn_to_utf8($param) {
50 return idn_to_unicode($param);
53 if (!(include_once 'simplepie/autoloader.php')) // vendor, debian
54 if (!(include_once 'php-simplepie/autoloader.php')) // fedora
55 exit_error(_('Could not load the SimplePie PHP library.'));
56 if (!is_dir(forge_get_config('data_path') .'/rss')) {
57 if (!mkdir(forge_get_config('data_path') .'/rss')) {
58 $content .= $HTML->error_msg(_('Cannot create backend directory. Contact forge administrator.'));
61 $rss = new SimplePie();
62 $rss->set_feed_url($this->rss_url);
63 $rss->set_cache_location(forge_get_config('data_path') .'/rss');
65 $rss->handle_content_type();
67 $items = array_slice($rss->get_items(), 0, $max_items);
69 $content .= $HTML->listTableTop();
70 foreach($items as $key => $item){
71 $content .= '<tr><td style="width:99%">';
72 if ($image = $item->get_link(0, 'image')) {
73 //hack to display twitter avatar
74 $content .= '<img src="'. $hp->purify($image, CODENDI_PURIFIER_CONVERT_HTML) .'" style="float:left; margin-right:1em;" />';
76 /* Do not trust SimplePie for purifying. */
77 $content .= html_e('a', array(
78 'href' => util_unconvert_htmlspecialchars($item->get_link()),
79 ), util_html_secure($item->get_title()));
80 if ($item->get_date()) {
81 $content .= '<span style="color:#999;" title="'. date(_("Y-m-d H:i"), $item->get_date('U')) .'"> - '. $this->_date_ago($item->get_date('U'),time()) .'</span>';
83 $content .= '</td></tr>';
85 $content .= $HTML->listTableBottom();
87 $content = $HTML->information(_('No element to display'));
95 function hasPreferences() {
98 function getPreferences() {
99 $hp = Codendi_HTMLPurifier::instance();
103 $prefs .= _('Title')._(':');
106 $prefs .= '<input type="text" class="textfield_medium" name="rss[title]" value="'. $hp->purify($this->rss_title, CODENDI_PURIFIER_CONVERT_HTML) .'" /></td></tr>';
109 $prefs .= 'URL'._(':');
112 $prefs .= '<input type="url" class="textfield_medium" name="rss[url]" value="'. $hp->purify($this->rss_url, CODENDI_PURIFIER_CONVERT_HTML) .'" /></td></tr>';
113 $prefs .= '</table>';
116 function getInstallPreferences() {
120 $prefs .= _('Title')._(':');
123 $prefs .= '<input type="text" class="textfield_medium" name="rss[title]" value="" placeholder="'. _('Set your RSS Title here.') .'" /></td></tr>';
126 $prefs .= 'URL'._(':');
129 $prefs .= '<input type="url" class="textfield_medium" name="rss[url]" value="" placeholder="'._('Set your RSS URL here.').'" />';
132 $prefs .= '</table>';
135 function cloneContent($id, $owner_id, $owner_type) {
136 $sql = "INSERT INTO widget_rss (owner_id, owner_type, title, url)
137 SELECT $1, $2, title, url
139 WHERE owner_id = $3 AND owner_type = $4";
140 $res = db_query_params($sql,array($owner_id,$owner_type,$this->owner_id,$this->owner_type));
141 return db_insertid($res,'widget_rss','id');
143 function loadContent($id) {
144 $sql = "SELECT * FROM widget_rss WHERE owner_id = $1 AND owner_type = $2 AND id = $3";
145 $res = db_query_params($sql,array($this->owner_id,$this->owner_type,$id));
146 if ($res && db_numrows($res)) {
147 $data = db_fetch_array($res);
148 $this->rss_title = $data['title'];
149 $this->rss_url = $data['url'];
150 $this->content_id = $id;
153 function create(&$request) {
155 $vUrl = new Valid_String('url');
156 $vUrl->setErrorMessage(_("Cannot add empty RSS URL"));
158 if($request->validInArray('rss', $vUrl)) {
159 $rss = $request->get('rss');
160 $vTitle = new Valid_String('title');
162 if (!$request->validInArray('rss', $vTitle)) {
163 if (!(include_once 'simplepie/simplepie.inc')) // vendor, debian
164 if (!(include_once 'php-simplepie/autoloader.php')) // fedora
165 exit_error(_('Could not load the SimplePie PHP library.'));
166 if (!is_dir(forge_get_config('data_path') .'/rss')) {
167 mkdir(forge_get_config('data_path') .'/rss');
169 $rss_reader = new SimplePie($rss['url'], forge_get_config('data_path') .'/rss', null, forge_get_config('sys_proxy'));
171 $rss['title'] = $rss_reader->get_title();
176 $sql = 'INSERT INTO widget_rss (owner_id, owner_type, title, url) VALUES ($1,$2,$3,$4)';
177 $res = db_query_params($sql,array($this->owner_id,$this->owner_type,$rss['title'],$rss['url']));
178 $content_id = db_insertid($res, 'widget_rss', 'id');
182 function updatePreferences(&$request) {
184 $vContentId = new Valid_UInt('content_id');
185 $vContentId->required();
186 if (($rss = $request->get('rss')) && $request->valid($vContentId)) {
187 $vUrl = new Valid_String('url');
188 if($request->validInArray('rss', $vUrl)) {
194 $vTitle = new Valid_String('title');
195 if($request->validInArray('rss', $vTitle)) {
196 $title = $rss['title'] ;
201 if ($url || $title) {
202 $sql = "UPDATE widget_rss SET title=$1 , url=$2 WHERE owner_id =$3 AND owner_type = $4 AND id = $5";
203 db_query_params($sql,array($title,$url,$this->owner_id,$this->owner_type,(int)$request->get('content_id')));
209 function destroy($id) {
210 $sql = 'DELETE FROM widget_rss WHERE id = $1 AND owner_id = $2 AND owner_type = $3';
211 db_query_params($sql,array($id,$this->owner_id,$this->owner_type));
213 function isUnique() {
216 function _date_ago($from_time, $to_time) {
217 $distance_in_minutes = round((abs($to_time - $from_time))/60);
219 if ($distance_in_minutes <= 1) {
220 return ($distance_in_minutes == 0) ? _('less than 1 minute') : _('1 minute');
221 } elseif ($distance_in_minutes <= 44) {
222 return sprintf(_('%d minutes ago'), $distance_in_minutes);
223 } elseif ($distance_in_minutes <= 89) {
224 return _('About one hour') ;
225 } elseif ($distance_in_minutes <= 1439) {
226 return sprintf(_('about %s hours'), round($distance_in_minutes/60));
227 } elseif ($distance_in_minutes <= 2879) {
228 return _('About one day') ;
229 } elseif ($distance_in_minutes <= 43199) {
230 return sprintf(_('%s days ago'), round($distance_in_minutes/1440));
231 } elseif ($distance_in_minutes <= 86399) {
232 return _('About one month') ;
233 } elseif ($distance_in_minutes <= 525959) {
234 return sprintf(_('%s months ago'), round($distance_in_minutes/43200));
235 } elseif ($distance_in_minutes <= 1051919) {
236 return _('About one year') ;
238 return sprintf(_('over %s years'), round($distance_in_minutes/525960));