]>
git.wh0rd.org - tt-rss.git/blob - include/functions2.php
2 function make_init_params () {
5 foreach ( array ( "ON_CATCHUP_SHOW_NEXT_FEED" , "HIDE_READ_FEEDS" ,
6 "ENABLE_FEED_CATS" , "FEEDS_SORT_BY_UNREAD" , "CONFIRM_FEED_CATCHUP" ,
7 "CDM_AUTO_CATCHUP" , "FRESH_ARTICLE_MAX_AGE" ,
8 "HIDE_READ_SHOWS_SPECIAL" , "COMBINED_DISPLAY_MODE" ) as $param ) {
10 $params [ strtolower ( $param )] = ( int ) get_pref ( $param );
13 $params [ "icons_url" ] = ICONS_URL
;
14 $params [ "cookie_lifetime" ] = SESSION_COOKIE_LIFETIME
;
15 $params [ "default_view_mode" ] = get_pref ( "_DEFAULT_VIEW_MODE" );
16 $params [ "default_view_limit" ] = ( int ) get_pref ( "_DEFAULT_VIEW_LIMIT" );
17 $params [ "default_view_order_by" ] = get_pref ( "_DEFAULT_VIEW_ORDER_BY" );
18 $params [ "bw_limit" ] = ( int ) $_SESSION [ "bw_limit" ];
19 $params [ "label_base_index" ] = ( int ) LABEL_BASE_INDEX
;
21 $theme = get_pref ( "USER_CSS_THEME" , false , false );
22 $params [ "theme" ] = theme_valid ( " $theme " ) ?
$theme : "" ;
24 $params [ "plugins" ] = implode ( ", " , PluginHost
:: getInstance ()-> get_plugin_names ());
26 $params [ "php_platform" ] = PHP_OS
;
27 $params [ "php_version" ] = PHP_VERSION
;
29 $params [ "sanity_checksum" ] = sha1 ( file_get_contents ( "include/sanity_check.php" ));
31 $result = db_query ( "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
32 ttrss_feeds WHERE owner_uid = " . $_SESSION [ "uid" ]);
34 $max_feed_id = db_fetch_result ( $result , 0 , "mid" );
35 $num_feeds = db_fetch_result ( $result , 0 , "nf" );
37 $params [ "max_feed_id" ] = ( int ) $max_feed_id ;
38 $params [ "num_feeds" ] = ( int ) $num_feeds ;
40 $params [ "hotkeys" ] = get_hotkeys_map ();
42 $params [ "csrf_token" ] = $_SESSION [ "csrf_token" ];
43 $params [ "widescreen" ] = ( int ) $_COOKIE [ "ttrss_widescreen" ];
45 $params [ 'simple_update' ] = defined ( 'SIMPLE_UPDATE_MODE' ) && SIMPLE_UPDATE_MODE
;
50 function get_hotkeys_info () {
52 __ ( "Navigation" ) => array (
53 "next_feed" => __ ( "Open next feed" ),
54 "prev_feed" => __ ( "Open previous feed" ),
55 "next_article" => __ ( "Open next article" ),
56 "prev_article" => __ ( "Open previous article" ),
57 "next_article_noscroll" => __ ( "Open next article (don't scroll long articles)" ),
58 "prev_article_noscroll" => __ ( "Open previous article (don't scroll long articles)" ),
59 "next_article_noexpand" => __ ( "Move to next article (don't expand or mark read)" ),
60 "prev_article_noexpand" => __ ( "Move to previous article (don't expand or mark read)" ),
61 "search_dialog" => __ ( "Show search dialog" )),
62 __ ( "Article" ) => array (
63 "toggle_mark" => __ ( "Toggle starred" ),
64 "toggle_publ" => __ ( "Toggle published" ),
65 "toggle_unread" => __ ( "Toggle unread" ),
66 "edit_tags" => __ ( "Edit tags" ),
67 "dismiss_selected" => __ ( "Dismiss selected" ),
68 "dismiss_read" => __ ( "Dismiss read" ),
69 "open_in_new_window" => __ ( "Open in new window" ),
70 "catchup_below" => __ ( "Mark below as read" ),
71 "catchup_above" => __ ( "Mark above as read" ),
72 "article_scroll_down" => __ ( "Scroll down" ),
73 "article_scroll_up" => __ ( "Scroll up" ),
74 "select_article_cursor" => __ ( "Select article under cursor" ),
75 "email_article" => __ ( "Email article" ),
76 "close_article" => __ ( "Close/collapse article" ),
77 "toggle_expand" => __ ( "Toggle article expansion (combined mode)" ),
78 "toggle_widescreen" => __ ( "Toggle widescreen mode" ),
79 "toggle_embed_original" => __ ( "Toggle embed original" )),
80 __ ( "Article selection" ) => array (
81 "select_all" => __ ( "Select all articles" ),
82 "select_unread" => __ ( "Select unread" ),
83 "select_marked" => __ ( "Select starred" ),
84 "select_published" => __ ( "Select published" ),
85 "select_invert" => __ ( "Invert selection" ),
86 "select_none" => __ ( "Deselect everything" )),
88 "feed_refresh" => __ ( "Refresh current feed" ),
89 "feed_unhide_read" => __ ( "Un/hide read feeds" ),
90 "feed_subscribe" => __ ( "Subscribe to feed" ),
91 "feed_edit" => __ ( "Edit feed" ),
92 "feed_catchup" => __ ( "Mark as read" ),
93 "feed_reverse" => __ ( "Reverse headlines" ),
94 "feed_debug_update" => __ ( "Debug feed update" ),
95 "feed_debug_viewfeed" => __ ( "Debug viewfeed()" ),
96 "catchup_all" => __ ( "Mark all feeds as read" ),
97 "cat_toggle_collapse" => __ ( "Un/collapse current category" ),
98 "toggle_combined_mode" => __ ( "Toggle combined mode" ),
99 "toggle_cdm_expanded" => __ ( "Toggle auto expand in combined mode" )),
100 __ ( "Go to" ) => array (
101 "goto_all" => __ ( "All articles" ),
102 "goto_fresh" => __ ( "Fresh" ),
103 "goto_marked" => __ ( "Starred" ),
104 "goto_published" => __ ( "Published" ),
105 "goto_tagcloud" => __ ( "Tag cloud" ),
106 "goto_prefs" => __ ( "Preferences" )),
107 __ ( "Other" ) => array (
108 "create_label" => __ ( "Create label" ),
109 "create_filter" => __ ( "Create filter" ),
110 "collapse_sidebar" => __ ( "Un/collapse sidebar" ),
111 "help_dialog" => __ ( "Show help dialog" ))
114 foreach ( PluginHost
:: getInstance ()-> get_hooks ( PluginHost
:: HOOK_HOTKEY_INFO
) as $plugin ) {
115 $hotkeys = $plugin -> hook_hotkey_info ( $hotkeys );
121 function get_hotkeys_map () {
123 // "navigation" => array(
126 "n" => "next_article" ,
127 "p" => "prev_article" ,
128 "(38)|up" => "prev_article" ,
129 "(40)|down" => "next_article" ,
130 // "^(38)|Ctrl-up" => "prev_article_noscroll",
131 // "^(40)|Ctrl-down" => "next_article_noscroll",
132 "(191)|/" => "search_dialog" ,
133 // "article" => array(
134 "s" => "toggle_mark" ,
135 "*s" => "toggle_publ" ,
136 "u" => "toggle_unread" ,
138 "*d" => "dismiss_selected" ,
139 "*x" => "dismiss_read" ,
140 "o" => "open_in_new_window" ,
141 "c p" => "catchup_below" ,
142 "c n" => "catchup_above" ,
143 "*n" => "article_scroll_down" ,
144 "*p" => "article_scroll_up" ,
145 "*(38)|Shift+up" => "article_scroll_up" ,
146 "*(40)|Shift+down" => "article_scroll_down" ,
147 "a *w" => "toggle_widescreen" ,
148 "a e" => "toggle_embed_original" ,
149 "e" => "email_article" ,
150 "a q" => "close_article" ,
151 // "article_selection" => array(
152 "a a" => "select_all" ,
153 "a u" => "select_unread" ,
154 "a *u" => "select_marked" ,
155 "a p" => "select_published" ,
156 "a i" => "select_invert" ,
157 "a n" => "select_none" ,
159 "f r" => "feed_refresh" ,
160 "f a" => "feed_unhide_read" ,
161 "f s" => "feed_subscribe" ,
162 "f e" => "feed_edit" ,
163 "f q" => "feed_catchup" ,
164 "f x" => "feed_reverse" ,
165 "f *d" => "feed_debug_update" ,
166 "f *g" => "feed_debug_viewfeed" ,
167 "f *c" => "toggle_combined_mode" ,
168 "f c" => "toggle_cdm_expanded" ,
169 "*q" => "catchup_all" ,
170 "x" => "cat_toggle_collapse" ,
173 "g f" => "goto_fresh" ,
174 "g s" => "goto_marked" ,
175 "g p" => "goto_published" ,
176 "g t" => "goto_tagcloud" ,
177 "g *p" => "goto_prefs" ,
179 "(9)|Tab" => "select_article_cursor" , // tab
180 "c l" => "create_label" ,
181 "c f" => "create_filter" ,
182 "c s" => "collapse_sidebar" ,
183 "^(191)|Ctrl+/" => "help_dialog" ,
186 if ( get_pref ( 'COMBINED_DISPLAY_MODE' )) {
187 $hotkeys [ "^(38)|Ctrl-up" ] = "prev_article_noscroll" ;
188 $hotkeys [ "^(40)|Ctrl-down" ] = "next_article_noscroll" ;
191 foreach ( PluginHost
:: getInstance ()-> get_hooks ( PluginHost
:: HOOK_HOTKEY_MAP
) as $plugin ) {
192 $hotkeys = $plugin -> hook_hotkey_map ( $hotkeys );
197 foreach ( array_keys ( $hotkeys ) as $hotkey ) {
198 $pair = explode ( " " , $hotkey , 2 );
200 if ( count ( $pair ) > 1 && ! in_array ( $pair [ 0 ], $prefixes )) {
201 array_push ( $prefixes , $pair [ 0 ]);
205 return array ( $prefixes , $hotkeys );
208 function check_for_update () {
209 if ( defined ( "GIT_VERSION_TIMESTAMP" )) {
210 $content = @fetch_file_contents
( "http://tt-rss.org/version.json" );
213 $content = json_decode ( $content , true );
215 if ( $content && isset ( $content [ "changeset" ])) {
216 if (( int ) GIT_VERSION_TIMESTAMP
< ( int ) $content [ "changeset" ][ "timestamp" ] &&
217 GIT_VERSION_HEAD
!= $content [ "changeset" ][ "id" ]) {
219 return $content [ "changeset" ][ "id" ];
228 function make_runtime_info () {
231 $result = db_query ( "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
232 ttrss_feeds WHERE owner_uid = " . $_SESSION [ "uid" ]);
234 $max_feed_id = db_fetch_result ( $result , 0 , "mid" );
235 $num_feeds = db_fetch_result ( $result , 0 , "nf" );
237 $data [ "max_feed_id" ] = ( int ) $max_feed_id ;
238 $data [ "num_feeds" ] = ( int ) $num_feeds ;
240 $data [ 'last_article_id' ] = getLastArticleId ();
241 $data [ 'cdm_expanded' ] = get_pref ( 'CDM_EXPANDED' );
243 $data [ 'dep_ts' ] = calculate_dep_timestamp ();
244 $data [ 'reload_on_ts_change' ] = ! defined ( '_NO_RELOAD_ON_TS_CHANGE' );
247 if ( CHECK_FOR_UPDATES
&& $_SESSION [ "last_version_check" ] +
86400 +
rand (- 1000 , 1000 ) < time ()) {
248 $update_result = @check_for_update
();
250 $data [ "update_result" ] = $update_result ;
252 $_SESSION [ "last_version_check" ] = time ();
255 if ( file_exists ( LOCK_DIRECTORY
. "/update_daemon.lock" )) {
257 $data [ 'daemon_is_running' ] = ( int ) file_is_locked ( "update_daemon.lock" );
259 if ( time () - $_SESSION [ "daemon_stamp_check" ] > 30 ) {
261 $stamp = ( int ) @file_get_contents
( LOCK_DIRECTORY
. "/update_daemon.stamp" );
264 $stamp_delta = time () - $stamp ;
266 if ( $stamp_delta > 1800 ) {
270 $_SESSION [ "daemon_stamp_check" ] = time ();
273 $data [ 'daemon_stamp_ok' ] = $stamp_check ;
275 $stamp_fmt = date ( "Y.m.d, G:i" , $stamp );
277 $data [ 'daemon_stamp' ] = $stamp_fmt ;
285 function search_to_sql ( $search , $search_language ) {
287 $keywords = str_getcsv ( $search , " " );
288 $query_keywords = array ();
289 $search_words = array ();
290 $search_query_leftover = array ();
292 if ( $search_language )
293 $search_language = db_escape_string ( mb_strtolower ( $search_language ));
295 $search_language = "english" ;
297 foreach ( $keywords as $k ) {
298 if ( strpos ( $k , "-" ) === 0 ) {
305 $commandpair = explode ( ":" , mb_strtolower ( $k ), 2 );
307 switch ( $commandpair [ 0 ]) {
309 if ( $commandpair [ 1 ]) {
310 array_push ( $query_keywords , "( $not (LOWER(ttrss_entries.title) LIKE '%" .
311 db_escape_string ( mb_strtolower ( $commandpair [ 1 ])). "%'))" );
313 array_push ( $query_keywords , "(UPPER(ttrss_entries.title) $not LIKE UPPER('% $k %')
314 OR UPPER(ttrss_entries.content) $not LIKE UPPER('% $k %'))" );
315 array_push ( $search_words , $k );
319 if ( $commandpair [ 1 ]) {
320 array_push ( $query_keywords , "( $not (LOWER(author) LIKE '%" .
321 db_escape_string ( mb_strtolower ( $commandpair [ 1 ])). "%'))" );
323 array_push ( $query_keywords , "(UPPER(ttrss_entries.title) $not LIKE UPPER('% $k %')
324 OR UPPER(ttrss_entries.content) $not LIKE UPPER('% $k %'))" );
325 array_push ( $search_words , $k );
329 if ( $commandpair [ 1 ]) {
330 if ( $commandpair [ 1 ] == "true" )
331 array_push ( $query_keywords , "( $not (note IS NOT NULL AND note != ''))" );
332 else if ( $commandpair [ 1 ] == "false" )
333 array_push ( $query_keywords , "( $not (note IS NULL OR note = ''))" );
335 array_push ( $query_keywords , "( $not (LOWER(note) LIKE '%" .
336 db_escape_string ( mb_strtolower ( $commandpair [ 1 ])). "%'))" );
338 array_push ( $query_keywords , "(UPPER(ttrss_entries.title) $not LIKE UPPER('% $k %')
339 OR UPPER(ttrss_entries.content) $not LIKE UPPER('% $k %'))" );
340 if (! $not ) array_push ( $search_words , $k );
345 if ( $commandpair [ 1 ]) {
346 if ( $commandpair [ 1 ] == "true" )
347 array_push ( $query_keywords , "( $not (marked = true))" );
349 array_push ( $query_keywords , "( $not (marked = false))" );
351 array_push ( $query_keywords , "(UPPER(ttrss_entries.title) $not LIKE UPPER('% $k %')
352 OR UPPER(ttrss_entries.content) $not LIKE UPPER('% $k %'))" );
353 if (! $not ) array_push ( $search_words , $k );
357 if ( $commandpair [ 1 ]) {
358 if ( $commandpair [ 1 ] == "true" )
359 array_push ( $query_keywords , "( $not (published = true))" );
361 array_push ( $query_keywords , "( $not (published = false))" );
364 array_push ( $query_keywords , "(UPPER(ttrss_entries.title) $not LIKE UPPER('% $k %')
365 OR UPPER(ttrss_entries.content) $not LIKE UPPER('% $k %'))" );
366 if (! $not ) array_push ( $search_words , $k );
370 if ( $commandpair [ 1 ]) {
371 if ( $commandpair [ 1 ] == "true" )
372 array_push ( $query_keywords , "( $not (unread = true))" );
374 array_push ( $query_keywords , "( $not (unread = false))" );
377 array_push ( $query_keywords , "(UPPER(ttrss_entries.title) $not LIKE UPPER('% $k %')
378 OR UPPER(ttrss_entries.content) $not LIKE UPPER('% $k %'))" );
379 if (! $not ) array_push ( $search_words , $k );
383 if ( strpos ( $k , "@" ) === 0 ) {
385 $user_tz_string = get_pref ( 'USER_TIMEZONE' , $_SESSION [ 'uid' ]);
386 $orig_ts = strtotime ( substr ( $k , 1 ));
387 $k = date ( "Y-m-d" , convert_timestamp ( $orig_ts , $user_tz_string , 'UTC' ));
389 //$k = date("Y-m-d", strtotime(substr($k, 1)));
391 array_push ( $query_keywords , "(" . SUBSTRING_FOR_DATE
. "(updated,1,LENGTH(' $k ')) $not = ' $k ')" );
394 if ( DB_TYPE
== "pgsql" ) {
395 $k = mb_strtolower ( $k );
396 array_push ( $search_query_leftover , $not ?
"! $k " : $k );
398 array_push ( $query_keywords , "(UPPER(ttrss_entries.title) $not LIKE UPPER('% $k %')
399 OR UPPER(ttrss_entries.content) $not LIKE UPPER('% $k %'))" );
402 if (! $not ) array_push ( $search_words , $k );
407 if ( count ( $search_query_leftover ) > 0 ) {
408 $search_query_leftover = db_escape_string ( implode ( " & " , $search_query_leftover ));
410 if ( DB_TYPE
== "pgsql" ) {
411 array_push ( $query_keywords ,
412 "(tsvector_combined @@ to_tsquery(' $search_language ', ' $search_query_leftover '))" );
417 $search_query_part = implode ( "AND" , $query_keywords );
419 return array ( $search_query_part , $search_words );
422 function getParentCategories ( $cat , $owner_uid ) {
425 $result = db_query ( "SELECT parent_cat FROM ttrss_feed_categories
426 WHERE id = ' $cat ' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid " );
428 while ( $line = db_fetch_assoc ( $result )) {
429 array_push ( $rv , $line [ "parent_cat" ]);
430 $rv = array_merge ( $rv , getParentCategories ( $line [ "parent_cat" ], $owner_uid ));
436 function getChildCategories ( $cat , $owner_uid ) {
439 $result = db_query ( "SELECT id FROM ttrss_feed_categories
440 WHERE parent_cat = ' $cat ' AND owner_uid = $owner_uid " );
442 while ( $line = db_fetch_assoc ( $result )) {
443 array_push ( $rv , $line [ "id" ]);
444 $rv = array_merge ( $rv , getChildCategories ( $line [ "id" ], $owner_uid ));
450 function queryFeedHeadlines ( $params ) {
452 $feed = $params [ "feed" ];
453 $limit = isset ( $params [ "limit" ]) ?
$params [ "limit" ] : 30 ;
454 $view_mode = $params [ "view_mode" ];
455 $cat_view = isset ( $params [ "cat_view" ]) ?
$params [ "cat_view" ] : false ;
456 $search = isset ( $params [ "search" ]) ?
$params [ "search" ] : false ;
457 $search_language = isset ( $params [ "search_language" ]) ?
$params [ "search_language" ] : "" ;
458 $override_order = isset ( $params [ "override_order" ]) ?
$params [ "override_order" ] : false ;
459 $offset = isset ( $params [ "offset" ]) ?
$params [ "offset" ] : 0 ;
460 $owner_uid = isset ( $params [ "owner_uid" ]) ?
$params [ "owner_uid" ] : $_SESSION [ "uid" ];
461 $since_id = isset ( $params [ "since_id" ]) ?
$params [ "since_id" ] : 0 ;
462 $include_children = isset ( $params [ "include_children" ]) ?
$params [ "include_children" ] : false ;
463 $ignore_vfeed_group = isset ( $params [ "ignore_vfeed_group" ]) ?
$params [ "ignore_vfeed_group" ] : false ;
464 $override_strategy = isset ( $params [ "override_strategy" ]) ?
$params [ "override_strategy" ] : false ;
465 $override_vfeed = isset ( $params [ "override_vfeed" ]) ?
$params [ "override_vfeed" ] : false ;
466 $start_ts = isset ( $params [ "start_ts" ]) ?
$params [ "start_ts" ] : false ;
467 $check_first_id = isset ( $params [ "check_first_id" ]) ?
$params [ "check_first_id" ] : false ;
468 $api_request = isset ( $params [ "api_request" ]) ?
$params [ "api_request" ] : false ;
470 $ext_tables_part = "" ;
471 $query_strategy_part = "" ;
473 $search_words = array ();
476 foreach ( PluginHost
:: getInstance ()-> get_hooks ( PluginHost
:: HOOK_SEARCH
) as $plugin ) {
477 list ( $search_query_part , $search_words ) = $plugin -> hook_search ( $search );
481 // fall back in case of no plugins
482 if (! $search_query_part ) {
483 list ( $search_query_part , $search_words ) = search_to_sql ( $search , $search_language );
485 $search_query_part .= " AND " ;
487 $search_query_part = "" ;
491 $since_id_part = "ttrss_entries.id > $since_id AND " ;
496 $view_query_part = "" ;
497 $disable_offsets = false ;
499 if ( $view_mode == "adaptive" ) {
501 $view_query_part = " " ;
502 } else if ( $feed != - 1 ) {
504 $unread = getFeedUnread ( $feed , $cat_view );
506 if ( $cat_view && $feed > 0 && $include_children )
507 $unread +
= getCategoryChildrenUnread ( $feed );
510 $view_query_part = " unread = true AND " ;
511 $disable_offsets = ! $api_request && get_pref ( "CDM_AUTO_CATCHUP" ) && get_pref ( "CDM_EXPANDED" );
516 if ( $view_mode == "marked" ) {
517 $view_query_part = " marked = true AND " ;
520 if ( $view_mode == "has_note" ) {
521 $view_query_part = " (note IS NOT NULL AND note != '') AND " ;
524 if ( $view_mode == "published" ) {
525 $view_query_part = " published = true AND " ;
528 if ( $view_mode == "unread" && $feed != - 6 ) {
529 $view_query_part = " unread = true AND " ;
530 $disable_offsets = ! $api_request && get_pref ( "CDM_AUTO_CATCHUP" ) && get_pref ( "CDM_EXPANDED" );
534 $limit_query_part = "LIMIT " . $limit ;
537 $allow_archived = false ;
539 $vfeed_query_part = "" ;
542 if (! is_numeric ( $feed )) {
543 $query_strategy_part = "true" ;
544 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
545 id = feed_id) as feed_title," ;
546 } else if ( $feed > 0 ) {
551 if ( $include_children ) {
553 $subcats = getChildCategories ( $feed , $owner_uid );
555 array_push ( $subcats , $feed );
556 $query_strategy_part = "cat_id IN (" .
557 implode ( "," , $subcats ). ")" ;
560 $query_strategy_part = "cat_id = ' $feed '" ;
564 $query_strategy_part = "cat_id IS NULL" ;
567 $vfeed_query_part = "ttrss_feeds.title AS feed_title," ;
570 $query_strategy_part = "feed_id = ' $feed '" ;
572 } else if ( $feed == 0 && ! $cat_view ) { // archive virtual feed
573 $query_strategy_part = "feed_id IS NULL" ;
574 $allow_archived = true ;
575 } else if ( $feed == 0 && $cat_view ) { // uncategorized
576 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL" ;
577 $vfeed_query_part = "ttrss_feeds.title AS feed_title," ;
578 } else if ( $feed == - 1 ) { // starred virtual feed
579 $query_strategy_part = "marked = true" ;
580 $vfeed_query_part = "ttrss_feeds.title AS feed_title," ;
581 $allow_archived = true ;
583 if (! $override_order ) {
584 $override_order = "last_marked DESC, date_entered DESC, updated DESC" ;
587 } else if ( $feed == - 2 ) { // published virtual feed OR labels category
590 $query_strategy_part = "published = true" ;
591 $vfeed_query_part = "ttrss_feeds.title AS feed_title," ;
592 $allow_archived = true ;
594 if (! $override_order ) {
595 $override_order = "last_published DESC, date_entered DESC, updated DESC" ;
599 $vfeed_query_part = "ttrss_feeds.title AS feed_title," ;
601 $ext_tables_part = "ttrss_labels2,ttrss_user_labels2," ;
603 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
604 ttrss_user_labels2.article_id = ref_id" ;
607 } else if ( $feed == - 6 ) { // recently read
608 $query_strategy_part = "unread = false AND last_read IS NOT NULL" ;
610 if ( DB_TYPE
== "pgsql" ) {
611 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '1 DAY' " ;
613 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL 1 DAY) " ;
616 $vfeed_query_part = "ttrss_feeds.title AS feed_title," ;
617 $allow_archived = true ;
618 $ignore_vfeed_group = true ;
620 if (! $override_order ) $override_order = "last_read DESC" ;
622 } else if ( $feed == - 3 ) { // fresh virtual feed
623 $query_strategy_part = "unread = true AND score >= 0" ;
625 $intl = get_pref ( "FRESH_ARTICLE_MAX_AGE" , $owner_uid );
627 if ( DB_TYPE
== "pgsql" ) {
628 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL ' $intl hour' " ;
630 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) " ;
633 $vfeed_query_part = "ttrss_feeds.title AS feed_title," ;
634 } else if ( $feed == - 4 ) { // all articles virtual feed
635 $allow_archived = true ;
636 $query_strategy_part = "true" ;
637 $vfeed_query_part = "ttrss_feeds.title AS feed_title," ;
638 } else if ( $feed <= LABEL_BASE_INDEX
) { // labels
639 $label_id = feed_to_label_id ( $feed );
641 $query_strategy_part = "label_id = ' $label_id ' AND
642 ttrss_labels2.id = ttrss_user_labels2.label_id AND
643 ttrss_user_labels2.article_id = ref_id" ;
645 $vfeed_query_part = "ttrss_feeds.title AS feed_title," ;
646 $ext_tables_part = "ttrss_labels2,ttrss_user_labels2," ;
647 $allow_archived = true ;
650 $query_strategy_part = "true" ;
653 $order_by = "score DESC, date_entered DESC, updated DESC" ;
655 if ( $override_order ) {
656 $order_by = $override_order ;
659 if ( $override_strategy ) {
660 $query_strategy_part = $override_strategy ;
663 if ( $override_vfeed ) {
664 $vfeed_query_part = $override_vfeed ;
670 $feed_title = T_sprintf ( "Search results: %s " , $search );
673 $feed_title = getCategoryTitle ( $feed );
675 if ( is_numeric ( $feed ) && $feed > 0 ) {
676 $result = db_query ( "SELECT title,site_url,last_error,last_updated
677 FROM ttrss_feeds WHERE id = ' $feed ' AND owner_uid = $owner_uid " );
679 $feed_title = db_fetch_result ( $result , 0 , "title" );
680 $feed_site_url = db_fetch_result ( $result , 0 , "site_url" );
681 $last_error = db_fetch_result ( $result , 0 , "last_error" );
682 $last_updated = db_fetch_result ( $result , 0 , "last_updated" );
684 $feed_title = getFeedTitle ( $feed );
690 $content_query_part = "content, " ;
692 if ( $limit_query_part ) {
693 $offset_query_part = "OFFSET $offset " ;
695 $offset_query_part = "" ;
698 if ( is_numeric ( $feed )) {
699 // proper override_order applied above
700 if ( $vfeed_query_part && ! $ignore_vfeed_group && get_pref ( 'VFEED_GROUP_BY_FEED' , $owner_uid )) {
701 if (! $override_order ) {
702 $order_by = "ttrss_feeds.title, $order_by " ;
704 $order_by = "ttrss_feeds.title, $override_order " ;
708 if (! $allow_archived ) {
709 $from_qpart = "${ext_tables_part}ttrss_entries LEFT JOIN ttrss_user_entries ON (ref_id = ttrss_entries.id),ttrss_feeds" ;
710 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND" ;
713 $from_qpart = "${ext_tables_part}ttrss_entries LEFT JOIN ttrss_user_entries ON (ref_id = ttrss_entries.id)
714 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)" ;
717 if ( $vfeed_query_part ) $vfeed_query_part .= "favicon_avg_color," ;
720 $start_ts_formatted = date ( "Y/m/d H:i:s" , strtotime ( $start_ts ));
721 $start_ts_query_part = "date_entered >= ' $start_ts_formatted ' AND" ;
723 $start_ts_query_part = "" ;
727 $first_id_query_strategy_part = $query_strategy_part ;
730 $first_id_query_strategy_part = "true" ;
732 if ( DB_TYPE
== "pgsql" ) {
733 $sanity_interval_qpart = "date_entered >= NOW() - INTERVAL '1 hour' AND" ;
735 $sanity_interval_qpart = "date_entered >= DATE_SUB(NOW(), INTERVAL 1 hour) AND" ;
738 if (! $search && ! $disable_offsets ) {
739 // if previous topmost article id changed that means our current pagination is no longer valid
740 $query = "SELECT DISTINCT
757 ttrss_user_entries.owner_uid = ' $owner_uid ' AND
761 $sanity_interval_qpart
762 $first_id_query_strategy_part ORDER BY $order_by LIMIT 1" ;
764 if ( $_REQUEST [ "debug" ]) {
768 $result = db_query ( $query );
769 if ( $result && db_num_rows ( $result ) > 0 ) {
770 $first_id = ( int ) db_fetch_result ( $result , 0 , "id" );
772 if ( $offset > 0 && $first_id && $check_first_id && $first_id != $check_first_id ) {
773 return array (- 1 , $feed_title , $feed_site_url , $last_error , $last_updated , $search_words , $first_id );
778 if ( $disable_offsets ) {
779 $offset_query_part = "" ;
782 $query = "SELECT DISTINCT
785 ttrss_entries.id,ttrss_entries.title,
789 always_display_enclosures,
798 unread,feed_id,marked,published,link,last_read,orig_feed_id,
799 last_marked, last_published,
807 ttrss_user_entries.owner_uid = ' $owner_uid ' AND
812 $query_strategy_part ORDER BY $order_by
813 $limit_query_part $offset_query_part " ;
815 if ( $_REQUEST [ "debug" ]) print $query ;
817 $result = db_query ( $query );
822 $query = "SELECT DISTINCT
826 ttrss_entries.id as id,
841 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images,
842 last_marked, last_published,
847 FROM ttrss_entries, ttrss_user_entries, ttrss_tags
849 ref_id = ttrss_entries.id AND
850 ttrss_user_entries.owner_uid = $owner_uid AND
851 post_int_id = int_id AND
852 tag_name = ' $feed ' AND
855 $query_strategy_part ORDER BY $order_by
856 $limit_query_part $offset_query_part " ;
858 if ( $_REQUEST [ "debug" ]) print $query ;
860 $result = db_query ( $query );
863 return array ( $result , $feed_title , $feed_site_url , $last_error , $last_updated , $search_words , $first_id );
867 function iframe_whitelisted ( $entry ) {
868 $whitelist = array ( "youtube.com" , "youtu.be" , "vimeo.com" );
870 @ $src = parse_url ( $entry -> getAttribute ( "src" ), PHP_URL_HOST
);
873 foreach ( $whitelist as $w ) {
874 if ( $src == $w ||
$src == "www. $w " )
882 function sanitize ( $str , $force_remove_images = false , $owner = false , $site_url = false , $highlight_words = false , $article_id = false ) {
883 if (! $owner ) $owner = $_SESSION [ "uid" ];
885 $res = trim ( $str ); if (! $res ) return '' ;
887 $charset_hack = '<head>
888 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
891 $res = trim ( $res ); if (! $res ) return '' ;
893 libxml_use_internal_errors ( true );
895 $doc = new DOMDocument ();
896 $doc -> loadHTML ( $charset_hack . $res );
897 $xpath = new DOMXPath ( $doc );
899 $entries = $xpath -> query ( '(//a[ @href ]|//img[ @src ])' );
901 foreach ( $entries as $entry ) {
905 if ( $entry -> hasAttribute ( 'href' )) {
906 $entry -> setAttribute ( 'href' ,
907 rewrite_relative_url ( $site_url , $entry -> getAttribute ( 'href' )));
909 $entry -> setAttribute ( 'rel' , 'noreferrer' );
912 if ( $entry -> hasAttribute ( 'src' )) {
913 $src = rewrite_relative_url ( $site_url , $entry -> getAttribute ( 'src' ));
915 $cached_filename = CACHE_DIR
. '/images/' . sha1 ( $src ) . '.png' ;
917 if ( file_exists ( $cached_filename )) {
918 $src = SELF_URL_PATH
. '/public.php?op=cached_image&hash=' . sha1 ( $src );
921 $entry -> setAttribute ( 'src' , $src );
924 if ( $entry -> nodeName
== 'img' ) {
925 if (( $owner && get_pref ( "STRIP_IMAGES" , $owner )) ||
926 $force_remove_images ||
$_SESSION [ "bw_limit" ]) {
928 $p = $doc -> createElement ( 'p' );
930 $a = $doc -> createElement ( 'a' );
931 $a -> setAttribute ( 'href' , $entry -> getAttribute ( 'src' ));
933 $a -> appendChild ( new DOMText ( $entry -> getAttribute ( 'src' )));
934 $a -> setAttribute ( 'target' , '_blank' );
938 $entry -> parentNode
-> replaceChild ( $p , $entry );
943 if ( strtolower ( $entry -> nodeName
) == "a" ) {
944 $entry -> setAttribute ( "target" , "_blank" );
948 $entries = $xpath -> query ( '//iframe' );
949 foreach ( $entries as $entry ) {
950 if (! iframe_whitelisted ( $entry )) {
951 $entry -> setAttribute ( 'sandbox' , 'allow-scripts' );
953 if ( $_SERVER [ 'HTTPS' ] == "on" ) {
954 $entry -> setAttribute ( "src" ,
955 str_replace ( "http://" , "https://" ,
956 $entry -> getAttribute ( "src" )));
961 $allowed_elements = array ( 'a' , 'address' , 'audio' , 'article' , 'aside' ,
962 'b' , 'bdi' , 'bdo' , 'big' , 'blockquote' , 'body' , 'br' ,
963 'caption' , 'cite' , 'center' , 'code' , 'col' , 'colgroup' ,
964 'data' , 'dd' , 'del' , 'details' , 'div' , 'dl' , 'font' ,
965 'dt' , 'em' , 'footer' , 'figure' , 'figcaption' ,
966 'h1' , 'h2' , 'h3' , 'h4' , 'h5' , 'h6' , 'header' , 'html' , 'i' ,
967 'img' , 'ins' , 'kbd' , 'li' , 'main' , 'mark' , 'nav' , 'noscript' ,
968 'ol' , 'p' , 'pre' , 'q' , 'ruby' , 'rp' , 'rt' , 's' , 'samp' , 'section' ,
969 'small' , 'source' , 'span' , 'strike' , 'strong' , 'sub' , 'summary' ,
970 'sup' , 'table' , 'tbody' , 'td' , 'tfoot' , 'th' , 'thead' , 'time' ,
971 'tr' , 'track' , 'tt' , 'u' , 'ul' , 'var' , 'wbr' , 'video' );
973 if ( $_SESSION [ 'hasSandbox' ]) $allowed_elements [] = 'iframe' ;
975 $disallowed_attributes = array ( 'id' , 'style' , 'class' );
977 foreach ( PluginHost
:: getInstance ()-> get_hooks ( PluginHost
:: HOOK_SANITIZE
) as $plugin ) {
978 $retval = $plugin -> hook_sanitize ( $doc , $site_url , $allowed_elements , $disallowed_attributes , $article_id );
979 if ( is_array ( $retval )) {
981 $allowed_elements = $retval [ 1 ];
982 $disallowed_attributes = $retval [ 2 ];
988 $doc -> removeChild ( $doc -> firstChild
); //remove doctype
989 $doc = strip_harmful_tags ( $doc , $allowed_elements , $disallowed_attributes );
991 if ( $highlight_words ) {
992 foreach ( $highlight_words as $word ) {
994 // http://stackoverflow.com/questions/4081372/highlight-keywords-in-a-paragraph
996 $elements = $xpath -> query ( "//*/text()" );
998 foreach ( $elements as $child ) {
1000 $fragment = $doc -> createDocumentFragment ();
1001 $text = $child -> textContent
;
1003 while (( $pos = mb_stripos ( $text , $word )) !== false ) {
1004 $fragment -> appendChild ( new DomText ( mb_substr ( $text , 0 , $pos )));
1005 $word = mb_substr ( $text , $pos , mb_strlen ( $word ));
1006 $highlight = $doc -> createElement ( 'span' );
1007 $highlight -> appendChild ( new DomText ( $word ));
1008 $highlight -> setAttribute ( 'class' , 'highlight' );
1009 $fragment -> appendChild ( $highlight );
1010 $text = mb_substr ( $text , $pos +
mb_strlen ( $word ));
1013 if (! empty ( $text )) $fragment -> appendChild ( new DomText ( $text ));
1015 $child -> parentNode
-> replaceChild ( $fragment , $child );
1020 $res = $doc -> saveHTML ();
1025 function strip_harmful_tags ( $doc , $allowed_elements , $disallowed_attributes ) {
1026 $xpath = new DOMXPath ( $doc );
1027 $entries = $xpath -> query ( '//*' );
1029 foreach ( $entries as $entry ) {
1030 if (! in_array ( $entry -> nodeName
, $allowed_elements )) {
1031 $entry -> parentNode
-> removeChild ( $entry );
1034 if ( $entry -> hasAttributes ()) {
1035 $attrs_to_remove = array ();
1037 foreach ( $entry -> attributes
as $attr ) {
1039 if ( strpos ( $attr -> nodeName
, 'on' ) === 0 ) {
1040 array_push ( $attrs_to_remove , $attr );
1043 if ( in_array ( $attr -> nodeName
, $disallowed_attributes )) {
1044 array_push ( $attrs_to_remove , $attr );
1048 foreach ( $attrs_to_remove as $attr ) {
1049 $entry -> removeAttributeNode ( $attr );
1057 function catchupArticlesById ( $ids , $cmode , $owner_uid = false ) {
1059 if (! $owner_uid ) $owner_uid = $_SESSION [ "uid" ];
1060 if ( count ( $ids ) == 0 ) return ;
1064 foreach ( $ids as $id ) {
1065 array_push ( $tmp_ids , "ref_id = ' $id '" );
1068 $ids_qpart = join ( " OR " , $tmp_ids );
1071 db_query ( "UPDATE ttrss_user_entries SET
1072 unread = false,last_read = NOW()
1073 WHERE ( $ids_qpart ) AND owner_uid = $owner_uid " );
1074 } else if ( $cmode == 1 ) {
1075 db_query ( "UPDATE ttrss_user_entries SET
1077 WHERE ( $ids_qpart ) AND owner_uid = $owner_uid " );
1079 db_query ( "UPDATE ttrss_user_entries SET
1080 unread = NOT unread,last_read = NOW()
1081 WHERE ( $ids_qpart ) AND owner_uid = $owner_uid " );
1086 $result = db_query ( "SELECT DISTINCT feed_id FROM ttrss_user_entries
1087 WHERE ( $ids_qpart ) AND owner_uid = $owner_uid " );
1089 while ( $line = db_fetch_assoc ( $result )) {
1090 ccache_update ( $line [ "feed_id" ], $owner_uid );
1094 function get_article_tags ( $id , $owner_uid = 0 , $tag_cache = false ) {
1096 $a_id = db_escape_string ( $id );
1098 if (! $owner_uid ) $owner_uid = $_SESSION [ "uid" ];
1100 $query = "SELECT DISTINCT tag_name,
1101 owner_uid as owner FROM
1102 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
1103 ref_id = ' $a_id ' AND owner_uid = ' $owner_uid ' LIMIT 1) ORDER BY tag_name" ;
1107 /* check cache first */
1109 if ( $tag_cache === false ) {
1110 $result = db_query ( "SELECT tag_cache FROM ttrss_user_entries
1111 WHERE ref_id = ' $id ' AND owner_uid = $owner_uid " );
1113 $tag_cache = db_fetch_result ( $result , 0 , "tag_cache" );
1117 $tags = explode ( "," , $tag_cache );
1120 /* do it the hard way */
1122 $tmp_result = db_query ( $query );
1124 while ( $tmp_line = db_fetch_assoc ( $tmp_result )) {
1125 array_push ( $tags , $tmp_line [ "tag_name" ]);
1128 /* update the cache */
1130 $tags_str = db_escape_string ( join ( "," , $tags ));
1132 db_query ( "UPDATE ttrss_user_entries
1133 SET tag_cache = ' $tags_str ' WHERE ref_id = ' $id '
1134 AND owner_uid = $owner_uid " );
1140 function trim_array ( $array ) {
1142 array_walk ( $tmp , 'trim' );
1146 function tag_is_valid ( $tag ) {
1147 if ( $tag == '' ) return false ;
1148 if ( preg_match ( "/^[0-9]*$/" , $tag )) return false ;
1149 if ( mb_strlen ( $tag ) > 250 ) return false ;
1151 if (! $tag ) return false ;
1156 function render_login_form () {
1157 header ( 'Cache-Control: public' );
1159 require_once "login_form.php" ;
1163 function format_warning ( $msg , $id = "" ) {
1164 return "<div class= \" alert \" id= \" $id\" > $msg </div>" ;
1167 function format_notice ( $msg , $id = "" ) {
1168 return "<div class= \" alert alert-info \" id= \" $id\" > $msg </div>" ;
1171 function format_error ( $msg , $id = "" ) {
1172 return "<div class= \" alert alert-danger \" id= \" $id\" > $msg </div>" ;
1175 function print_notice ( $msg ) {
1176 return print format_notice ( $msg );
1179 function print_warning ( $msg ) {
1180 return print format_warning ( $msg );
1183 function print_error ( $msg ) {
1184 return print format_error ( $msg );
1188 function T_sprintf () {
1189 $args = func_get_args ();
1190 return vsprintf ( __ ( array_shift ( $args )), $args );
1193 function format_inline_player ( $url , $ctype ) {
1197 $url = htmlspecialchars ( $url );
1199 if ( strpos ( $ctype , "audio/" ) === 0 ) {
1201 if ( $_SESSION [ "hasAudio" ] && ( strpos ( $ctype , "ogg" ) !== false ||
1202 $_SESSION [ "hasMp3" ])) {
1204 $entry .= "<audio preload= \" none \" controls>
1205 <source type= \" $ctype\" src= \" $url\" />
1210 $entry .= "<object type= \" application/x-shockwave-flash \"
1211 data= \" lib/button/musicplayer.swf?song_url= $url\"
1212 width= \" 17 \" height= \" 17 \" style='float : left; margin-right : 5px;'>
1213 <param name= \" movie \"
1214 value= \" lib/button/musicplayer.swf?song_url= $url\" />
1218 if ( $entry ) $entry .= " <a target= \" _blank \"
1219 href= \" $url\" >" . basename ( $url ) . "</a>" ;
1227 /* $filename = substr($url, strrpos($url, "/")+1);
1229 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
1230 $filename . " (" . $ctype . ")" . "</a>"; */
1234 function format_article ( $id , $mark_as_read = true , $zoom_mode = false , $owner_uid = false ) {
1235 if (! $owner_uid ) $owner_uid = $_SESSION [ "uid" ];
1241 /* we can figure out feed_id from article id anyway, why do we
1242 * pass feed_id here? let's ignore the argument :(*/
1244 $result = db_query ( "SELECT feed_id FROM ttrss_user_entries
1245 WHERE ref_id = ' $id '" );
1247 $feed_id = ( int ) db_fetch_result ( $result , 0 , "feed_id" );
1249 $rv [ 'feed_id' ] = $feed_id ;
1251 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
1253 if ( $mark_as_read ) {
1254 $result = db_query ( "UPDATE ttrss_user_entries
1255 SET unread = false,last_read = NOW()
1256 WHERE ref_id = ' $id ' AND owner_uid = $owner_uid " );
1258 ccache_update ( $feed_id , $owner_uid );
1261 $result = db_query ( "SELECT id,title,link,content,feed_id,comments,int_id,lang,
1262 " . SUBSTRING_FOR_DATE
. "(updated,1,16) as updated,
1263 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
1264 (SELECT title FROM ttrss_feeds WHERE id = feed_id) as feed_title,
1265 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
1266 (SELECT always_display_enclosures FROM ttrss_feeds WHERE id = feed_id) as always_display_enclosures,
1272 FROM ttrss_entries,ttrss_user_entries
1273 WHERE id = ' $id ' AND ref_id = id AND owner_uid = $owner_uid " );
1277 $line = db_fetch_assoc ( $result );
1279 $line [ "tags" ] = get_article_tags ( $id , $owner_uid , $line [ "tag_cache" ]);
1280 unset ( $line [ "tag_cache" ]);
1282 $line [ "content" ] = sanitize ( $line [ "content" ],
1283 sql_bool_to_bool ( $line [ 'hide_images' ]),
1284 $owner_uid , $line [ "site_url" ], false , $line [ "id" ]);
1286 foreach ( PluginHost
:: getInstance ()-> get_hooks ( PluginHost
:: HOOK_RENDER_ARTICLE
) as $p ) {
1287 $line = $p -> hook_render_article ( $line );
1290 $num_comments = $line [ "num_comments" ];
1291 $entry_comments = "" ;
1293 if ( $num_comments > 0 ) {
1294 if ( $line [ "comments" ]) {
1295 $comments_url = htmlspecialchars ( $line [ "comments" ]);
1297 $comments_url = htmlspecialchars ( $line [ "link" ]);
1299 $entry_comments = "<a class= \" postComments \"
1300 target='_blank' href= \" $comments_url\" > $num_comments " .
1301 _ngettext ( "comment" , "comments" , $num_comments ). "</a>" ;
1304 if ( $line [ "comments" ] && $line [ "link" ] != $line [ "comments" ]) {
1305 $entry_comments = "<a class= \" postComments \" target='_blank' href= \" " . htmlspecialchars ( $line [ "comments" ]). " \" >" . __ ( "comments" ). "</a>" ;
1310 header ( "Content-Type: text/html" );
1311 $rv [ 'content' ] .= "<html><head>
1312 <meta http-equiv= \" Content-Type \" content= \" text/html; charset=utf-8 \" />
1313 <title>Tiny Tiny RSS - " . $line [ "title" ]. "</title>" .
1314 stylesheet_tag ( "css/tt-rss.css" ).
1315 stylesheet_tag ( "css/zoom.css" ).
1316 stylesheet_tag ( "css/dijit.css" ). "
1318 <link rel= \" shortcut icon \" type= \" image/png \" href= \" images/favicon.png \" >
1319 <link rel= \" icon \" type= \" image/png \" sizes= \" 72x72 \" href= \" images/favicon-72px.png \" >
1321 </head><body id= \" ttrssZoom \" >" ;
1324 $rv [ 'content' ] .= "<div class= \" postReply \" id= \" POST- $id\" >" ;
1326 $rv [ 'content' ] .= "<div class= \" postHeader \" id= \" POSTHDR- $id\" >" ;
1328 $entry_author = $line [ "author" ];
1330 if ( $entry_author ) {
1331 $entry_author = __ ( " - " ) . $entry_author ;
1334 $parsed_updated = make_local_datetime ( $line [ "updated" ], true ,
1338 $rv [ 'content' ] .= "<div class= \" postDate \" > $parsed_updated </div>" ;
1340 if ( $line [ "link" ]) {
1341 $rv [ 'content' ] .= "<div class='postTitle'><a target='_blank'
1342 title= \" " . htmlspecialchars ( $line [ 'title' ]). " \"
1344 htmlspecialchars ( $line [ "link" ]) . " \" >" .
1345 $line [ "title" ] . "</a>" .
1346 "<span class='author'> $entry_author </span></div>" ;
1348 $rv [ 'content' ] .= "<div class='postTitle'>" . $line [ "title" ] . " $entry_author </div>" ;
1352 $feed_title = "<a href= \" " . htmlspecialchars ( $line [ "site_url" ]).
1353 " \" target= \" _blank \" >" .
1354 htmlspecialchars ( $line [ "feed_title" ]). "</a>" ;
1356 $rv [ 'content' ] .= "<div class= \" postFeedTitle \" > $feed_title </div>" ;
1358 $rv [ 'content' ] .= "<div class= \" postDate \" > $parsed_updated </div>" ;
1361 $tags_str = format_tags_string ( $line [ "tags" ], $id );
1362 $tags_str_full = join ( ", " , $line [ "tags" ]);
1364 if (! $tags_str_full ) $tags_str_full = __ ( "no tags" );
1366 if (! $entry_comments ) $entry_comments = " " ; # placeholder
1368 $rv [ 'content' ] .= "<div class='postTags' style='float : right'>
1369 <img src='images/tag.png'
1370 class='tagsPic' alt='Tags' title='Tags'> " ;
1373 $rv [ 'content' ] .= "<span id= \" ATSTR- $id\" > $tags_str </span>
1374 <a title= \" " . __ ( 'Edit tags for this article' ). " \"
1375 href= \" # \" onclick= \" editArticleTags( $id , $feed_id ) \" >(+)</a>" ;
1377 $rv [ 'content' ] .= "<div dojoType= \" dijit.Tooltip \"
1378 id= \" ATSTRTIP- $id\" connectId= \" ATSTR- $id\"
1379 position= \" below \" > $tags_str_full </div>" ;
1381 foreach ( PluginHost
:: getInstance ()-> get_hooks ( PluginHost
:: HOOK_ARTICLE_BUTTON
) as $p ) {
1382 $rv [ 'content' ] .= $p -> hook_article_button ( $line );
1386 $tags_str = strip_tags ( $tags_str );
1387 $rv [ 'content' ] .= "<span id= \" ATSTR- $id\" > $tags_str </span>" ;
1389 $rv [ 'content' ] .= "</div>" ;
1390 $rv [ 'content' ] .= "<div clear='both'>" ;
1392 foreach ( PluginHost
:: getInstance ()-> get_hooks ( PluginHost
:: HOOK_ARTICLE_LEFT_BUTTON
) as $p ) {
1393 $rv [ 'content' ] .= $p -> hook_article_left_button ( $line );
1396 $rv [ 'content' ] .= " $entry_comments </div>" ;
1398 if ( $line [ "orig_feed_id" ]) {
1400 $tmp_result = db_query ( "SELECT * FROM ttrss_archived_feeds
1401 WHERE id = " . $line [ "orig_feed_id" ]);
1403 if ( db_num_rows ( $tmp_result ) != 0 ) {
1405 $rv [ 'content' ] .= "<div clear='both'>" ;
1406 $rv [ 'content' ] .= __ ( "Originally from:" );
1408 $rv [ 'content' ] .= " " ;
1410 $tmp_line = db_fetch_assoc ( $tmp_result );
1412 $rv [ 'content' ] .= "<a target='_blank'
1413 href=' " . htmlspecialchars ( $tmp_line [ 'site_url' ]) . "'>" .
1414 $tmp_line [ 'title' ] . "</a>" ;
1416 $rv [ 'content' ] .= " " ;
1418 $rv [ 'content' ] .= "<a target='_blank' href='" . htmlspecialchars ( $tmp_line [ 'feed_url' ]) . "'>" ;
1419 $rv [ 'content' ] .= "<img title='" . __ ( 'Feed URL' ). "' class='tinyFeedIcon' src='images/pub_set.png'></a>" ;
1421 $rv [ 'content' ] .= "</div>" ;
1425 $rv [ 'content' ] .= "</div>" ;
1427 $rv [ 'content' ] .= "<div id= \" POSTNOTE- $id\" >" ;
1428 if ( $line [ 'note' ]) {
1429 $rv [ 'content' ] .= format_article_note ( $id , $line [ 'note' ], ! $zoom_mode );
1431 $rv [ 'content' ] .= "</div>" ;
1433 if (! $line [ 'lang' ]) $line [ 'lang' ] = 'en' ;
1435 $rv [ 'content' ] .= "<div class= \" postContent \" lang= \" " . $line [ 'lang' ]. " \" >" ;
1437 $rv [ 'content' ] .= $line [ "content" ];
1440 $rv [ 'content' ] .= format_article_enclosures ( $id ,
1441 sql_bool_to_bool ( $line [ "always_display_enclosures" ]),
1443 sql_bool_to_bool ( $line [ "hide_images" ]));
1446 $rv [ 'content' ] .= "</div>" ;
1448 $rv [ 'content' ] .= "</div>" ;
1454 <div class='footer'>
1455 <button onclick= \" return window.close() \" >" .
1456 __ ( "Close this window" ). "</button></div>" ;
1457 $rv [ 'content' ] .= "</body></html>" ;
1464 function print_checkpoint ( $n , $s ) {
1465 $ts = microtime ( true );
1466 echo sprintf ( "<!-- CP[ $n ] %.4f seconds --> \n " , $ts - $s );
1470 function sanitize_tag ( $tag ) {
1473 $tag = mb_strtolower ( $tag , 'utf-8' );
1475 $tag = preg_replace ( '/[, \'\" \+\>\<]/' , "" , $tag );
1477 if ( DB_TYPE
== "mysql" ) {
1478 $tag = preg_replace ( '/[\x{10000}-\x{10FFFF}]/u' , " \xEF\xBF\xBD " , $tag );
1484 function get_self_url_prefix () {
1485 if ( strrpos ( SELF_URL_PATH
, "/" ) === strlen ( SELF_URL_PATH
)- 1 ) {
1486 return substr ( SELF_URL_PATH
, 0 , strlen ( SELF_URL_PATH
)- 1 );
1488 return SELF_URL_PATH
;
1493 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
1495 * @return string The Mozilla Firefox feed adding URL.
1497 function add_feed_url () {
1498 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
1500 $url_path = get_self_url_prefix () .
1501 "/public.php?op=subscribe&feed_url= %s " ;
1503 } // function add_feed_url
1505 function encrypt_password ( $pass , $salt = '' , $mode2 = false ) {
1506 if ( $salt && $mode2 ) {
1507 return "MODE2:" . hash ( 'sha256' , $salt . $pass );
1509 return "SHA1X:" . sha1 ( " $salt : $pass " );
1511 return "SHA1:" . sha1 ( $pass );
1513 } // function encrypt_password
1515 function load_filters ( $feed_id , $owner_uid , $action_id = false ) {
1518 $cat_id = ( int ) getFeedCategory ( $feed_id );
1521 $null_cat_qpart = "cat_id IS NULL OR" ;
1523 $null_cat_qpart = "" ;
1525 $result = db_query ( "SELECT * FROM ttrss_filters2 WHERE
1526 owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title" );
1528 $check_cats = join ( "," , array_merge (
1529 getParentCategories ( $cat_id , $owner_uid ),
1532 while ( $line = db_fetch_assoc ( $result )) {
1533 $filter_id = $line [ "id" ];
1535 $result2 = db_query ( "SELECT
1536 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
1537 FROM ttrss_filters2_rules AS r,
1538 ttrss_filter_types AS t
1540 ( $null_cat_qpart (cat_id IS NULL AND cat_filter = false) OR cat_id IN ( $check_cats )) AND
1541 (feed_id IS NULL OR feed_id = ' $feed_id ') AND
1542 filter_type = t.id AND filter_id = ' $filter_id '" );
1547 while ( $rule_line = db_fetch_assoc ( $result2 )) {
1548 # print_r($rule_line);
1551 $rule [ "reg_exp" ] = $rule_line [ "reg_exp" ];
1552 $rule [ "type" ] = $rule_line [ "type_name" ];
1553 $rule [ "inverse" ] = sql_bool_to_bool ( $rule_line [ "inverse" ]);
1555 array_push ( $rules , $rule );
1558 $result2 = db_query ( "SELECT a.action_param,t.name AS type_name
1559 FROM ttrss_filters2_actions AS a,
1560 ttrss_filter_actions AS t
1562 action_id = t.id AND filter_id = ' $filter_id '" );
1564 while ( $action_line = db_fetch_assoc ( $result2 )) {
1565 # print_r($action_line);
1568 $action [ "type" ] = $action_line [ "type_name" ];
1569 $action [ "param" ] = $action_line [ "action_param" ];
1571 array_push ( $actions , $action );
1576 $filter [ "match_any_rule" ] = sql_bool_to_bool ( $line [ "match_any_rule" ]);
1577 $filter [ "inverse" ] = sql_bool_to_bool ( $line [ "inverse" ]);
1578 $filter [ "rules" ] = $rules ;
1579 $filter [ "actions" ] = $actions ;
1581 if ( count ( $rules ) > 0 && count ( $actions ) > 0 ) {
1582 array_push ( $filters , $filter );
1589 function get_score_pic ( $score ) {
1591 return "score_high.png" ;
1592 } else if ( $score > 0 ) {
1593 return "score_half_high.png" ;
1594 } else if ( $score < - 100 ) {
1595 return "score_low.png" ;
1596 } else if ( $score < 0 ) {
1597 return "score_half_low.png" ;
1599 return "score_neutral.png" ;
1603 function feed_has_icon ( $id ) {
1604 return is_file ( ICONS_DIR
. "/ $id .ico" ) && filesize ( ICONS_DIR
. "/ $id .ico" ) > 0 ;
1607 function init_plugins () {
1608 PluginHost
:: getInstance ()-> load ( PLUGINS
, PluginHost
:: KIND_ALL
);
1613 function format_tags_string ( $tags , $id ) {
1614 if (! is_array ( $tags ) ||
count ( $tags ) == 0 ) {
1615 return __ ( "no tags" );
1617 $maxtags = min ( 5 , count ( $tags ));
1620 for ( $i = 0 ; $i < $maxtags ; $i ++
) {
1621 $tags_str .= "<a class= \" tag \" href= \" # \" onclick= \" viewfeed('" . $tags [ $i ]. "') \" >" . $tags [ $i ] . "</a>, " ;
1624 $tags_str = mb_substr ( $tags_str , 0 , mb_strlen ( $tags_str )- 2 );
1626 if ( count ( $tags ) > $maxtags )
1627 $tags_str .= ", …" ;
1633 function format_article_labels ( $labels , $id ) {
1635 if (! is_array ( $labels )) return '' ;
1639 foreach ( $labels as $l ) {
1640 $labels_str .= sprintf ( "<span class='hlLabelRef'
1641 style='color : %s ; background-color : %s '> %s </span>" ,
1642 $l [ 2 ], $l [ 3 ], $l [ 1 ]);
1649 function format_article_note ( $id , $note , $allow_edit = true ) {
1651 $str = "<div class='articleNote' onclick= \" editArticleNote( $id ) \" >
1652 <div class='noteEdit' onclick= \" editArticleNote( $id ) \" >" .
1653 ( $allow_edit ?
__ ( '(edit note)' ) : "" ). "</div> $note </div>" ;
1659 function get_feed_category ( $feed_cat , $parent_cat_id = false ) {
1660 if ( $parent_cat_id ) {
1661 $parent_qpart = "parent_cat = ' $parent_cat_id '" ;
1662 $parent_insert = "' $parent_cat_id '" ;
1664 $parent_qpart = "parent_cat IS NULL" ;
1665 $parent_insert = "NULL" ;
1669 "SELECT id FROM ttrss_feed_categories
1670 WHERE $parent_qpart AND title = ' $feed_cat ' AND owner_uid = " . $_SESSION [ "uid" ]);
1672 if ( db_num_rows ( $result ) == 0 ) {
1675 return db_fetch_result ( $result , 0 , "id" );
1679 function add_feed_category ( $feed_cat , $parent_cat_id = false ) {
1681 if (! $feed_cat ) return false ;
1685 if ( $parent_cat_id ) {
1686 $parent_qpart = "parent_cat = ' $parent_cat_id '" ;
1687 $parent_insert = "' $parent_cat_id '" ;
1689 $parent_qpart = "parent_cat IS NULL" ;
1690 $parent_insert = "NULL" ;
1693 $feed_cat = mb_substr ( $feed_cat , 0 , 250 );
1696 "SELECT id FROM ttrss_feed_categories
1697 WHERE $parent_qpart AND title = ' $feed_cat ' AND owner_uid = " . $_SESSION [ "uid" ]);
1699 if ( db_num_rows ( $result ) == 0 ) {
1702 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
1703 VALUES ('" . $_SESSION [ "uid" ]. "', ' $feed_cat ', $parent_insert )" );
1713 function getArticleFeed ( $id ) {
1714 $result = db_query ( "SELECT feed_id FROM ttrss_user_entries
1715 WHERE ref_id = ' $id ' AND owner_uid = " . $_SESSION [ "uid" ]);
1717 if ( db_num_rows ( $result ) != 0 ) {
1718 return db_fetch_result ( $result , 0 , "feed_id" );
1725 * Fixes incomplete URLs by prepending "http://".
1726 * Also replaces feed:// with http://, and
1727 * prepends a trailing slash if the url is a domain name only.
1729 * @param string $url Possibly incomplete URL
1731 * @return string Fixed URL.
1733 function fix_url ( $url ) {
1735 // support schema-less urls
1736 if ( strpos ( $url , '//' ) === 0 ) {
1737 $url = 'https:' . $url ;
1740 if ( strpos ( $url , '://' ) === false ) {
1741 $url = 'http://' . $url ;
1742 } else if ( substr ( $url , 0 , 5 ) == 'feed:' ) {
1743 $url = 'http:' . substr ( $url , 5 );
1746 //prepend slash if the URL has no slash in it
1747 // "http://www.example" -> "http://www.example/"
1748 if ( strpos ( $url , '/' , strpos ( $url , ':' ) +
3 ) === false ) {
1752 if ( $url != "http:///" )
1758 function validate_feed_url ( $url ) {
1759 $parts = parse_url ( $url );
1761 return ( $parts [ 'scheme' ] == 'http' ||
$parts [ 'scheme' ] == 'feed' ||
$parts [ 'scheme' ] == 'https' );
1765 function get_article_enclosures ( $id ) {
1767 $query = "SELECT * FROM ttrss_enclosures
1768 WHERE post_id = ' $id ' AND content_url != ''" ;
1772 $result = db_query ( $query );
1774 if ( db_num_rows ( $result ) > 0 ) {
1775 while ( $line = db_fetch_assoc ( $result )) {
1776 array_push ( $rv , $line );
1783 /* function save_email_address($email) {
1784 // FIXME: implement persistent storage of emails
1786 if (!$_SESSION['stored_emails'])
1787 $_SESSION['stored_emails'] = array();
1789 if (!in_array($email, $_SESSION['stored_emails']))
1790 array_push($_SESSION['stored_emails'], $email);
1794 function get_feed_access_key ( $feed_id , $is_cat , $owner_uid = false ) {
1796 if (! $owner_uid ) $owner_uid = $_SESSION [ "uid" ];
1798 $sql_is_cat = bool_to_sql_bool ( $is_cat );
1800 $result = db_query ( "SELECT access_key FROM ttrss_access_keys
1801 WHERE feed_id = ' $feed_id ' AND is_cat = $sql_is_cat
1802 AND owner_uid = " . $owner_uid );
1804 if ( db_num_rows ( $result ) == 1 ) {
1805 return db_fetch_result ( $result , 0 , "access_key" );
1807 $key = db_escape_string ( uniqid_short ());
1809 $result = db_query ( "INSERT INTO ttrss_access_keys
1810 (access_key, feed_id, is_cat, owner_uid)
1811 VALUES (' $key ', ' $feed_id ', $sql_is_cat , ' $owner_uid ')" );
1818 function get_feeds_from_html ( $url , $content )
1820 $url = fix_url ( $url );
1821 $baseUrl = substr ( $url , 0 , strrpos ( $url , '/' ) +
1 );
1823 libxml_use_internal_errors ( true );
1825 $doc = new DOMDocument ();
1826 $doc -> loadHTML ( $content );
1827 $xpath = new DOMXPath ( $doc );
1828 $entries = $xpath -> query ( '/html/head/link[ @rel ="alternate" and ' .
1829 '(contains( @type ,"rss") or contains( @type ,"atom"))]|/html/head/link[ @rel ="feed"]' );
1830 $feedUrls = array ();
1831 foreach ( $entries as $entry ) {
1832 if ( $entry -> hasAttribute ( 'href' )) {
1833 $title = $entry -> getAttribute ( 'title' );
1835 $title = $entry -> getAttribute ( 'type' );
1837 $feedUrl = rewrite_relative_url (
1838 $baseUrl , $entry -> getAttribute ( 'href' )
1840 $feedUrls [ $feedUrl ] = $title ;
1846 function is_html ( $content ) {
1847 return preg_match ( "/<html|DOCTYPE html/i" , substr ( $content , 0 , 100 )) !== 0 ;
1850 function url_is_html ( $url , $login = false , $pass = false ) {
1851 return is_html ( fetch_file_contents ( $url , false , $login , $pass ));
1854 function print_label_select ( $name , $value , $attributes = "" ) {
1856 $result = db_query ( "SELECT caption FROM ttrss_labels2
1857 WHERE owner_uid = '" . $_SESSION [ "uid" ]. "' ORDER BY caption" );
1859 print "<select default= \" $value\" name= \" " . htmlspecialchars ( $name ) .
1860 " \" $attributes onchange= \" labelSelectOnChange(this) \" >" ;
1862 while ( $line = db_fetch_assoc ( $result )) {
1864 $issel = ( $line [ "caption" ] == $value ) ?
"selected= \" 1 \" " : "" ;
1866 print "<option value= \" " . htmlspecialchars ( $line [ "caption" ]). " \"
1867 $issel >" . htmlspecialchars ( $line [ "caption" ]) . "</option>" ;
1871 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
1878 function format_article_enclosures ( $id , $always_display_enclosures ,
1879 $article_content , $hide_images = false ) {
1881 $result = get_article_enclosures ( $id );
1884 foreach ( PluginHost
:: getInstance ()-> get_hooks ( PluginHost
:: HOOK_FORMAT_ENCLOSURES
) as $plugin ) {
1885 $retval = $plugin -> hook_format_enclosures ( $rv , $result , $id , $always_display_enclosures , $article_content , $hide_images );
1886 if ( is_array ( $retval )) {
1888 $result = $retval [ 1 ];
1894 if ( $rv === '' && ! empty ( $result )) {
1895 $entries_html = array ();
1897 $entries_inline = array ();
1899 foreach ( $result as $line ) {
1901 $url = $line [ "content_url" ];
1902 $ctype = $line [ "content_type" ];
1903 $title = $line [ "title" ];
1904 $width = $line [ "width" ];
1905 $height = $line [ "height" ];
1907 if (! $ctype ) $ctype = __ ( "unknown type" );
1909 $filename = substr ( $url , strrpos ( $url , "/" ) +
1 );
1911 $player = format_inline_player ( $url , $ctype );
1913 if ( $player ) array_push ( $entries_inline , $player );
1915 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
1916 # $filename . " (" . $ctype . ")" . "</a>";
1918 $entry = "<div onclick= \" window.open('" . htmlspecialchars ( $url ). "') \"
1919 dojoType= \" dijit.MenuItem \" > $filename ( $ctype )</div>" ;
1921 array_push ( $entries_html , $entry );
1925 $entry [ "type" ] = $ctype ;
1926 $entry [ "filename" ] = $filename ;
1927 $entry [ "url" ] = $url ;
1928 $entry [ "title" ] = $title ;
1929 $entry [ "width" ] = $width ;
1930 $entry [ "height" ] = $height ;
1932 array_push ( $entries , $entry );
1935 if ( $_SESSION [ 'uid' ] && ! get_pref ( "STRIP_IMAGES" ) && ! $_SESSION [ "bw_limit" ]) {
1936 if ( $always_display_enclosures ||
1937 ! preg_match ( "/<img/i" , $article_content )) {
1939 foreach ( $entries as $entry ) {
1941 foreach ( PluginHost
:: getInstance ()-> get_hooks ( PluginHost
:: HOOK_RENDER_ENCLOSURE
) as $plugin )
1942 $retval = $plugin -> hook_render_enclosure ( $entry , $hide_images );
1949 if ( preg_match ( "/image/" , $entry [ "type" ]) ||
1950 preg_match ( "/\.(jpg|png|gif|bmp)/i" , $entry [ "filename" ])) {
1952 if (! $hide_images ) {
1954 if ( $entry [ 'height' ] > 0 )
1955 $encsize .= ' height="' . intval ( $entry [ 'width' ]) . '"' ;
1956 if ( $entry [ 'width' ] > 0 )
1957 $encsize .= ' width="' . intval ( $entry [ 'height' ]) . '"' ;
1959 alt= \" " . htmlspecialchars ( $entry [ "filename" ]). " \"
1960 src= \" " . htmlspecialchars ( $entry [ "url" ]) . " \"
1961 " . $encsize . " /></p>" ;
1963 $rv .= "<p><a target= \" _blank \"
1964 href= \" " . htmlspecialchars ( $entry [ "url" ]). " \"
1965 >" . htmlspecialchars ( $entry [ "url" ]) . "</a></p>" ;
1968 if ( $entry [ 'title' ]) {
1969 $rv .= "<div class= \" enclosure_title \" >${entry['title']}</div>" ;
1977 if ( count ( $entries_inline ) > 0 ) {
1978 $rv .= "<hr clear='both'/>" ;
1979 foreach ( $entries_inline as $entry ) { $rv .= $entry ; };
1980 $rv .= "<hr clear='both'/>" ;
1983 $rv .= "<div class= \" attachments \" dojoType= \" dijit.form.DropDownButton \" >" .
1984 "<span>" . __ ( 'Attachments' ). "</span>" ;
1986 $rv .= "<div dojoType= \" dijit.Menu \" style= \" display: none; \" >" ;
1988 foreach ( $entries as $entry ) {
1989 if ( $entry [ "title" ])
1990 $title = "— " . truncate_string ( $entry [ "title" ], 30 );
1994 $rv .= "<div onclick='window.open( \" " . htmlspecialchars ( $entry [ "url" ]). " \" )'
1995 dojoType= \" dijit.MenuItem \" >" . htmlspecialchars ( $entry [ "filename" ]). " $title </div>" ;
2006 function getLastArticleId () {
2007 $result = db_query ( "SELECT ref_id AS id FROM ttrss_user_entries
2008 WHERE owner_uid = " . $_SESSION [ "uid" ] . " ORDER BY ref_id DESC LIMIT 1" );
2010 if ( db_num_rows ( $result ) == 1 ) {
2011 return db_fetch_result ( $result , 0 , "id" );
2017 function build_url ( $parts ) {
2018 return $parts [ 'scheme' ] . "://" . $parts [ 'host' ] . $parts [ 'path' ];
2022 * Converts a (possibly) relative URL to a absolute one.
2024 * @param string $url Base URL (i.e. from where the document is)
2025 * @param string $rel_url Possibly relative URL in the document
2027 * @return string Absolute URL
2029 function rewrite_relative_url ( $url , $rel_url ) {
2030 if ( strpos ( $rel_url , ":" ) !== false ) {
2032 } else if ( strpos ( $rel_url , "://" ) !== false ) {
2034 } else if ( strpos ( $rel_url , "//" ) === 0 ) {
2035 # protocol-relative URL (rare but they exist)
2037 } else if ( strpos ( $rel_url , "/" ) === 0 )
2039 $parts = parse_url ( $url );
2040 $parts [ 'path' ] = $rel_url ;
2042 return build_url ( $parts );
2045 $parts = parse_url ( $url );
2046 if (! isset ( $parts [ 'path' ])) {
2047 $parts [ 'path' ] = '/' ;
2049 $dir = $parts [ 'path' ];
2050 if ( substr ( $dir , - 1 ) !== '/' ) {
2051 $dir = dirname ( $parts [ 'path' ]);
2052 $dir !== '/' && $dir .= '/' ;
2054 $parts [ 'path' ] = $dir . $rel_url ;
2056 return build_url ( $parts );
2060 function cleanup_tags ( $days = 14 , $limit = 1000 ) {
2062 if ( DB_TYPE
== "pgsql" ) {
2063 $interval_query = "date_updated < NOW() - INTERVAL ' $days days'" ;
2064 } else if ( DB_TYPE
== "mysql" ) {
2065 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)" ;
2070 while ( $limit > 0 ) {
2073 $query = "SELECT ttrss_tags.id AS id
2074 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
2075 WHERE post_int_id = int_id AND $interval_query AND
2076 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part " ;
2078 $result = db_query ( $query );
2082 while ( $line = db_fetch_assoc ( $result )) {
2083 array_push ( $ids , $line [ 'id' ]);
2086 if ( count ( $ids ) > 0 ) {
2087 $ids = join ( "," , $ids );
2089 $tmp_result = db_query ( "DELETE FROM ttrss_tags WHERE id IN ( $ids )" );
2090 $tags_deleted +
= db_affected_rows ( $tmp_result );
2095 $limit -= $limit_part ;
2098 return $tags_deleted ;
2101 function print_user_stylesheet () {
2102 $value = get_pref ( 'USER_STYLESHEET' );
2105 print "<style type= \" text/css \" >" ;
2106 print str_replace ( "<br/>" , " \n " , $value );
2112 function filter_to_sql ( $filter , $owner_uid ) {
2115 if ( DB_TYPE
== "pgsql" )
2118 $reg_qpart = "REGEXP" ;
2120 foreach ( $filter [ "rules" ] AS $rule ) {
2121 $rule [ 'reg_exp' ] = str_replace ( '/' , '\/' , $rule [ "reg_exp" ]);
2122 $regexp_valid = preg_match ( '/' . $rule [ 'reg_exp' ] . '/' ,
2123 $rule [ 'reg_exp' ]) !== FALSE ;
2125 if ( $regexp_valid ) {
2127 $rule [ 'reg_exp' ] = db_escape_string ( $rule [ 'reg_exp' ]);
2129 switch ( $rule [ "type" ]) {
2131 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('" .
2132 $rule [ 'reg_exp' ] . "')" ;
2135 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('" .
2136 $rule [ 'reg_exp' ] . "')" ;
2139 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('" .
2140 $rule [ 'reg_exp' ] . "') OR LOWER(" .
2141 "ttrss_entries.content) $reg_qpart LOWER('" . $rule [ 'reg_exp' ] . "')" ;
2144 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('" .
2145 $rule [ 'reg_exp' ] . "')" ;
2148 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('" .
2149 $rule [ 'reg_exp' ] . "')" ;
2152 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('" .
2153 $rule [ 'reg_exp' ] . "')" ;
2157 if ( isset ( $rule [ 'inverse' ])) $qpart = "NOT ( $qpart )" ;
2159 if ( isset ( $rule [ "feed_id" ]) && $rule [ "feed_id" ] > 0 ) {
2160 $qpart .= " AND feed_id = " . db_escape_string ( $rule [ "feed_id" ]);
2163 if ( isset ( $rule [ "cat_id" ])) {
2165 if ( $rule [ "cat_id" ] > 0 ) {
2166 $children = getChildCategories ( $rule [ "cat_id" ], $owner_uid );
2167 array_push ( $children , $rule [ "cat_id" ]);
2169 $children = join ( "," , $children );
2171 $cat_qpart = "cat_id IN ( $children )" ;
2173 $cat_qpart = "cat_id IS NULL" ;
2176 $qpart .= " AND $cat_qpart " ;
2179 $qpart .= " AND feed_id IS NOT NULL" ;
2181 array_push ( $query , "( $qpart )" );
2186 if ( count ( $query ) > 0 ) {
2187 $fullquery = "(" . join ( $filter [ "match_any_rule" ] ?
"OR" : "AND" , $query ) . ")" ;
2189 $fullquery = "(false)" ;
2192 if ( $filter [ 'inverse' ]) $fullquery = "(NOT $fullquery )" ;
2197 if (! function_exists ( 'gzdecode' )) {
2198 function gzdecode ( $string ) { // no support for 2nd argument
2199 return file_get_contents ( 'compress.zlib://data:who/cares;base64,' .
2200 base64_encode ( $string ));
2204 function get_random_bytes ( $length ) {
2205 if ( function_exists ( 'openssl_random_pseudo_bytes' )) {
2206 return openssl_random_pseudo_bytes ( $length );
2210 for ( $i = 0 ; $i < $length ; $i ++
)
2211 $output .= chr ( mt_rand ( 0 , 255 ));
2217 function read_stdin () {
2218 $fp = fopen ( "php://stdin" , "r" );
2221 $line = trim ( fgets ( $fp ));
2229 function tmpdirname ( $path , $prefix ) {
2230 // Use PHP's tmpfile function to create a temporary
2231 // directory name. Delete the file and keep the name.
2232 $tempname = tempnam ( $path , $prefix );
2236 if (! unlink ( $tempname ))
2242 function getFeedCategory ( $feed ) {
2243 $result = db_query ( "SELECT cat_id FROM ttrss_feeds
2244 WHERE id = ' $feed '" );
2246 if ( db_num_rows ( $result ) > 0 ) {
2247 return db_fetch_result ( $result , 0 , "cat_id" );
2254 function implements_interface ( $class , $interface ) {
2255 return in_array ( $interface , class_implements ( $class ));
2258 function geturl ( $url , $depth = 0 , $nobody = true ){
2260 if ( $depth == 20 ) return $url ;
2262 if (! function_exists ( 'curl_init' ))
2263 return user_error ( 'CURL Must be installed for geturl function to work. Ask your host to enable it or uncomment extension=php_curl.dll in php.ini' , E_USER_ERROR
);
2265 $curl = curl_init ();
2266 $header [ 0 ] = "Accept: text/xml,application/xml,application/xhtml+xml," ;
2267 $header [ 0 ] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" ;
2268 $header [] = "Cache-Control: max-age=0" ;
2269 $header [] = "Connection: keep-alive" ;
2270 $header [] = "Keep-Alive: 300" ;
2271 $header [] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7" ;
2272 $header [] = "Accept-Language: en-us,en;q=0.5" ;
2273 $header [] = "Pragma: " ;
2275 curl_setopt ( $curl , CURLOPT_URL
, $url );
2276 curl_setopt ( $curl , CURLOPT_USERAGENT
, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0' );
2277 curl_setopt ( $curl , CURLOPT_HTTPHEADER
, $header );
2278 curl_setopt ( $curl , CURLOPT_HEADER
, true );
2279 curl_setopt ( $curl , CURLOPT_NOBODY
, $nobody );
2280 curl_setopt ( $curl , CURLOPT_REFERER
, $url );
2281 curl_setopt ( $curl , CURLOPT_ENCODING
, 'gzip,deflate' );
2282 curl_setopt ( $curl , CURLOPT_AUTOREFERER
, true );
2283 curl_setopt ( $curl , CURLOPT_RETURNTRANSFER
, true );
2284 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
2285 curl_setopt ( $curl , CURLOPT_TIMEOUT
, 60 );
2286 curl_setopt ( $curl , CURLOPT_SSL_VERIFYPEER
, false );
2288 if ( defined ( '_CURL_HTTP_PROXY' )) {
2289 curl_setopt ( $curl , CURLOPT_PROXY
, _CURL_HTTP_PROXY
);
2292 $html = curl_exec ( $curl );
2294 $status = curl_getinfo ( $curl );
2296 if ( $status [ 'http_code' ]!= 200 ){
2298 // idiot site not allowing http head
2299 if ( $status [ 'http_code' ] == 405 ) {
2301 return geturl ( $url , $depth +
1 , false );
2304 if ( $status [ 'http_code' ] == 301 ||
$status [ 'http_code' ] == 302 ) {
2306 list ( $header ) = explode ( " \r\n\r\n " , $html , 2 );
2308 preg_match ( "/(Location:|URI:)[^( \n )]*/" , $header , $matches );
2309 $url = trim ( str_replace ( $matches [ 1 ], "" , $matches [ 0 ]));
2310 $url_parsed = parse_url ( $url );
2311 return ( isset ( $url_parsed )) ?
geturl ( $url , $depth +
1 ): '' ;
2314 global $fetch_last_error ;
2316 $fetch_last_error = curl_errno ( $curl ) . " " . curl_error ( $curl );
2320 # foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
2321 # $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
2322 # $handle = @fopen('./curl.error.log', 'a');
2323 # fwrite($handle, $line);
2330 function get_minified_js ( $files ) {
2331 require_once 'lib/jshrink/Minifier.php' ;
2335 foreach ( $files as $js ) {
2336 if (! isset ( $_GET [ 'debug' ])) {
2337 $cached_file = CACHE_DIR
. "/js/" . basename ( $js ). ".js" ;
2339 if ( file_exists ( $cached_file ) && is_readable ( $cached_file ) && filemtime ( $cached_file ) >= filemtime ( "js/ $js .js" )) {
2341 list ( $header , $contents ) = explode ( " \n " , file_get_contents ( $cached_file ), 2 );
2343 if ( $header && $contents ) {
2344 list ( $htag , $hversion ) = explode ( ":" , $header );
2346 if ( $htag == "tt-rss" && $hversion == VERSION
) {
2353 $minified = JShrink\Minifier
:: minify ( file_get_contents ( "js/ $js .js" ));
2354 file_put_contents ( $cached_file , "tt-rss:" . VERSION
. " \n " . $minified );
2358 $rv .= file_get_contents ( "js/ $js .js" ); // no cache in debug mode
2365 function stylesheet_tag ( $filename ) {
2366 $timestamp = filemtime ( $filename );
2368 return "<link rel= \" stylesheet \" type= \" text/css \" href= \" $filename ? $timestamp\" /> \n " ;
2371 function javascript_tag ( $filename ) {
2374 if (!( strpos ( $filename , "?" ) === FALSE )) {
2375 $query = substr ( $filename , strpos ( $filename , "?" ) +
1 );
2376 $filename = substr ( $filename , 0 , strpos ( $filename , "?" ));
2379 $timestamp = filemtime ( $filename );
2381 if ( $query ) $timestamp .= "& $query " ;
2383 return "<script type= \" text/javascript \" charset= \" utf-8 \" src= \" $filename ? $timestamp\" ></script> \n " ;
2386 function calculate_dep_timestamp () {
2387 $files = array_merge ( glob ( "js/*.js" ), glob ( "css/*.css" ));
2391 foreach ( $files as $file ) {
2392 if ( filemtime ( $file ) > $max_ts ) $max_ts = filemtime ( $file );
2398 function T_js_decl ( $s1 , $s2 ) {
2400 $s1 = preg_replace ( "/ \n /" , "" , $s1 );
2401 $s2 = preg_replace ( "/ \n /" , "" , $s2 );
2403 $s1 = preg_replace ( "/ \" /" , " \\\" " , $s1 );
2404 $s2 = preg_replace ( "/ \" /" , " \\\" " , $s2 );
2406 return "T_messages[ \" $s1\" ] = \" $s2\" ; \n " ;
2410 function init_js_translations () {
2412 print 'var T_messages = new Object();
2415 if (T_messages[msg]) {
2416 return T_messages[msg];
2422 function ngettext(msg1, msg2, n) {
2423 return __((parseInt(n) > 1) ? msg2 : msg1);
2426 $l10n = _get_reader ();
2428 for ( $i = 0 ; $i < $l10n -> total
; $i ++
) {
2429 $orig = $l10n -> get_original_string ( $i );
2430 if ( strpos ( $orig , " \000 " ) !== FALSE ) { // Plural forms
2431 $key = explode ( chr ( 0 ), $orig );
2432 print T_js_decl ( $key [ 0 ], _ngettext ( $key [ 0 ], $key [ 1 ], 1 )); // Singular
2433 print T_js_decl ( $key [ 1 ], _ngettext ( $key [ 0 ], $key [ 1 ], 2 )); // Plural
2435 $translation = __ ( $orig );
2436 print T_js_decl ( $orig , $translation );
2441 function label_to_feed_id ( $label ) {
2442 return LABEL_BASE_INDEX
- 1 - abs ( $label );
2445 function feed_to_label_id ( $feed ) {
2446 return LABEL_BASE_INDEX
- 1 +
abs ( $feed );
2449 function get_theme_path ( $theme ) {
2450 $check = "themes/ $theme " ;
2451 if ( file_exists ( $check )) return $check ;
2453 $check = "themes.local/ $theme " ;
2454 if ( file_exists ( $check )) return $check ;
2457 function theme_valid ( $theme ) {
2458 if ( $theme == "default.css" ||
$theme == "night.css" ) return true ; // needed for array_filter
2459 $file = "themes/" . basename ( $theme );
2461 if (! file_exists ( $file )) $file = "themes.local/" . basename ( $theme );
2463 if ( file_exists ( $file ) && is_readable ( $file )) {
2464 $fh = fopen ( $file , "r" );
2467 $header = fgets ( $fh );
2470 return strpos ( $header , "supports-version:" . VERSION_STATIC
) !== FALSE ;
2477 function error_json ( $code ) {
2478 require_once "errors.php" ;
2480 @ $message = $ERRORS [ $code ];
2482 return json_encode ( array ( "error" =>
2483 array ( "code" => $code , "message" => $message )));
2487 function abs_to_rel_path ( $dir ) {
2488 $tmp = str_replace ( dirname ( __DIR__
), "" , $dir );
2490 if ( strlen ( $tmp ) > 0 && substr ( $tmp , 0 , 1 ) == "/" ) $tmp = substr ( $tmp , 1 );