]>
git.wh0rd.org - tt-rss.git/blob - classes/api.php
cf8b2dcfca93d95dffe395c9d2162b0f380a737f
3 class API
extends Handler
{
12 function before ( $method ) {
13 if ( parent
:: before ( $method )) {
14 header ( "Content-Type: text/json" );
16 if (! $_SESSION [ "uid" ] && $method != "login" && $method != "isloggedin" ) {
17 print $this -> wrap ( self
:: STATUS_ERR
, array ( "error" => 'NOT_LOGGED_IN' ));
21 if ( $_SESSION [ "uid" ] && $method != "logout" && ! get_pref ( $this -> link
, 'ENABLE_API_ACCESS' )) {
22 print $this -> wrap ( self
:: STATUS_ERR
, array ( "error" => 'API_DISABLED' ));
26 $this -> seq
= ( int ) $_REQUEST [ 'seq' ];
33 function wrap ( $status , $reply ) {
34 print json_encode ( array ( "seq" => $this -> seq
,
36 "content" => $reply ));
39 function getVersion () {
40 $rv = array ( "version" => VERSION
);
41 print $this -> wrap ( self
:: STATUS_OK
, $rv );
44 function getApiLevel () {
45 $rv = array ( "level" => self
:: API_LEVEL
);
46 print $this -> wrap ( self
:: STATUS_OK
, $rv );
52 $login = db_escape_string ( $this -> link
, $_REQUEST [ "user" ]);
53 $password = $_REQUEST [ "password" ];
54 $password_base64 = base64_decode ( $_REQUEST [ "password" ]);
56 if ( SINGLE_USER_MODE
) $login = "admin" ;
58 $result = db_query ( $this -> link
, "SELECT id FROM ttrss_users WHERE login = ' $login '" );
60 if ( db_num_rows ( $result ) != 0 ) {
61 $uid = db_fetch_result ( $result , 0 , "id" );
67 print $this -> wrap ( self
:: STATUS_ERR
, array ( "error" => "LOGIN_ERROR" ));
71 if ( get_pref ( $this -> link
, "ENABLE_API_ACCESS" , $uid )) {
72 if ( authenticate_user ( $this -> link
, $login , $password )) { // try login with normal password
73 print $this -> wrap ( self
:: STATUS_OK
, array ( "session_id" => session_id (),
74 "api_level" => self
:: API_LEVEL
));
75 } else if ( authenticate_user ( $this -> link
, $login , $password_base64 )) { // else try with base64_decoded password
76 print $this -> wrap ( self
:: STATUS_OK
, array ( "session_id" => session_id (),
77 "api_level" => self
:: API_LEVEL
));
78 } else { // else we are not logged in
79 print $this -> wrap ( self
:: STATUS_ERR
, array ( "error" => "LOGIN_ERROR" ));
82 print $this -> wrap ( self
:: STATUS_ERR
, array ( "error" => "API_DISABLED" ));
89 print $this -> wrap ( self
:: STATUS_OK
, array ( "status" => "OK" ));
92 function isLoggedIn () {
93 print $this -> wrap ( self
:: STATUS_OK
, array ( "status" => $_SESSION [ "uid" ] != '' ));
96 function getUnread () {
97 $feed_id = db_escape_string ( $this -> link
, $_REQUEST [ "feed_id" ]);
98 $is_cat = db_escape_string ( $this -> link
, $_REQUEST [ "is_cat" ]);
101 print $this -> wrap ( self
:: STATUS_OK
, array ( "unread" => getFeedUnread ( $this -> link
, $feed_id , $is_cat )));
103 print $this -> wrap ( self
:: STATUS_OK
, array ( "unread" => getGlobalUnread ( $this -> link
)));
107 /* Method added for ttrss-reader for Android */
108 function getCounters () {
109 print $this -> wrap ( self
:: STATUS_OK
, getAllCounters ( $this -> link
));
112 function getFeeds () {
113 $cat_id = db_escape_string ( $this -> link
, $_REQUEST [ "cat_id" ]);
114 $unread_only = sql_bool_to_bool ( $_REQUEST [ "unread_only" ]);
115 $limit = ( int ) db_escape_string ( $this -> link
, $_REQUEST [ "limit" ]);
116 $offset = ( int ) db_escape_string ( $this -> link
, $_REQUEST [ "offset" ]);
117 $include_nested = sql_bool_to_bool ( $_REQUEST [ "include_nested" ]);
119 $feeds = $this -> api_get_feeds ( $this -> link
, $cat_id , $unread_only , $limit , $offset , $include_nested );
121 print $this -> wrap ( self
:: STATUS_OK
, $feeds );
124 function getCategories () {
125 $unread_only = sql_bool_to_bool ( $_REQUEST [ "unread_only" ]);
126 $enable_nested = sql_bool_to_bool ( $_REQUEST [ "enable_nested" ]);
128 // TODO do not return empty categories, return Uncategorized and standard virtual cats
131 $nested_qpart = "parent_cat IS NULL" ;
133 $nested_qpart = "true" ;
135 $result = db_query ( $this -> link
, "SELECT
136 id, title, order_id, (SELECT COUNT(id) FROM
138 ttrss_feed_categories.id IS NOT NULL AND cat_id = ttrss_feed_categories.id) AS num_feeds,
139 (SELECT COUNT(id) FROM
140 ttrss_feed_categories AS c2 WHERE
141 c2.parent_cat = ttrss_feed_categories.id) AS num_cats
142 FROM ttrss_feed_categories
143 WHERE $nested_qpart AND owner_uid = " .
148 while ( $line = db_fetch_assoc ( $result )) {
149 if ( $line [ "num_feeds" ] > 0 ||
$line [ "num_cats" ] > 0 ) {
150 $unread = getFeedUnread ( $this -> link
, $line [ "id" ], true );
153 $unread +
= getCategoryChildrenUnread ( $this -> link
, $line [ "id" ]);
155 if ( $unread ||
! $unread_only ) {
156 array_push ( $cats , array ( "id" => $line [ "id" ],
157 "title" => $line [ "title" ],
159 "order_id" => ( int ) $line [ "order_id" ],
165 foreach ( array (- 2 ,- 1 , 0 ) as $cat_id ) {
166 $unread = getFeedUnread ( $this -> link
, $cat_id , true );
168 if ( $unread ||
! $unread_only ) {
169 array_push ( $cats , array ( "id" => $cat_id ,
170 "title" => getCategoryTitle ( $this -> link
, $cat_id ),
171 "unread" => $unread ));
175 print $this -> wrap ( self
:: STATUS_OK
, $cats );
178 function getHeadlines () {
179 $feed_id = db_escape_string ( $this -> link
, $_REQUEST [ "feed_id" ]);
180 if ( $feed_id != "" ) {
182 $limit = ( int ) db_escape_string ( $this -> link
, $_REQUEST [ "limit" ]);
184 if (! $limit ||
$limit >= 60 ) $limit = 60 ;
186 $offset = ( int ) db_escape_string ( $this -> link
, $_REQUEST [ "skip" ]);
187 $filter = db_escape_string ( $this -> link
, $_REQUEST [ "filter" ]);
188 $is_cat = sql_bool_to_bool ( $_REQUEST [ "is_cat" ]);
189 $show_excerpt = sql_bool_to_bool ( $_REQUEST [ "show_excerpt" ]);
190 $show_content = sql_bool_to_bool ( $_REQUEST [ "show_content" ]);
191 /* all_articles, unread, adaptive, marked, updated */
192 $view_mode = db_escape_string ( $this -> link
, $_REQUEST [ "view_mode" ]);
193 $include_attachments = sql_bool_to_bool ( $_REQUEST [ "include_attachments" ]);
194 $since_id = ( int ) db_escape_string ( $this -> link
, $_REQUEST [ "since_id" ]);
195 $include_nested = sql_bool_to_bool ( $_REQUEST [ "include_nested" ]);
196 $sanitize_content = true ;
198 /* do not rely on params below */
200 $search = db_escape_string ( $this -> link
, $_REQUEST [ "search" ]);
201 $search_mode = db_escape_string ( $this -> link
, $_REQUEST [ "search_mode" ]);
203 $headlines = $this -> api_get_headlines ( $this -> link
, $feed_id , $limit , $offset ,
204 $filter , $is_cat , $show_excerpt , $show_content , $view_mode , false ,
205 $include_attachments , $since_id , $search , $search_mode ,
206 $include_nested , $sanitize_content );
208 print $this -> wrap ( self
:: STATUS_OK
, $headlines );
210 print $this -> wrap ( self
:: STATUS_ERR
, array ( "error" => 'INCORRECT_USAGE' ));
214 function updateArticle () {
215 $article_ids = array_filter ( explode ( "," , db_escape_string ( $this -> link
, $_REQUEST [ "article_ids" ])), is_numeric
);
216 $mode = ( int ) db_escape_string ( $this -> link
, $_REQUEST [ "mode" ]);
217 $data = db_escape_string ( $this -> link
, $_REQUEST [ "data" ]);
218 $field_raw = ( int ) db_escape_string ( $this -> link
, $_REQUEST [ "field" ]);
223 switch ( $field_raw ) {
226 $additional_fields = ",last_marked = NOW()" ;
229 $field = "published" ;
230 $additional_fields = ",last_published = NOW()" ;
234 $additional_fields = ",last_read = NOW()" ;
248 $set_to = "NOT $field " ;
252 if ( $field == "note" ) $set_to = "' $data '" ;
254 if ( $field && $set_to && count ( $article_ids ) > 0 ) {
256 $article_ids = join ( ", " , $article_ids );
258 $result = db_query ( $this -> link
, "UPDATE ttrss_user_entries SET $field = $set_to $additional_fields WHERE ref_id IN ( $article_ids ) AND owner_uid = " . $_SESSION [ "uid" ]);
260 $num_updated = db_affected_rows ( $this -> link
, $result );
262 if ( $num_updated > 0 && $field == "unread" ) {
263 $result = db_query ( $this -> link
, "SELECT DISTINCT feed_id FROM ttrss_user_entries
264 WHERE ref_id IN ( $article_ids )" );
266 while ( $line = db_fetch_assoc ( $result )) {
267 ccache_update ( $this -> link
, $line [ "feed_id" ], $_SESSION [ "uid" ]);
271 if ( $num_updated > 0 && $field == "published" ) {
272 if ( PUBSUBHUBBUB_HUB
) {
273 $rss_link = get_self_url_prefix () .
274 "/public.php?op=rss&id=-2&key=" .
275 get_feed_access_key ( $this -> link
, - 2 , false );
277 $p = new Publisher ( PUBSUBHUBBUB_HUB
);
278 $pubsub_result = $p -> publish_update ( $rss_link );
282 print $this -> wrap ( self
:: STATUS_OK
, array ( "status" => "OK" ,
283 "updated" => $num_updated ));
286 print $this -> wrap ( self
:: STATUS_ERR
, array ( "error" => 'INCORRECT_USAGE' ));
291 function getArticle () {
293 $article_id = join ( "," , array_filter ( explode ( "," , db_escape_string ( $this -> link
, $_REQUEST [ "article_id" ])), is_numeric
));
295 $query = "SELECT id,title,link,content,cached_content,feed_id,comments,int_id,
296 marked,unread,published,
297 " . SUBSTRING_FOR_DATE
. "(updated,1,16) as updated,
299 FROM ttrss_entries,ttrss_user_entries
300 WHERE id IN ( $article_id ) AND ref_id = id AND owner_uid = " .
303 $result = db_query ( $this -> link
, $query );
307 if ( db_num_rows ( $result ) != 0 ) {
309 while ( $line = db_fetch_assoc ( $result )) {
311 $attachments = get_article_enclosures ( $this -> link
, $line [ 'id' ]);
315 "title" => $line [ "title" ],
316 "link" => $line [ "link" ],
317 "labels" => get_article_labels ( $this -> link
, $line [ 'id' ]),
318 "unread" => sql_bool_to_bool ( $line [ "unread" ]),
319 "marked" => sql_bool_to_bool ( $line [ "marked" ]),
320 "published" => sql_bool_to_bool ( $line [ "published" ]),
321 "comments" => $line [ "comments" ],
322 "author" => $line [ "author" ],
323 "updated" => ( int ) strtotime ( $line [ "updated" ]),
324 "content" => $line [ "cached_content" ] != "" ?
$line [ "cached_content" ] : $line [ "content" ],
325 "feed_id" => $line [ "feed_id" ],
326 "attachments" => $attachments
329 array_push ( $articles , $article );
334 print $this -> wrap ( self
:: STATUS_OK
, $articles );
338 function getConfig () {
340 "icons_dir" => ICONS_DIR
,
341 "icons_url" => ICONS_URL
);
343 $config [ "daemon_is_running" ] = file_is_locked ( "update_daemon.lock" );
345 $result = db_query ( $this -> link
, "SELECT COUNT(*) AS cf FROM
346 ttrss_feeds WHERE owner_uid = " . $_SESSION [ "uid" ]);
348 $num_feeds = db_fetch_result ( $result , 0 , "cf" );
350 $config [ "num_feeds" ] = ( int ) $num_feeds ;
352 print $this -> wrap ( self
:: STATUS_OK
, $config );
355 function updateFeed () {
356 require_once "include/rssfuncs.php" ;
358 $feed_id = ( int ) db_escape_string ( $this -> link
, $_REQUEST [ "feed_id" ]);
360 update_rss_feed ( $this -> link
, $feed_id , true );
362 print $this -> wrap ( self
:: STATUS_OK
, array ( "status" => "OK" ));
365 function catchupFeed () {
366 $feed_id = db_escape_string ( $this -> link
, $_REQUEST [ "feed_id" ]);
367 $is_cat = db_escape_string ( $this -> link
, $_REQUEST [ "is_cat" ]);
369 catchup_feed ( $this -> link
, $feed_id , $is_cat );
371 print $this -> wrap ( self
:: STATUS_OK
, array ( "status" => "OK" ));
375 $pref_name = db_escape_string ( $this -> link
, $_REQUEST [ "pref_name" ]);
377 print $this -> wrap ( self
:: STATUS_OK
, array ( "value" => get_pref ( $this -> link
, $pref_name )));
380 function getLabels () {
381 //$article_ids = array_filter(explode(",", db_escape_string($this->link, $_REQUEST["article_ids"])), is_numeric);
383 $article_id = ( int ) $_REQUEST [ 'article_id' ];
387 $result = db_query ( $this -> link
, "SELECT id, caption, fg_color, bg_color
389 WHERE owner_uid = '" . $_SESSION [ 'uid' ]. "' ORDER BY caption" );
392 $article_labels = get_article_labels ( $this -> link
, $article_id );
394 $article_labels = array ();
396 while ( $line = db_fetch_assoc ( $result )) {
399 foreach ( $article_labels as $al ) {
400 if ( $al [ 0 ] == $line [ 'id' ]) {
406 array_push ( $rv , array (
407 "id" => ( int ) $line [ 'id' ],
408 "caption" => $line [ 'caption' ],
409 "fg_color" => $line [ 'fg_color' ],
410 "bg_color" => $line [ 'bg_color' ],
411 "checked" => $checked ));
414 print $this -> wrap ( self
:: STATUS_OK
, $rv );
417 function setArticleLabel () {
419 $article_ids = array_filter ( explode ( "," , db_escape_string ( $this -> link
, $_REQUEST [ "article_ids" ])), is_numeric
);
420 $label_id = ( int ) db_escape_string ( $this -> link
, $_REQUEST [ 'label_id' ]);
421 $assign = ( bool ) db_escape_string ( $this -> link
, $_REQUEST [ 'assign' ]) == "true" ;
423 $label = db_escape_string ( $this -> link
, label_find_caption ( $this -> link
,
424 $label_id , $_SESSION [ "uid" ]));
430 foreach ( $article_ids as $id ) {
433 label_add_article ( $this -> link
, $id , $label , $_SESSION [ "uid" ]);
435 label_remove_article ( $this -> link
, $id , $label , $_SESSION [ "uid" ]);
442 print $this -> wrap ( self
:: STATUS_OK
, array ( "status" => "OK" ,
443 "updated" => $num_updated ));
448 print $this -> wrap ( self
:: STATUS_ERR
, array ( "error" => 'UNKNOWN_METHOD' ));
451 function shareToPublished () {
452 $title = db_escape_string ( $this -> link
, strip_tags ( $_REQUEST [ "title" ]));
453 $url = db_escape_string ( $this -> link
, strip_tags ( $_REQUEST [ "url" ]));
454 $content = db_escape_string ( $this -> link
, strip_tags ( $_REQUEST [ "content" ]));
456 if ( Article
:: create_published_article ( $this -> link
, $title , $url , $content , "" , $_SESSION [ "uid" ])) {
457 print $this -> wrap ( self
:: STATUS_OK
, array ( "status" => 'OK' ));
459 print $this -> wrap ( self
:: STATUS_ERR
, array ( "error" => 'Publishing failed' ));
463 static function api_get_feeds ( $link , $cat_id , $unread_only , $limit , $offset , $include_nested = false ) {
469 if ( $cat_id == - 4 ||
$cat_id == - 2 ) {
470 $counters = getLabelCounters ( $link , true );
472 foreach ( array_values ( $counters ) as $cv ) {
474 $unread = $cv [ "counter" ];
476 if ( $unread ||
! $unread_only ) {
480 "title" => $cv [ "description" ],
481 "unread" => $cv [ "counter" ],
485 array_push ( $feeds , $row );
492 if ( $cat_id == - 4 ||
$cat_id == - 1 ) {
493 foreach ( array (- 1 , - 2 , - 3 , - 4 , - 6 , 0 ) as $i ) {
494 $unread = getFeedUnread ( $link , $i );
496 if ( $unread ||
! $unread_only ) {
497 $title = getFeedTitle ( $link , $i );
505 array_push ( $feeds , $row );
513 if ( $include_nested && $cat_id ) {
514 $result = db_query ( $link , "SELECT
515 id, title FROM ttrss_feed_categories
516 WHERE parent_cat = ' $cat_id ' AND owner_uid = " . $_SESSION [ "uid" ] .
517 " ORDER BY id, title" );
519 while ( $line = db_fetch_assoc ( $result )) {
520 $unread = getFeedUnread ( $link , $line [ "id" ], true ) +
521 getCategoryChildrenUnread ( $link , $line [ "id" ]);
523 if ( $unread ||
! $unread_only ) {
526 "title" => $line [ "title" ],
530 array_push ( $feeds , $row );
538 $limit_qpart = "LIMIT $limit OFFSET $offset " ;
543 if ( $cat_id == - 4 ||
$cat_id == - 3 ) {
544 $result = db_query ( $link , "SELECT
545 id, feed_url, cat_id, title, order_id, " .
546 SUBSTRING_FOR_DATE
. "(last_updated,1,19) AS last_updated
547 FROM ttrss_feeds WHERE owner_uid = " . $_SESSION [ "uid" ] .
548 " ORDER BY cat_id, title " . $limit_qpart );
552 $cat_qpart = "cat_id = ' $cat_id '" ;
554 $cat_qpart = "cat_id IS NULL" ;
556 $result = db_query ( $link , "SELECT
557 id, feed_url, cat_id, title, order_id, " .
558 SUBSTRING_FOR_DATE
. "(last_updated,1,19) AS last_updated
559 FROM ttrss_feeds WHERE
560 $cat_qpart AND owner_uid = " . $_SESSION [ "uid" ] .
561 " ORDER BY cat_id, title " . $limit_qpart );
564 while ( $line = db_fetch_assoc ( $result )) {
566 $unread = getFeedUnread ( $link , $line [ "id" ]);
568 $has_icon = feed_has_icon ( $line [ 'id' ]);
570 if ( $unread ||
! $unread_only ) {
573 "feed_url" => $line [ "feed_url" ],
574 "title" => $line [ "title" ],
575 "id" => ( int ) $line [ "id" ],
576 "unread" => ( int ) $unread ,
577 "has_icon" => $has_icon ,
578 "cat_id" => ( int ) $line [ "cat_id" ],
579 "last_updated" => ( int ) strtotime ( $line [ "last_updated" ]),
580 "order_id" => ( int ) $line [ "order_id" ],
583 array_push ( $feeds , $row );
590 static function api_get_headlines ( $link , $feed_id , $limit , $offset ,
591 $filter , $is_cat , $show_excerpt , $show_content , $view_mode , $order ,
592 $include_attachments , $since_id ,
593 $search = "" , $search_mode = "" ,
594 $include_nested = false , $sanitize_content = true ) {
596 $qfh_ret = queryFeedHeadlines ( $link , $feed_id , $limit ,
597 $view_mode , $is_cat , $search , $search_mode ,
598 $order , $offset , 0 , false , $since_id , $include_nested );
600 $result = $qfh_ret [ 0 ];
601 $feed_title = $qfh_ret [ 1 ];
603 $headlines = array ();
605 while ( $line = db_fetch_assoc ( $result )) {
606 $is_updated = ( $line [ "last_read" ] == "" &&
607 ( $line [ "unread" ] != "t" && $line [ "unread" ] != "1" ));
609 $tags = explode ( "," , $line [ "tag_cache" ]);
610 $labels = json_decode ( $line [ "label_cache" ], true );
612 //if (!$tags) $tags = get_article_tags($link, $line["id"]);
613 //if (!$labels) $labels = get_article_labels($link, $line["id"]);
615 $headline_row = array (
616 "id" => ( int ) $line [ "id" ],
617 "unread" => sql_bool_to_bool ( $line [ "unread" ]),
618 "marked" => sql_bool_to_bool ( $line [ "marked" ]),
619 "published" => sql_bool_to_bool ( $line [ "published" ]),
620 "updated" => ( int ) strtotime ( $line [ "updated" ]),
621 "is_updated" => $is_updated ,
622 "title" => $line [ "title" ],
623 "link" => $line [ "link" ],
624 "feed_id" => $line [ "feed_id" ],
628 if ( $include_attachments )
629 $headline_row [ 'attachments' ] = get_article_enclosures ( $link ,
633 $excerpt = truncate_string ( strip_tags ( $line [ "content_preview" ]), 100 );
634 $headline_row [ "excerpt" ] = $excerpt ;
639 if ( $line [ "cached_content" ] != "" ) {
640 $line [ "content_preview" ] =& $line [ "cached_content" ];
643 if ( $sanitize_content ) {
644 $headline_row [ "content" ] = sanitize ( $link ,
645 $line [ "content_preview" ],
646 sql_bool_to_bool ( $line [ 'hide_images' ]),
647 false , $line [ "site_url" ]);
649 $headline_row [ "content" ] = $line [ "content_preview" ];
653 // unify label output to ease parsing
654 if ( $labels [ "no-labels" ] == 1 ) $labels = array ();
656 $headline_row [ "labels" ] = $labels ;
658 $headline_row [ "feed_title" ] = $line [ "feed_title" ];
660 $headline_row [ "comments_count" ] = ( int ) $line [ "num_comments" ];
661 $headline_row [ "comments_link" ] = $line [ "comments" ];
663 $headline_row [ "always_display_attachments" ] = sql_bool_to_bool ( $line [ "always_display_enclosures" ]);
666 foreach ( $pluginhost -> get_hooks ( $pluginhost :: HOOK_RENDER_ARTICLE_API
) as $p ) {
667 $headline_row = $p -> hook_render_article_api ( $headline_row );
670 array_push ( $headlines , $headline_row );
676 function unsubscribeFeed () {
677 $feed_id = ( int ) db_escape_string ( $this -> link
, $_REQUEST [ "feed_id" ]);
679 $result = db_query ( $this -> link
, "SELECT id FROM ttrss_feeds WHERE
680 id = ' $feed_id ' AND owner_uid = " . $_SESSION [ "uid" ]);
682 if ( db_num_rows ( $result ) != 0 ) {
683 Pref_Feeds
:: remove_feed ( $this -> link
, $feed_id , $_SESSION [ "uid" ]);
684 print $this -> wrap ( self
:: STATUS_OK
, array ( "status" => "OK" ));
686 print $this -> wrap ( self
:: STATUS_ERR
, array ( "error" => "FEED_NOT_FOUND" ));
690 function subscribeToFeed () {
691 $feed_url = db_escape_string ( $this -> link
, $_REQUEST [ "feed_url" ]);
692 $category_id = ( int ) db_escape_string ( $this -> link
, $_REQUEST [ "category_id" ]);
693 $login = db_escape_string ( $this -> link
, $_REQUEST [ "login" ]);
694 $password = db_escape_string ( $this -> link
, $_REQUEST [ "password" ]);
697 $rc = subscribe_to_feed ( $this -> link
, $feed_url , $category_id ,
698 $login , $password , false );
700 print $this -> wrap ( self
:: STATUS_OK
, array ( "status" => $rc ));
702 print $this -> wrap ( self
:: STATUS_ERR
, array ( "error" => 'INCORRECT_USAGE' ));