]>
git.wh0rd.org - tt-rss.git/blob - plugins/instances/init.php
6a7f7003a9a90076a167652c91f374ec133c57d7
2 class Instances
extends Plugin
implements IHandler
{
7 private $status_codes = array (
8 0 => "Connection failed" ,
10 2 => "Invalid object received" ,
11 16 => "Access denied" );
15 "Support for linking tt-rss instances together and sharing popular feeds." ,
20 function init ( $host ) {
21 $this -> link
= $host -> get_link ();
24 $host -> add_hook ( $host :: HOOK_PREFS_TABS
, $this );
25 $host -> add_handler ( "pref-instances" , "*" , $this );
26 $host -> add_handler ( "public" , "fbexport" , $this );
27 $host -> add_command ( "get-feeds" , "receive popular feeds from linked instances" , $this );
28 $host -> add_hook ( $host :: HOOK_UPDATE_TASK
, $this );
31 function hook_update_task ( $args ) {
32 _debug ( "Get linked feeds..." );
33 $this -> get_linked_feeds ( $this -> link
);
37 // -1 - never connected
38 // 0 - no data received
39 // 1 - data received successfully
40 // 2 - did not receive valid data
41 // >10 - server error, code + 10 (e.g. 16 means server error 6)
43 function get_linked_feeds ( $link , $instance_id = false ) {
45 $instance_qpart = "id = ' $instance_id ' AND " ;
49 if ( DB_TYPE
== "pgsql" ) {
50 $date_qpart = "last_connected < NOW() - INTERVAL '6 hours'" ;
52 $date_qpart = "last_connected < DATE_SUB(NOW(), INTERVAL 6 HOUR)" ;
55 $result = db_query ( $link , "SELECT id, access_key, access_url FROM ttrss_linked_instances
56 WHERE $instance_qpart $date_qpart ORDER BY last_connected" );
58 while ( $line = db_fetch_assoc ( $result )) {
61 _debug ( "Updating: " . $line [ 'access_url' ] . " ( $id )" );
63 $fetch_url = $line [ 'access_url' ] . '/public.php?op=fbexport' ;
64 $post_query = 'key=' . $line [ 'access_key' ];
66 $feeds = fetch_file_contents ( $fetch_url , false , false , false , $post_query );
68 // try doing it the old way
70 $fetch_url = $line [ 'access_url' ] . '/backend.php?op=fbexport' ;
71 $feeds = fetch_file_contents ( $fetch_url , false , false , false , $post_query );
75 $feeds = json_decode ( $feeds , true );
78 if ( $feeds [ 'error' ]) {
79 $status = $feeds [ 'error' ][ 'code' ] +
10 ;
83 db_query ( $link , "DELETE FROM ttrss_linked_feeds
84 WHERE instance_id = ' $id '" );
89 if ( count ( $feeds [ 'feeds' ]) > 0 ) {
91 db_query ( $link , "DELETE FROM ttrss_linked_feeds
92 WHERE instance_id = ' $id '" );
94 foreach ( $feeds [ 'feeds' ] as $feed ) {
95 $feed_url = db_escape_string ( $this -> link
, $feed [ 'feed_url' ]);
96 $title = db_escape_string ( $this -> link
, $feed [ 'title' ]);
97 $subscribers = db_escape_string ( $this -> link
, $feed [ 'subscribers' ]);
98 $site_url = db_escape_string ( $this -> link
, $feed [ 'site_url' ]);
100 db_query ( $link , "INSERT INTO ttrss_linked_feeds
101 (feed_url, site_url, title, subscribers, instance_id, created, updated)
103 (' $feed_url ', ' $site_url ', ' $title ', ' $subscribers ', ' $id ', NOW(), NOW())" );
106 // received 0 feeds, this might indicate that
107 // the instance on the other hand is rebuilding feedbrowser cache
108 // we will try again later
110 // TODO: maybe perform expiration based on updated here?
113 _debug ( "Processed " . count ( $feeds [ 'feeds' ]) . " feeds." );
123 _debug ( "Status: $status " );
125 db_query ( $link , "UPDATE ttrss_linked_instances SET
126 last_status_out = ' $status ', last_connected = NOW() WHERE id = ' $id '" );
132 function get_feeds () {
133 $this -> get_linked_feeds ( $this -> link
, false );
136 function get_prefs_js () {
137 return file_get_contents ( dirname ( __FILE__
) . "/instances.js" );
140 function hook_prefs_tabs ( $args ) {
141 if ( $_SESSION [ "access_level" ] >= 10 || SINGLE_USER_MODE
) {
142 ?
>< div id
= "instanceConfigTab" dojoType
= "dijit.layout.ContentPane"
143 href
= "backend.php?op=pref-instances"
144 title
= "<?php echo __('Linked') ?>" ></ div
>< ?php
148 function csrf_ignore ( $method ) {
149 $csrf_ignored = array ( "index" , "edit" );
151 return array_search ( $method , $csrf_ignored ) !== false ;
154 function before ( $method ) {
155 if ( $_SESSION [ "uid" ]) {
156 if ( $_SESSION [ "access_level" ] < 10 ) {
157 print __ ( "Your access level is insufficient to open this tab." );
170 $ids = db_escape_string ( $this -> link
, $_REQUEST [ 'ids' ]);
172 db_query ( $this -> link
, "DELETE FROM ttrss_linked_instances WHERE
177 $id = db_escape_string ( $this -> link
, $_REQUEST [ "id" ]);
178 $access_url = db_escape_string ( $this -> link
, $_REQUEST [ "access_url" ]);
179 $access_key = db_escape_string ( $this -> link
, $_REQUEST [ "access_key" ]);
181 db_query ( $this -> link
, "BEGIN" );
183 $result = db_query ( $this -> link
, "SELECT id FROM ttrss_linked_instances
184 WHERE access_url = ' $access_url '" );
186 if ( db_num_rows ( $result ) == 0 ) {
187 db_query ( $this -> link
, "INSERT INTO ttrss_linked_instances
188 (access_url, access_key, last_connected, last_status_in, last_status_out)
190 (' $access_url ', ' $access_key ', '1970-01-01', -1, -1)" );
194 db_query ( $this -> link
, "COMMIT" );
198 $id = db_escape_string ( $this -> link
, $_REQUEST [ "id" ]);
200 $result = db_query ( $this -> link
, "SELECT * FROM ttrss_linked_instances WHERE
203 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" id \" value= \" $id\" >" ;
204 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" op \" value= \" pref-instances \" >" ;
205 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" method \" value= \" editSave \" >" ;
207 print "<div class= \" dlgSec \" >" . __ ( "Instance" ). "</div>" ;
209 print "<div class= \" dlgSecCont \" >" ;
213 $access_url = htmlspecialchars ( db_fetch_result ( $result , 0 , "access_url" ));
215 print __ ( "URL:" ) . " " ;
217 print "<input dojoType= \" dijit.form.ValidationTextBox \" required= \" 1 \"
218 placeHolder= \" " . __ ( "Instance URL" ). " \"
219 regExp='^(http|https)://.*'
220 style= \" font-size : 16px; width: 20em \" name= \" access_url \"
221 value= \" $access_url\" >" ;
225 $access_key = htmlspecialchars ( db_fetch_result ( $result , 0 , "access_key" ));
229 print __ ( "Access key:" ) . " " ;
231 print "<input dojoType= \" dijit.form.ValidationTextBox \" required= \" 1 \"
232 placeHolder= \" " . __ ( "Access key" ). " \" regExp='\w{40}'
233 style= \" width: 20em \" name= \" access_key \" id= \" instance_edit_key \"
234 value= \" $access_key\" >" ;
236 print "<p class='insensitive'>" . __ ( "Use one access key for both linked instances." );
240 print "<div class= \" dlgButtons \" >
241 <div style='float : left'>
242 <button dojoType= \" dijit.form.Button \"
243 onclick= \" return dijit.byId('instanceEditDlg').regenKey() \" >" .
244 __ ( 'Generate new key' ). "</button>
246 <button dojoType= \" dijit.form.Button \"
247 onclick= \" return dijit.byId('instanceEditDlg').execute() \" >" .
248 __ ( 'Save' ). "</button>
249 <button dojoType= \" dijit.form.Button \"
250 onclick= \" return dijit.byId('instanceEditDlg').hide() \"\" >" .
251 __ ( 'Cancel' ). "</button></div>" ;
255 function editSave () {
256 $id = db_escape_string ( $this -> link
, $_REQUEST [ "id" ]);
257 $access_url = db_escape_string ( $this -> link
, $_REQUEST [ "access_url" ]);
258 $access_key = db_escape_string ( $this -> link
, $_REQUEST [ "access_key" ]);
260 db_query ( $this -> link
, "UPDATE ttrss_linked_instances SET
261 access_key = ' $access_key ', access_url = ' $access_url ',
262 last_connected = '1970-01-01'
269 if (! function_exists ( 'curl_init' )) {
270 print "<div style='padding : 1em'>" ;
271 print_error ( "This functionality requires CURL functions. Please enable CURL in your PHP configuration (you might also want to disable open_basedir in php.ini) and reload this page." );
275 print "<div id= \" pref-instance-wrap \" dojoType= \" dijit.layout.BorderContainer \" gutters= \" false \" >" ;
276 print "<div id= \" pref-instance-header \" dojoType= \" dijit.layout.ContentPane \" region= \" top \" >" ;
278 print "<div id= \" pref-instance-toolbar \" dojoType= \" dijit.Toolbar \" >" ;
280 $sort = db_escape_string ( $this -> link
, $_REQUEST [ "sort" ]);
282 if (! $sort ||
$sort == "undefined" ) {
283 $sort = "access_url" ;
286 print "<div dojoType= \" dijit.form.DropDownButton \" >" .
287 "<span>" . __ ( 'Select' ). "</span>" ;
288 print "<div dojoType= \" dijit.Menu \" style= \" display: none; \" >" ;
289 print "<div onclick= \" selectTableRows('prefInstanceList', 'all') \"
290 dojoType= \" dijit.MenuItem \" >" . __ ( 'All' ). "</div>" ;
291 print "<div onclick= \" selectTableRows('prefInstanceList', 'none') \"
292 dojoType= \" dijit.MenuItem \" >" . __ ( 'None' ). "</div>" ;
293 print "</div></div>" ;
295 print "<button dojoType= \" dijit.form.Button \" onclick= \" addInstance() \" >" . __ ( 'Link instance' ). "</button>" ;
296 print "<button dojoType= \" dijit.form.Button \" onclick= \" editSelectedInstance() \" >" . __ ( 'Edit' ). "</button>" ;
297 print "<button dojoType= \" dijit.form.Button \" onclick= \" removeSelectedInstances() \" >" . __ ( 'Remove' ). "</button>" ;
299 print "</div>" ; #toolbar
301 $result = db_query ( $this -> link
, "SELECT *,
302 (SELECT COUNT(*) FROM ttrss_linked_feeds
303 WHERE instance_id = ttrss_linked_instances.id) AS num_feeds
304 FROM ttrss_linked_instances
307 print "<p class= \" insensitive \" style='margin-left : 1em;'>" . __ ( "You can connect other instances of Tiny Tiny RSS to this one to share Popular feeds. Link to this instance of Tiny Tiny RSS by using this URL:" );
309 print " <a href= \" # \" onclick= \" alert('" . htmlspecialchars ( get_self_url_prefix ()). "') \" >(display url)</a>" ;
311 print "<p><table width='100%' id='prefInstanceList' class='prefInstanceList' cellspacing='0'>" ;
313 print "<tr class= \" title \" >
314 <td align='center' width= \" 5% \" > </td>
315 <td width=''><a href= \" # \" onclick= \" updateInstanceList('access_url') \" >" . __ ( 'Instance URL' ). "</a></td>
316 <td width='20%'><a href= \" # \" onclick= \" updateInstanceList('access_key') \" >" . __ ( 'Access key' ). "</a></td>
317 <td width='10%'><a href= \" # \" onclick= \" updateUsersList('last_connected') \" >" . __ ( 'Last connected' ). "</a></td>
318 <td width='10%'><a href= \" # \" onclick= \" updateUsersList('last_status_out') \" >" . __ ( 'Status' ). "</a></td>
319 <td width='10%'><a href= \" # \" onclick= \" updateUsersList('num_feeds') \" >" . __ ( 'Stored feeds' ). "</a></td>
324 while ( $line = db_fetch_assoc ( $result )) {
325 $class = ( $lnum %
2 ) ?
"even" : "odd" ;
328 $this_row_id = "id= \" LIRR- $id\" " ;
330 $line [ "last_connected" ] = make_local_datetime ( $this -> link
, $line [ "last_connected" ], false );
332 print "<tr class= \" $class\" $this_row_id >" ;
334 print "<td align='center'><input onclick='toggleSelectRow(this);'
335 type= \" checkbox \" id= \" LICHK- $id\" ></td>" ;
337 $onclick = "onclick='editInstance( $id , event)' title='" . __ ( 'Click to edit' ). "'" ;
339 $access_key = mb_substr ( $line [ 'access_key' ], 0 , 4 ) . '...' .
340 mb_substr ( $line [ 'access_key' ], - 4 );
342 print "<td $onclick >" . htmlspecialchars ( $line [ 'access_url' ]) . "</td>" ;
343 print "<td $onclick >" . htmlspecialchars ( $access_key ) . "</td>" ;
344 print "<td $onclick >" . htmlspecialchars ( $line [ 'last_connected' ]) . "</td>" ;
345 print "<td $onclick >" . $this -> status_codes
[ $line [ 'last_status_out' ]] . "</td>" ;
346 print "<td $onclick >" . htmlspecialchars ( $line [ 'num_feeds' ]) . "</td>" ;
355 print "</div>" ; #pane
358 $pluginhost -> run_hooks ( $pluginhost :: HOOK_PREFS_TAB
,
359 "hook_prefs_tab" , "prefInstances" );
361 print "</div>" ; #container
365 function fbexport () {
367 $access_key = db_escape_string ( $this -> link
, $_POST [ "key" ]);
369 // TODO: rate limit checking using last_connected
370 $result = db_query ( $this -> link
, "SELECT id FROM ttrss_linked_instances
371 WHERE access_key = ' $access_key '" );
373 if ( db_num_rows ( $result ) == 1 ) {
375 $instance_id = db_fetch_result ( $result , 0 , "id" );
377 $result = db_query ( $this -> link
, "SELECT feed_url, site_url, title, subscribers
378 FROM ttrss_feedbrowser_cache ORDER BY subscribers DESC LIMIT 100" );
382 while ( $line = db_fetch_assoc ( $result )) {
383 array_push ( $feeds , $line );
386 db_query ( $this -> link
, "UPDATE ttrss_linked_instances SET
387 last_status_in = 1 WHERE id = ' $instance_id '" );
389 print json_encode ( array ( "feeds" => $feeds ));
391 print json_encode ( array ( "error" => array ( "code" => 6 )));
395 function addInstance () {
396 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" op \" value= \" pref-instances \" >" ;
397 print "<input dojoType= \" dijit.form.TextBox \" style= \" display : none \" name= \" method \" value= \" add \" >" ;
399 print "<div class= \" dlgSec \" >" . __ ( "Instance" ). "</div>" ;
401 print "<div class= \" dlgSecCont \" >" ;
405 print __ ( "URL:" ) . " " ;
407 print "<input dojoType= \" dijit.form.ValidationTextBox \" required= \" 1 \"
408 placeHolder= \" " . __ ( "Instance URL" ). " \"
409 regExp='^(http|https)://.*'
410 style= \" font-size : 16px; width: 20em \" name= \" access_url \" >" ;
414 $access_key = sha1 ( uniqid ( rand (), true ));
418 print __ ( "Access key:" ) . " " ;
420 print "<input dojoType= \" dijit.form.ValidationTextBox \" required= \" 1 \"
421 placeHolder= \" " . __ ( "Access key" ). " \" regExp='\w{40}'
422 style= \" width: 20em \" name= \" access_key \" id= \" instance_add_key \"
423 value= \" $access_key\" >" ;
425 print "<p class='insensitive'>" . __ ( "Use one access key for both linked instances." );
429 print "<div class= \" dlgButtons \" >
430 <div style='float : left'>
431 <button dojoType= \" dijit.form.Button \"
432 onclick= \" return dijit.byId('instanceAddDlg').regenKey() \" >" .
433 __ ( 'Generate new key' ). "</button>
435 <button dojoType= \" dijit.form.Button \"
436 onclick= \" return dijit.byId('instanceAddDlg').execute() \" >" .
437 __ ( 'Create link' ). "</button>
438 <button dojoType= \" dijit.form.Button \"
439 onclick= \" return dijit.byId('instanceAddDlg').hide() \"\" >" .
440 __ ( 'Cancel' ). "</button></div>" ;