X-Git-Url: https://git.wh0rd.org/?a=blobdiff_plain;f=include%2Ffunctions.php;h=921c9235b2c330062501ba2532e386b0374797a2;hb=1bcf8f456a89035701355652d10e8b135394e868;hp=960cf996f3b9e7cce8db33ef00e58662281ae466;hpb=fd994f1a488783003ede5fdb9cf2f68ef91696bb;p=tt-rss.git diff --git a/include/functions.php b/include/functions.php index 960cf996..921c9235 100644 --- a/include/functions.php +++ b/include/functions.php @@ -1,8 +1,9 @@ "Italiano", "ja_JP" => "日本語 (Japanese)", "nb_NO" => "Norwegian bokmål", + "pl_PL" => "Polski", "ru_RU" => "Русский", "pt_BR" => "Portuguese/Brazil", "zh_CN" => "Simplified Chinese"); @@ -69,14 +72,11 @@ $lang = _TRANSLATION_OVERRIDE_DEFAULT; } - if ($_COOKIE["ttrss_lang"] && $_COOKIE["ttrss_lang"] != "auto") { - $lang = $_COOKIE["ttrss_lang"]; - } - /* In login action of mobile version */ if ($_POST["language"] && defined('MOBILE_VERSION')) { $lang = $_POST["language"]; - $_COOKIE["ttrss_lang"] = $lang; + } else { + $lang = $_SESSION["language"]; } if ($lang) { @@ -102,16 +102,10 @@ require_once 'db-prefs.php'; require_once 'version.php'; - define('MAGPIE_OUTPUT_ENCODING', 'UTF-8'); - - define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION . ' (http://tt-rss.org/)'); - define('MAGPIE_USER_AGENT', SELF_USER_AGENT); - ini_set('user_agent', SELF_USER_AGENT); require_once 'lib/pubsubhubbub/publisher.php'; - - $purifier = false; + require_once 'lib/htmLawed.php'; $tz_offset = -1; $utc_tz = new DateTimeZone('UTC'); @@ -465,104 +459,6 @@ print ""; } - function get_article_filters($filters, $title, $content, $link, $timestamp, $author, $tags) { - $matches = array(); - - foreach ($filters as $filter) { - $match_any_rule = $filter["match_any_rule"]; - $filter_match = false; - - foreach ($filter["rules"] as $rule) { - $match = false; - $reg_exp = $rule["reg_exp"]; - - if (!$reg_exp) - continue; - - switch ($rule["type"]) { - case "title": - $match = @preg_match("/$reg_exp/i", $title); - break; - case "content": - $match = @preg_match("/$reg_exp/i", $content); - break; - case "both": - $match = (@preg_match("/$reg_exp/i", $title) || @preg_match("/$reg_exp/i", $title)); - break; - case "link": - $match = @preg_match("/$reg_exp/i", $link); - break; - case "author": - $match = @preg_match("/$reg_exp/i", $author); - break; - case "tag": - $tag_string = join(",", $tags); - $match = @preg_match("/$reg_exp/i", $tag_string); - break; - } - - if ($match_any_rule) { - if ($match) { - $filter_match = true; - break; - } - } else { - $filter_match = $match; - if (!$match) { - break; - } - } - } - - if ($filter_match) { - foreach ($filter["actions"] AS $action) { - array_push($matches, $action); - } - } - } - - return $matches; - } - - function find_article_filter($filters, $filter_name) { - foreach ($filters as $f) { - if ($f["type"] == $filter_name) { - return $f; - }; - } - return false; - } - - function find_article_filters($filters, $filter_name) { - $results = array(); - - foreach ($filters as $f) { - if ($f["type"] == $filter_name) { - array_push($results, $f); - }; - } - return $results; - } - - function calculate_article_score($filters) { - $score = 0; - - foreach ($filters as $f) { - if ($f["type"] == "score") { - $score += $f["param"]; - }; - } - return $score; - } - - function assign_article_to_labels($link, $id, $filters, $owner_uid) { - foreach ($filters as $f) { - if ($f["type"] == "label") { - label_add_article($link, $id, $f["param"], $owner_uid); - }; - } - } - function getmicrotime() { list($usec, $sec) = explode(" ",microtime()); return ((float)$usec + (float)$sec); @@ -651,23 +547,15 @@ if (!SINGLE_USER_MODE) { $user_id = false; - $modules = explode(",", AUTH_MODULES); - foreach ($modules as $module) { - $module_class = "auth_$module"; - if (class_exists($module_class)) { - $authenticator = new $module_class($link); + global $pluginhost; + foreach ($pluginhost->get_hooks($pluginhost::HOOK_AUTH_USER) as $plugin) { - $user_id = (int) $authenticator->authenticate($login, $password); - - if ($user_id) { - $_SESSION["auth_module"] = $module; - break; - } + $user_id = (int) $plugin->authenticate($login, $password); - } else { - print T_sprintf("Fatal: authentication module %s not found.", $module); - die; + if ($user_id) { + $_SESSION["auth_module"] = strtolower(get_class($plugin)); + break; } } @@ -815,78 +703,48 @@ return true; } - function login_sequence($link, $mobile = false) { - $_SESSION["prefs_cache"] = array(); - - if (!SINGLE_USER_MODE) { - - $login_action = $_POST["login_action"]; - - # try to authenticate user if called from login form - if ($login_action == "do_login") { - $login = db_escape_string($_POST["login"]); - $password = $_POST["password"]; - $remember_me = $_POST["remember_me"]; - - if (authenticate_user($link, $login, $password)) { - $_POST["password"] = ""; - - $_SESSION["language"] = $_POST["language"]; - $_SESSION["ref_schema_version"] = get_schema_version($link, true); - $_SESSION["bw_limit"] = !!$_POST["bw_limit"]; - - if ($_POST["profile"]) { - - $profile = db_escape_string($_POST["profile"]); + function load_user_plugins($link, $owner_uid) { + if ($owner_uid) { + $plugins = get_pref($link, "_ENABLED_PLUGINS", $owner_uid); - $result = db_query($link, "SELECT id FROM ttrss_settings_profiles - WHERE id = '$profile' AND owner_uid = " . $_SESSION["uid"]); + global $pluginhost; + $pluginhost->load($plugins, $pluginhost::KIND_USER, $owner_uid); - if (db_num_rows($result) != 0) { - $_SESSION["profile"] = $profile; - $_SESSION["prefs_cache"] = array(); - } - } - - if ($_REQUEST['return']) { - header("Location: " . $_REQUEST['return']); - } else { - header("Location: " . $_SERVER["REQUEST_URI"]); - } - - exit; - - return; - } else { - $_SESSION["login_error_msg"] = __("Incorrect username or password"); - } + if (get_schema_version($link) > 100) { + $pluginhost->load_data(); } + } + } + function login_sequence($link, $login_form = 0) { + if (SINGLE_USER_MODE) { + authenticate_user($link, "admin", null); + load_user_plugins($link, $_SESSION["uid"]); + } else { if (!$_SESSION["uid"] || !validate_session($link)) { if (AUTH_AUTO_LOGIN && authenticate_user($link, null, null)) { $_SESSION["ref_schema_version"] = get_schema_version($link, true); } else { authenticate_user($link, null, null, true); - render_login_form($link, $mobile); - exit; } + + if (!$_SESSION["uid"]) render_login_form($link, $login_form); + } else { /* bump login timestamp */ db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " . $_SESSION["uid"]); + } - if ($_SESSION["language"] && SESSION_COOKIE_LIFETIME > 0) { - setcookie("ttrss_lang", $_SESSION["language"], - time() + SESSION_COOKIE_LIFETIME); - } - - // try to remove possible duplicates from feed counter cache -// ccache_cleanup($link, $_SESSION["uid"]); + if ($_SESSION["uid"] && $_SESSION["language"] && SESSION_COOKIE_LIFETIME > 0) { + setcookie("ttrss_lang", $_SESSION["language"], + time() + SESSION_COOKIE_LIFETIME); } - } else { - return authenticate_user($link, "admin", null); + if ($_SESSION["uid"]) { + load_user_plugins($link, $_SESSION["uid"]); + } } } @@ -1204,24 +1062,22 @@ if ($feed >= 0) { if ($feed > 0) { - $cat_qpart = "cat_id = '$feed'"; + $children = getChildCategories($link, $feed, $owner_uid); + array_push($children, $feed); + + $children = join(",", $children); + + $cat_qpart = "cat_id IN ($children)"; } else { $cat_qpart = "cat_id IS NULL"; } - $tmp_result = db_query($link, "SELECT id - FROM ttrss_feeds WHERE $cat_qpart AND owner_uid = $owner_uid"); - - while ($tmp_line = db_fetch_assoc($tmp_result)) { - - $tmp_feed = $tmp_line["id"]; + db_query($link, "UPDATE ttrss_user_entries + SET unread = false,last_read = NOW() + WHERE feed_id IN (SELECT id FROM ttrss_feeds WHERE $cat_qpart) + AND $ref_check_qpart + AND owner_uid = $owner_uid"); - db_query($link, "UPDATE ttrss_user_entries - SET unread = false,last_read = NOW() - WHERE feed_id = '$tmp_feed' - AND $ref_check_qpart - AND owner_uid = $owner_uid"); - } } else if ($feed == -2) { db_query($link, "UPDATE ttrss_user_entries @@ -1350,7 +1206,7 @@ if (db_num_rows($result) == 1) { return db_fetch_result($result, 0, "title"); } else { - return "Uncategorized"; + return __("Uncategorized"); } } } @@ -1384,8 +1240,7 @@ } $cv = array("id" => $line["cat_id"], "kind" => "cat", - "child_counter" => $child_counter, - "counter" => $line["unread"]); + "counter" => $line["unread"] + $child_counter); array_push($ret_arr, $cv); } @@ -1393,7 +1248,7 @@ /* Special case: NULL category doesn't actually exist in the DB */ $cv = array("id" => 0, "kind" => "cat", - "counter" => ccache_find($link, 0, $_SESSION["uid"], true)); + "counter" => (int) ccache_find($link, 0, $_SESSION["uid"], true)); array_push($ret_arr, $cv); @@ -1603,7 +1458,7 @@ } $cv = array("id" => "global-unread", - "counter" => $global_unread); + "counter" => (int) $global_unread); array_push($ret_arr, $cv); @@ -1613,7 +1468,7 @@ $subscribed_feeds = db_fetch_result($result, 0, "fn"); $cv = array("id" => "subscribed-feeds", - "counter" => $subscribed_feeds); + "counter" => (int) $subscribed_feeds); array_push($ret_arr, $cv); @@ -1659,7 +1514,7 @@ $count = getFeedUnread($link, $i); $cv = array("id" => $i, - "counter" => $count); + "counter" => (int) $count); // if (get_pref($link, 'EXTENDED_FEEDLIST')) // $cv["xmsg"] = getFeedArticles($link, $i)." ".__("total"); @@ -1687,7 +1542,7 @@ $count = getFeedUnread($link, $id); $cv = array("id" => $id, - "counter" => $count); + "counter" => (int) $count); if ($descriptions) $cv["description"] = $label_name; @@ -1732,7 +1587,7 @@ $cv = array("id" => $id, "updated" => $last_updated, - "counter" => $count, + "counter" => (int) $count, "has_img" => (int) $has_img); if ($last_error) @@ -1780,34 +1635,24 @@ if (!$url || !validate_feed_url($url)) return array("code" => 2); - $update_method = 0; - - $result = db_query($link, "SELECT twitter_oauth FROM ttrss_users - WHERE id = ".$_SESSION['uid']); + $contents = @fetch_file_contents($url, false, $auth_login, $auth_pass); - $has_oauth = db_fetch_result($result, 0, 'twitter_oauth'); + if (!$contents) { + return array("code" => 5, "message" => $fetch_last_error); + } - if (!$need_auth || !$has_oauth || strpos($url, '://api.twitter.com') === false) { - if (!fetch_file_contents($url, false, $auth_login, $auth_pass)) - return array("code" => 5, "message" => $fetch_last_error); + if (is_html($contents)) { + $feedUrls = get_feeds_from_html($url, $contents); - if (url_is_html($url, $auth_login, $auth_pass)) { - $feedUrls = get_feeds_from_html($url, $auth_login, $auth_pass); - if (count($feedUrls) == 0) { - return array("code" => 3); - } else if (count($feedUrls) > 1) { - return array("code" => 4); - } - //use feed url as new URL - $url = key($feedUrls); + if (count($feedUrls) == 0) { + return array("code" => 3); + } else if (count($feedUrls) > 1) { + return array("code" => 4, "feeds" => $feedUrls); } + //use feed url as new URL + $url = key($feedUrls); + } - } else { - if (!fetch_twitter_rss($link, $url, $_SESSION['uid'])) - return array("code" => 5); - - $update_method = 3; - } if ($cat_id == "0" || !$cat_id) { $cat_qpart = "NULL"; } else { @@ -1823,7 +1668,7 @@ "INSERT INTO ttrss_feeds (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method) VALUES ('".$_SESSION["uid"]."', '$url', - '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', '$update_method')"); + '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0)"); $result = db_query($link, "SELECT id FROM ttrss_feeds WHERE feed_url = '$url' @@ -2015,16 +1860,16 @@ } - function getFeedIcon($id, $cat) { + function getFeedIcon($id) { switch ($id) { case 0: return "images/archive.png"; break; case -1: - return "images/mark_set.png"; + return "images/mark_set.svg"; break; case -2: - return "images/pub_set.png"; + return "images/pub_set.svg"; break; case -3: return "images/fresh.png"; @@ -2048,7 +1893,7 @@ function getFeedTitle($link, $id, $cat = false) { if ($cat) { - return getFeedCatTitle($link, $id); + return getCategoryTitle($link, $id); } else if ($id == -1) { return __("Starred articles"); } else if ($id == -2) { @@ -2090,8 +1935,8 @@ $params["sign_progress"] = theme_image($link, "images/indicator_white.gif"); $params["sign_progress_tiny"] = theme_image($link, "images/indicator_tiny.gif"); - $params["sign_excl"] = theme_image($link, "images/sign_excl.png"); - $params["sign_info"] = theme_image($link, "images/sign_info.png"); + $params["sign_excl"] = theme_image($link, "images/sign_excl.svg"); + $params["sign_info"] = theme_image($link, "images/sign_info.svg"); foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS", "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP", @@ -2103,7 +1948,6 @@ $params["icons_url"] = ICONS_URL; $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME; - $params["default_include_children"] = get_pref($link, "_DEFAULT_INCLUDE_CHILDREN"); $params["default_view_mode"] = get_pref($link, "_DEFAULT_VIEW_MODE"); $params["default_view_limit"] = (int) get_pref($link, "_DEFAULT_VIEW_LIMIT"); $params["default_view_order_by"] = get_pref($link, "_DEFAULT_VIEW_ORDER_BY"); @@ -2119,12 +1963,144 @@ $params["num_feeds"] = (int) $num_feeds; $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST"); + $params["hotkeys"] = get_hotkeys_map($link); $params["csrf_token"] = $_SESSION["csrf_token"]; return $params; } + function get_hotkeys_info($link) { + $hotkeys = array( + __("Navigation") => array( + "next_feed" => __("Open next feed"), + "prev_feed" => __("Open previous feed"), + "next_article" => __("Open next article"), + "prev_article" => __("Open previous article"), + "search_dialog" => __("Show search dialog")), + __("Article") => array( + "toggle_mark" => __("Toggle starred"), + "toggle_publ" => __("Toggle published"), + "toggle_unread" => __("Toggle unread"), + "edit_tags" => __("Edit tags"), + "dismiss_selected" => __("Dismiss selected"), + "dismiss_read" => __("Dismiss read"), + "open_in_new_window" => __("Open in new window"), + "catchup_below" => __("Mark below as read"), + "catchup_above" => __("Mark above as read"), + "article_scroll_down" => __("Scroll down"), + "article_scroll_up" => __("Scroll up"), + "select_article_cursor" => __("Select article under cursor"), + "email_article" => __("Email article"), + "toggle_widescreen" => __("Toggle widescreen mode")), + __("Article selection") => array( + "select_all" => __("Select all articles"), + "select_unread" => __("Select unread"), + "select_marked" => __("Select starred"), + "select_published" => __("Select published"), + "select_invert" => __("Invert selection"), + "select_none" => __("Deselect everything")), + __("Feed") => array( + "feed_refresh" => __("Refresh current feed"), + "feed_unhide_read" => __("Un/hide read feeds"), + "feed_subscribe" => __("Subscribe to feed"), + "feed_edit" => __("Edit feed"), + "feed_catchup" => __("Mark as read"), + "feed_reverse" => __("Reverse headlines"), + "feed_debug_update" => __("Debug feed update"), + "catchup_all" => __("Mark all feeds as read"), + "cat_toggle_collapse" => __("Un/collapse current category")), + __("Go to") => array( + "goto_all" => __("All articles"), + "goto_fresh" => __("Fresh"), + "goto_marked" => __("Starred"), + "goto_published" => __("Published"), + "goto_tagcloud" => __("Tag cloud"), + "goto_prefs" => __("Preferences")), + __("Other") => array( + "create_label" => __("Create label"), + "create_filter" => __("Create filter"), + "collapse_sidebar" => __("Un/collapse sidebar"), + "help_dialog" => __("Show help dialog")) + ); + + return $hotkeys; + } + + function get_hotkeys_map($link) { + $hotkeys = array( +// "navigation" => array( + "k" => "next_feed", + "j" => "prev_feed", + "n" => "next_article", + "p" => "prev_article", + "(38)" => "prev_article", + "(40)" => "next_article", + "/" => "search_dialog", +// "article" => array( + "s" => "toggle_mark", + "S" => "toggle_publ", + "u" => "toggle_unread", + "T" => "edit_tags", + "D" => "dismiss_selected", + "X" => "dismiss_read", + "o" => "open_in_new_window", + "c p" => "catchup_below", + "c n" => "catchup_above", + "N" => "article_scroll_down", + "P" => "article_scroll_up", + "a W" => "toggle_widescreen", + "e" => "email_article", +// "article_selection" => array( + "a a" => "select_all", + "a u" => "select_unread", + "a U" => "select_marked", + "a p" => "select_published", + "a i" => "select_invert", + "a n" => "select_none", +// "feed" => array( + "f r" => "feed_refresh", + "f a" => "feed_unhide_read", + "f s" => "feed_subscribe", + "f e" => "feed_edit", + "f q" => "feed_catchup", + "f x" => "feed_reverse", + "f D" => "feed_debug_update", + "Q" => "catchup_all", + "x" => "cat_toggle_collapse", +// "goto" => array( + "g a" => "goto_all", + "g f" => "goto_fresh", + "g s" => "goto_marked", + "g p" => "goto_published", + "g t" => "goto_tagcloud", + "g P" => "goto_prefs", +// "other" => array( + "(9)" => "select_article_cursor", // tab + "c l" => "create_label", + "c f" => "create_filter", + "c s" => "collapse_sidebar", + "(191)" => "help_dialog", + ); + + global $pluginhost; + foreach ($pluginhost->get_hooks($pluginhost::HOOK_HOTKEY_MAP) as $plugin) { + $hotkeys = $plugin->hook_hotkey_map($hotkeys); + } + + $prefixes = array(); + + foreach (array_keys($hotkeys) as $hotkey) { + $pair = explode(" ", $hotkey, 2); + + if (count($pair) > 1 && !in_array($pair[0], $prefixes)) { + array_push($prefixes, $pair[0]); + } + } + + return array($prefixes, $hotkeys); + } + function make_runtime_info($link) { $data = array(); @@ -2241,6 +2217,20 @@ return $search_query_part; } + function getParentCategories($link, $cat, $owner_uid) { + $rv = array(); + + $result = db_query($link, "SELECT parent_cat FROM ttrss_feed_categories + WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid"); + + while ($line = db_fetch_assoc($result)) { + array_push($rv, $line["parent_cat"]); + $rv = array_merge($rv, getParentCategories($link, $line["parent_cat"], $owner_uid)); + } + + return $rv; + } + function getChildCategories($link, $cat, $owner_uid) { $rv = array(); @@ -2281,7 +2271,30 @@ } if ($filter) { - $filter_query_part = filter_to_sql($filter); + + if (DB_TYPE == "pgsql") { + $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' "; + } else { + $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) "; + } + + $override_order = "updated DESC"; + + $filter_query_part = filter_to_sql($link, $filter, $owner_uid); + + // Try to check if SQL regexp implementation chokes on a valid regexp + $result = db_query($link, "SELECT true AS true_val FROM ttrss_entries, + ttrss_user_entries, ttrss_feeds, ttrss_feed_categories + WHERE $filter_query_part LIMIT 1", false); + + $test = db_fetch_result($result, 0, "true_val"); + + if (!$test) { + $filter_query_part = "false AND"; + } else { + $filter_query_part .= " AND"; + } + } else { $filter_query_part = ""; } @@ -2329,44 +2342,35 @@ $limit_query_part = "LIMIT " . $limit; } + $allow_archived = false; + $vfeed_query_part = ""; // override query strategy and enable feed display when searching globally if ($search && $search_mode == "all_feeds") { - $query_strategy_part = "ttrss_entries.id > 0"; + $query_strategy_part = "true"; $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; /* tags */ - } else if (preg_match("/^-?[0-9][0-9]*$/", $feed) == false) { - $query_strategy_part = "ttrss_entries.id > 0"; + } else if (!is_numeric($feed)) { + $query_strategy_part = "true"; $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE id = feed_id) as feed_title,"; - } else if ($feed > 0 && $search && $search_mode == "this_cat") { - + } else if ($search && $search_mode == "this_cat") { $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; - $tmp_result = false; - - if ($cat_view) { - $tmp_result = db_query($link, "SELECT id - FROM ttrss_feeds WHERE cat_id = '$feed'"); - } else { - $tmp_result = db_query($link, "SELECT id - FROM ttrss_feeds WHERE cat_id = (SELECT cat_id FROM ttrss_feeds - WHERE id = '$feed') AND id != '$feed'"); - } - - $cat_siblings = array(); - - if (db_num_rows($tmp_result) > 0) { - while ($p = db_fetch_assoc($tmp_result)) { - array_push($cat_siblings, "feed_id = " . $p["id"]); + if ($feed > 0) { + if ($include_children) { + $subcats = getChildCategories($link, $feed, $owner_uid); + array_push($subcats, $feed); + $cats_qpart = join(",", $subcats); + } else { + $cats_qpart = $feed; } - $query_strategy_part = sprintf("(feed_id = %d OR %s)", - $feed, implode(" OR ", $cat_siblings)); + $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)"; } else { - $query_strategy_part = "ttrss_entries.id > 0"; + $query_strategy_part = "ttrss_feeds.cat_id IS NULL"; } } else if ($feed > 0) { @@ -2378,13 +2382,10 @@ # sub-cats $subcats = getChildCategories($link, $feed, $owner_uid); - if (count($subcats) == 0) { - $query_strategy_part = "cat_id = '$feed'"; - } else { - array_push($subcats, $feed); - $query_strategy_part = "cat_id IN (". + array_push($subcats, $feed); + $query_strategy_part = "cat_id IN (". implode(",", $subcats).")"; - } + } else { $query_strategy_part = "cat_id = '$feed'"; } @@ -2400,17 +2401,23 @@ } } else if ($feed == 0 && !$cat_view) { // archive virtual feed $query_strategy_part = "feed_id IS NULL"; + $allow_archived = true; } else if ($feed == 0 && $cat_view) { // uncategorized $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL"; $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; } else if ($feed == -1) { // starred virtual feed $query_strategy_part = "marked = true"; $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; + $allow_archived = true; + } else if ($feed == -2) { // published virtual feed OR labels category if (!$cat_view) { $query_strategy_part = "published = true"; $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; + $allow_archived = true; + + if (!$override_order) $override_order = "last_read DESC, updated DESC"; } else { $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; @@ -2423,7 +2430,9 @@ } else if ($feed == -6) { // recently read $query_strategy_part = "unread = false AND last_read IS NOT NULL"; $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; - $override_order = "last_read DESC"; + $allow_archived = true; + + if (!$override_order) $override_order = "last_read DESC"; } else if ($feed == -3) { // fresh virtual feed $query_strategy_part = "unread = true AND score >= 0"; @@ -2448,9 +2457,10 @@ $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2"; + $allow_archived = true; } else { - $query_strategy_part = "id > 0"; // dumb + $query_strategy_part = "true"; } if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) { @@ -2476,7 +2486,7 @@ $feed_title = ""; if ($search) { - $feed_title = "Search results"; + $feed_title = T_sprintf("Search results: %s", $search); } else { if ($cat_view) { $feed_title = getCategoryTitle($link, $feed); @@ -2494,9 +2504,9 @@ } } - $content_query_part = "content as content_preview,"; + $content_query_part = "content as content_preview, cached_content, "; - if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) { + if (is_numeric($feed)) { if ($feed >= 0) { $feed_kind = "Feeds"; @@ -2508,18 +2518,21 @@ $offset_query_part = "OFFSET $offset"; } + // proper override_order applied above if ($vfeed_query_part && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) { if (!$override_order) { $order_by = "ttrss_feeds.title, $order_by"; + } else { + $order_by = "ttrss_feeds.title, $override_order"; } } - if ($feed != "0") { + if (!$allow_archived) { $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part"; $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND"; } else { - $from_qpart = "ttrss_entries,ttrss_user_entries$ext_tables_part + $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)"; } @@ -2634,36 +2647,12 @@ } function sanitize($link, $str, $force_strip_tags = false, $owner = false, $site_url = false) { - global $purifier; - if (!$owner) $owner = $_SESSION["uid"]; $res = trim($str); if (!$res) return ''; - // create global Purifier object if needed - if (!$purifier) { - require_once 'lib/htmlpurifier/library/HTMLPurifier.auto.php'; - - $config = HTMLPurifier_Config::createDefault(); - - $allowed = "p,a[href],i,em,b,strong,code,pre,blockquote,br,img[src|alt|title|align|hspace],ul,ol,li,h1,h2,h3,h4,s,object[classid|type|id|name|width|height|codebase],param[name|value],table,tr,td,span[class]"; - - $config->set('HTML.SafeObject', true); - @$config->set('HTML', 'Allowed', $allowed); - $config->set('Output.FlashCompat', true); - $config->set('Attr.EnableID', true); - if (!defined('MOBILE_VERSION')) { - @$config->set('Cache', 'SerializerPath', CACHE_DIR . "/htmlpurifier"); - } else { - @$config->set('Cache', 'SerializerPath', "../" . CACHE_DIR . "/htmlpurifier"); - } - - $config->set('Filter.YouTube', true); - - $purifier = new HTMLPurifier($config); - } - - $res = $purifier->purify($res); + $config = array('safe' => 1, 'deny_attribute' => 'style, width, height, class, id', 'comment' => 1, 'cdata' => 1, 'balance' => 0); + $res = htmLawed($res, $config); if (get_pref($link, "STRIP_IMAGES", $owner)) { $res = preg_replace('/]+>/is', '', $res); @@ -2718,224 +2707,10 @@ $node = $doc->getElementsByTagName('body')->item(0); + // http://tt-rss.org/redmine/issues/357 return $doc->saveXML($node, LIBXML_NOEMPTYTAG); } - /** - * Send by mail a digest of last articles. - * - * @param mixed $link The database connection. - * @param integer $limit The maximum number of articles by digest. - * @return boolean Return false if digests are not enabled. - */ - function send_headlines_digests($link, $debug = false) { - - require_once 'lib/phpmailer/class.phpmailer.php'; - - $user_limit = 15; // amount of users to process (e.g. emails to send out) - $limit = 1000; // maximum amount of headlines to include - - if ($debug) _debug("Sending digests, batch of max $user_limit users, headline limit = $limit"); - - if (DB_TYPE == "pgsql") { - $interval_query = "last_digest_sent < NOW() - INTERVAL '1 days'"; - } else if (DB_TYPE == "mysql") { - $interval_query = "last_digest_sent < DATE_SUB(NOW(), INTERVAL 1 DAY)"; - } - - $result = db_query($link, "SELECT id,email FROM ttrss_users - WHERE email != '' AND (last_digest_sent IS NULL OR $interval_query)"); - - while ($line = db_fetch_assoc($result)) { - - if (get_pref($link, 'DIGEST_ENABLE', $line['id'], false)) { - $preferred_ts = strtotime(get_pref($link, 'DIGEST_PREFERRED_TIME', $line['id'], '00:00')); - - // try to send digests within 2 hours of preferred time - if ($preferred_ts && time() >= $preferred_ts && - time() - $preferred_ts <= 7200) { - - if ($debug) print "Sending digest for UID:" . $line['id'] . " - " . $line["email"] . " ... "; - - $do_catchup = get_pref($link, 'DIGEST_CATCHUP', $line['id'], false); - - global $tz_offset; - - // reset tz_offset global to prevent tz cache clash between users - $tz_offset = -1; - - $tuple = prepare_headlines_digest($link, $line["id"], 1, $limit); - $digest = $tuple[0]; - $headlines_count = $tuple[1]; - $affected_ids = $tuple[2]; - $digest_text = $tuple[3]; - - if ($headlines_count > 0) { - - $mail = new PHPMailer(); - - $mail->PluginDir = "lib/phpmailer/"; - $mail->SetLanguage("en", "lib/phpmailer/language/"); - - $mail->CharSet = "UTF-8"; - - $mail->From = SMTP_FROM_ADDRESS; - $mail->FromName = SMTP_FROM_NAME; - $mail->AddAddress($line["email"], $line["login"]); - - if (SMTP_HOST) { - $mail->Host = SMTP_HOST; - $mail->Mailer = "smtp"; - $mail->SMTPAuth = SMTP_LOGIN != ''; - $mail->Username = SMTP_LOGIN; - $mail->Password = SMTP_PASSWORD; - } - - $mail->IsHTML(true); - $mail->Subject = DIGEST_SUBJECT; - $mail->Body = $digest; - $mail->AltBody = $digest_text; - - $rc = $mail->Send(); - - if (!$rc && $debug) print "ERROR: " . $mail->ErrorInfo; - - if ($debug) print "RC=$rc\n"; - - if ($rc && $do_catchup) { - if ($debug) print "Marking affected articles as read...\n"; - catchupArticlesById($link, $affected_ids, 0, $line["id"]); - } - } else { - if ($debug) print "No headlines\n"; - } - - db_query($link, "UPDATE ttrss_users SET last_digest_sent = NOW() - WHERE id = " . $line["id"]); - - } - } - } - - if ($debug) _debug("All done."); - - } - - function prepare_headlines_digest($link, $user_id, $days = 1, $limit = 1000) { - - require_once "lib/MiniTemplator.class.php"; - - $tpl = new MiniTemplator; - $tpl_t = new MiniTemplator; - - $tpl->readTemplateFromFile("templates/digest_template_html.txt"); - $tpl_t->readTemplateFromFile("templates/digest_template.txt"); - - $user_tz_string = get_pref($link, 'USER_TIMEZONE', $user_id); - $local_ts = convert_timestamp(time(), 'UTC', $user_tz_string); - - $tpl->setVariable('CUR_DATE', date('Y/m/d', $local_ts)); - $tpl->setVariable('CUR_TIME', date('G:i', $local_ts)); - - $tpl_t->setVariable('CUR_DATE', date('Y/m/d', $local_ts)); - $tpl_t->setVariable('CUR_TIME', date('G:i', $local_ts)); - - $affected_ids = array(); - - if (DB_TYPE == "pgsql") { - $interval_query = "ttrss_entries.date_updated > NOW() - INTERVAL '$days days'"; - } else if (DB_TYPE == "mysql") { - $interval_query = "ttrss_entries.date_updated > DATE_SUB(NOW(), INTERVAL $days DAY)"; - } - - $result = db_query($link, "SELECT ttrss_entries.title, - ttrss_feeds.title AS feed_title, - COALESCE(ttrss_feed_categories.title, '".__('Uncategorized')."') AS cat_title, - date_updated, - ttrss_user_entries.ref_id, - link, - score, - content, - ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated - FROM - ttrss_user_entries,ttrss_entries,ttrss_feeds - LEFT JOIN - ttrss_feed_categories ON (cat_id = ttrss_feed_categories.id) - WHERE - ref_id = ttrss_entries.id AND feed_id = ttrss_feeds.id - AND include_in_digest = true - AND $interval_query - AND ttrss_user_entries.owner_uid = $user_id - AND unread = true - AND score >= 0 - ORDER BY ttrss_feed_categories.title, ttrss_feeds.title, score DESC, date_updated DESC - LIMIT $limit"); - - $cur_feed_title = ""; - - $headlines_count = db_num_rows($result); - - $headlines = array(); - - while ($line = db_fetch_assoc($result)) { - array_push($headlines, $line); - } - - for ($i = 0; $i < sizeof($headlines); $i++) { - - $line = $headlines[$i]; - - array_push($affected_ids, $line["ref_id"]); - - $updated = make_local_datetime($link, $line['last_updated'], false, - $user_id); - -/* if ($line["score"] != 0) { - if ($line["score"] > 0) $line["score"] = '+' . $line["score"]; - - $line["title"] .= " (".$line['score'].")"; - } */ - - if (get_pref($link, 'ENABLE_FEED_CATS', $user_id)) { - $line['feed_title'] = $line['cat_title'] . " / " . $line['feed_title']; - } - - $tpl->setVariable('FEED_TITLE', $line["feed_title"]); - $tpl->setVariable('ARTICLE_TITLE', $line["title"]); - $tpl->setVariable('ARTICLE_LINK', $line["link"]); - $tpl->setVariable('ARTICLE_UPDATED', $updated); - $tpl->setVariable('ARTICLE_EXCERPT', - truncate_string(strip_tags($line["content"]), 300)); -// $tpl->setVariable('ARTICLE_CONTENT', -// strip_tags($article_content)); - - $tpl->addBlock('article'); - - $tpl_t->setVariable('FEED_TITLE', $line["feed_title"]); - $tpl_t->setVariable('ARTICLE_TITLE', $line["title"]); - $tpl_t->setVariable('ARTICLE_LINK', $line["link"]); - $tpl_t->setVariable('ARTICLE_UPDATED', $updated); -// $tpl_t->setVariable('ARTICLE_EXCERPT', -// truncate_string(strip_tags($line["excerpt"]), 100)); - - $tpl_t->addBlock('article'); - - if ($headlines[$i]['feed_title'] != $headlines[$i+1]['feed_title']) { - $tpl->addBlock('feed'); - $tpl_t->addBlock('feed'); - } - - } - - $tpl->addBlock('digest'); - $tpl->generateOutputToString($tmp); - - $tpl_t->addBlock('digest'); - $tpl_t->generateOutputToString($tmp_t); - - return array($tmp, $headlines_count, $affected_ids, $tmp_t); - } - function check_for_update($link) { if (CHECK_FOR_NEW_VERSION && $_SESSION['access_level'] >= 10) { $version_url = "http://tt-rss.org/version.php?ver=" . VERSION; @@ -2996,7 +2771,7 @@ WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); } else if ($cmode == 1) { db_query($link, "UPDATE ttrss_user_entries SET - published = true + published = true,last_read = NOW() WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); } else { db_query($link, "UPDATE ttrss_user_entries SET @@ -3144,17 +2919,16 @@ return true; } - function render_login_form($link, $mobile = 0) { - switch ($mobile) { + function render_login_form($link, $form_id = 0) { + switch ($form_id) { case 0: require_once "login_form.php"; break; case 1: require_once "mobile/login_form.php"; break; - case 2: - require_once "mobile/classic/login_form.php"; } + exit; } // from http://developer.apple.com/internet/safari/faq.html @@ -3169,19 +2943,19 @@ function format_warning($msg, $id = "") { global $link; return "
- $msg
"; + $msg"; } function format_notice($msg, $id = "") { global $link; return "
- $msg
"; + $msg"; } function format_error($msg, $id = "") { global $link; return "
- $msg
"; + $msg"; } function print_notice($msg) { @@ -3260,15 +3034,17 @@ //if (!$zoom_mode) { print "
0) { if ($line["comments"]) { - $comments_url = $line["comments"]; + $comments_url = htmlspecialchars($line["comments"]); } else { - $comments_url = $line["link"]; + $comments_url = htmlspecialchars($line["link"]); } $entry_comments = "$num_comments comments"; } else { if ($line["comments"] && $line["link"] != $line["comments"]) { - $entry_comments = "comments"; + $entry_comments = "comments"; } } @@ -3336,7 +3113,7 @@ "; } - $title_escaped = db_escape_string($line['title']); + $title_escaped = htmlspecialchars($line['title']); $rv['content'] .= "
" . truncate_string(strip_tags($line['title']), 15) . "
"; @@ -3361,14 +3138,14 @@ $rv['content'] .= "
$parsed_updated
"; if ($line["link"]) { - $rv['content'] .= ""; } else { - $rv['content'] .= "
" . $line["title"] . "$entry_author
"; + $rv['content'] .= "
" . $line["title"] . "$entry_author
"; } $tag_cache = $line["tag_cache"]; @@ -3403,15 +3180,10 @@ onclick=\"postOpenInNewTab(event, $id)\" alt='Zoom' title='".__('Open article in new tab')."'>"; - $button_plugins = explode(",", ARTICLE_BUTTON_PLUGINS); - - foreach ($button_plugins as $p) { - $pclass = trim("button_${p}"); + global $pluginhost; - if (class_exists($pclass)) { - $plugin = new $pclass($link); - $rv['content'] .= $plugin->render($id, $line); - } + foreach ($pluginhost->get_hooks($pluginhost::HOOK_ARTICLE_BUTTON) as $p) { + $rv['content'] .= $p->hook_article_button($line); } $rv['content'] .= ""; - $rv['content'] .= ""; + $rv['content'] .= ""; $rv['content'] .= ""; } @@ -3493,6 +3265,10 @@ } } + if ($cache_content && $line["cached_content"] != "") { + $line["content"] =& $line["cached_content"]; + } + $article_content = sanitize($link, $line["content"], false, $owner_uid, $feed_site_url); @@ -3540,7 +3316,11 @@ } function get_self_url_prefix() { - return SELF_URL_PATH; + if (strrpos(SELF_URL_PATH, "/") === strlen(SELF_URL_PATH)-1) { + return substr(SELF_URL_PATH, 0, strlen(SELF_URL_PATH)-1); + } else { + return SELF_URL_PATH; + } } function opml_publish_url($link){ @@ -3584,7 +3364,7 @@ //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH); $url_path = get_self_url_prefix() . - "/backend.php?op=pref-feeds&quiet=1&method=add&feed_url=%s"; + "/public.php?op=subscribe&feed_url=%s"; return $url_path; } // function add_feed_url @@ -3598,21 +3378,18 @@ } } // function encrypt_password - function sanitize_article_content($text) { - # we don't support CDATA sections in articles, they break our own escaping - $text = preg_replace("/\[\[CDATA/", "", $text); - $text = preg_replace("/\]\]\>/", "", $text); - return $text; - } - function load_filters($link, $feed_id, $owner_uid, $action_id = false) { $filters = array(); - $cat_id = getFeedCategory($link, $feed_id); + $cat_id = (int)getFeedCategory($link, $feed_id); $result = db_query($link, "SELECT * FROM ttrss_filters2 WHERE owner_uid = $owner_uid AND enabled = true"); + $check_cats = join(",", array_merge( + getParentCategories($link, $cat_id, $owner_uid), + array($cat_id))); + while ($line = db_fetch_assoc($result)) { $filter_id = $line["id"]; @@ -3621,7 +3398,7 @@ FROM ttrss_filters2_rules AS r, ttrss_filter_types AS t WHERE - (cat_id IS NULL OR cat_id = '$cat_id') AND + (cat_id IS NULL OR cat_id IN ($check_cats)) AND (feed_id IS NULL OR feed_id = '$feed_id') AND filter_type = t.id AND filter_id = '$filter_id'"); @@ -3701,6 +3478,12 @@ db_query($link, "SET NAMES " . MYSQL_CHARSET); } } + + global $pluginhost; + + $pluginhost = new PluginHost($link); + $pluginhost->load(PLUGINS, $pluginhost::KIND_ALL); + return true; } else { print "Unable to connect to database:" . db_last_error(); @@ -3941,24 +3724,26 @@ } } - function get_article_labels($link, $id) { + function get_article_labels($link, $id, $owner_uid = false) { $rv = array(); + if (!$owner_uid) $owner_uid = $_SESSION["uid"]; $result = db_query($link, "SELECT label_cache FROM ttrss_user_entries WHERE ref_id = '$id' AND owner_uid = " . - $_SESSION["uid"]); - - $label_cache = db_fetch_result($result, 0, "label_cache"); + $owner_uid); - if ($label_cache) { + if (db_num_rows($result) > 0) { + $label_cache = db_fetch_result($result, 0, "label_cache"); - $label_cache = json_decode($label_cache, true); + if ($label_cache) { + $label_cache = json_decode($label_cache, true); - if ($label_cache["no-labels"] == 1) - return $rv; - else - return $label_cache; + if ($label_cache["no-labels"] == 1) + return $rv; + else + return $label_cache; + } } $result = db_query($link, @@ -3966,7 +3751,7 @@ FROM ttrss_labels2, ttrss_user_labels2 WHERE id = label_id AND article_id = '$id' - AND owner_uid = ".$_SESSION["uid"] . " + AND owner_uid = ". $owner_uid . " ORDER BY caption"); while ($line = db_fetch_assoc($result)) { @@ -3976,9 +3761,9 @@ } if (count($rv) > 0) - label_update_cache($link, $id, $rv); + label_update_cache($link, $owner_uid, $id, $rv); else - label_update_cache($link, $id, array("no-labels" => 1)); + label_update_cache($link, $owner_uid, $id, array("no-labels" => 1)); return $rv; } @@ -3996,7 +3781,19 @@ } } - function label_update_cache($link, $id, $labels = false, $force = false) { + function get_all_labels($link, $owner_uid) { + $rv = array(); + + $result = db_query($link, "SELECT fg_color, bg_color, caption FROM ttrss_labels2 WHERE owner_uid = " . $owner_uid); + + while ($line = db_fetch_assoc($result)) { + array_push($rv, $line); + } + + return $rv; + } + + function label_update_cache($link, $owner_uid, $id, $labels = false, $force = false) { if ($force) label_clear_cache($link, $id); @@ -4007,7 +3804,7 @@ $labels = db_escape_string(json_encode($labels)); db_query($link, "UPDATE ttrss_user_entries SET - label_cache = '$labels' WHERE ref_id = '$id'"); + label_cache = '$labels' WHERE ref_id = '$id' AND owner_uid = '$owner_uid'"); } @@ -4423,7 +4220,7 @@ return $rv; } - function api_get_feeds($link, $cat_id, $unread_only, $limit, $offset) { + function api_get_feeds($link, $cat_id, $unread_only, $limit, $offset, $include_nested = false) { $feeds = array(); @@ -4453,7 +4250,7 @@ /* Virtual feeds */ if ($cat_id == -4 || $cat_id == -1) { - foreach (array(-1, -2, -3, -4, 0) as $i) { + foreach (array(-1, -2, -3, -4, -6, 0) as $i) { $unread = getFeedUnread($link, $i); if ($unread || !$unread_only) { @@ -4471,6 +4268,30 @@ } } + /* Child cats */ + + if ($include_nested && $cat_id) { + $result = db_query($link, "SELECT + id, title FROM ttrss_feed_categories + WHERE parent_cat = '$cat_id' AND owner_uid = " . $_SESSION["uid"] . + " ORDER BY id, title"); + + while ($line = db_fetch_assoc($result)) { + $unread = getFeedUnread($link, $line["id"], true) + + getCategoryChildrenUnread($link, $line["id"]); + + if ($unread || !$unread_only) { + $row = array( + "id" => $line["id"], + "title" => $line["title"], + "unread" => $unread, + "is_cat" => true, + ); + array_push($feeds, $row); + } + } + } + /* Real feeds */ if ($limit) { @@ -4529,11 +4350,12 @@ function api_get_headlines($link, $feed_id, $limit, $offset, $filter, $is_cat, $show_excerpt, $show_content, $view_mode, $order, $include_attachments, $since_id, - $search = "", $search_mode = "", $match_on = "") { + $search = "", $search_mode = "", $match_on = "", + $include_nested = false, $sanitize_content = true) { $qfh_ret = queryFeedHeadlines($link, $feed_id, $limit, $view_mode, $is_cat, $search, $search_mode, $match_on, - $order, $offset, 0, false, $since_id); + $order, $offset, 0, false, $since_id, $include_nested); $result = $qfh_ret[0]; $feed_title = $qfh_ret[1]; @@ -4573,7 +4395,17 @@ } if ($show_content) { - $headline_row["content"] = $line["content_preview"]; + + if ($line["cached_content"] != "") { + $line["content_preview"] =& $line["cached_content"]; + } + + if ($sanitize_content) { + $headline_row["content"] = sanitize($link, + $line["content_preview"], false, false, $line["site_url"]); + } else { + $headline_row["content"] = $line["content_preview"]; + } } // unify label output to ease parsing @@ -4583,6 +4415,11 @@ $headline_row["feed_title"] = $line["feed_title"]; + $headline_row["comments_count"] = (int)$line["num_comments"]; + $headline_row["comments_link"] = $line["comments"]; + + $headline_row["always_display_attachments"] = sql_bool_to_bool($line["always_display_enclosures"]); + array_push($headlines, $headline_row); } @@ -4703,22 +4540,13 @@ return false; } - /** - * Extracts RSS/Atom feed URLs from the given HTML URL. - * - * @param string $url HTML page URL - * - * @return array Array of feeds. Key is the full URL, value the title - */ - function get_feeds_from_html($url, $login = false, $pass = false) + function get_feeds_from_html($url, $content) { $url = fix_url($url); $baseUrl = substr($url, 0, strrpos($url, '/') + 1); libxml_use_internal_errors(true); - $content = @fetch_file_contents($url, false, $login, $pass); - $doc = new DOMDocument(); $doc->loadHTML($content); $xpath = new DOMXPath($doc); @@ -4739,23 +4567,12 @@ return $feedUrls; } - /** - * Checks if the content behind the given URL is a HTML file - * - * @param string $url URL to check - * - * @return boolean True if the URL contains HTML content - */ - function url_is_html($url, $login = false, $pass = false) { - $content = substr(fetch_file_contents($url, false, $login, $pass), 0, 1000); - - if (stripos($content, '') === false - && stripos($content, '\\1", $line); - - return $result; - } */ - function rewrite_urls($html) { libxml_use_internal_errors(true); @@ -5048,273 +4854,80 @@ return $html; } - function filter_to_sql($filter) { - $query = ""; - - $regexp_valid = preg_match('/' . $filter['reg_exp'] . '/', - $filter['reg_exp']) !== FALSE; - - if ($regexp_valid) { - - if (DB_TYPE == "pgsql") - $reg_qpart = "~"; - else - $reg_qpart = "REGEXP"; - - switch ($filter["type"]) { - case "title": - $query = "LOWER(ttrss_entries.title) $reg_qpart LOWER('". - $filter['reg_exp'] . "')"; - break; - case "content": - $query = "LOWER(ttrss_entries.content) $reg_qpart LOWER('". - $filter['reg_exp'] . "')"; - break; - case "both": - $query = "LOWER(ttrss_entries.title) $reg_qpart LOWER('". - $filter['reg_exp'] . "') OR LOWER(" . - "ttrss_entries.content) $reg_qpart LOWER('" . $filter['reg_exp'] . "')"; - break; - case "tag": - $query = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('". - $filter['reg_exp'] . "')"; - break; - case "link": - $query = "LOWER(ttrss_entries.link) $reg_qpart LOWER('". - $filter['reg_exp'] . "')"; - break; - case "date": - - if ($filter["filter_param"] == "before") - $cmp_qpart = "<"; - else - $cmp_qpart = ">="; - - $timestamp = date("Y-m-d H:N:s", strtotime($filter["reg_exp"])); - $query = "ttrss_entries.date_entered $cmp_qpart '$timestamp'"; - break; - case "author": - $query = "LOWER(ttrss_entries.author) $reg_qpart LOWER('". - $filter['reg_exp'] . "')"; - break; - } - - if ($filter["inverse"]) - $query = "NOT ($query)"; - - if ($query) { - if (DB_TYPE == "pgsql") { - $query = " ($query) AND ttrss_entries.date_entered > NOW() - INTERVAL '14 days'"; - } else { - $query = " ($query) AND ttrss_entries.date_entered > DATE_SUB(NOW(), INTERVAL 14 DAY)"; - } - $query .= " AND "; - } - - return $query; - } else { - return false; - } - } - - // Status codes: - // -1 - never connected - // 0 - no data received - // 1 - data received successfully - // 2 - did not receive valid data - // >10 - server error, code + 10 (e.g. 16 means server error 6) + function filter_to_sql($link, $filter, $owner_uid) { + $query = array(); - function get_linked_feeds($link, $instance_id = false) { - if ($instance_id) - $instance_qpart = "id = '$instance_id' AND "; + if (DB_TYPE == "pgsql") + $reg_qpart = "~"; else - $instance_qpart = ""; + $reg_qpart = "REGEXP"; - if (DB_TYPE == "pgsql") { - $date_qpart = "last_connected < NOW() - INTERVAL '6 hours'"; - } else { - $date_qpart = "last_connected < DATE_SUB(NOW(), INTERVAL 6 HOUR)"; - } + foreach ($filter["rules"] AS $rule) { + $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/', + $rule['reg_exp']) !== FALSE; - $result = db_query($link, "SELECT id, access_key, access_url FROM ttrss_linked_instances - WHERE $instance_qpart $date_qpart ORDER BY last_connected"); + if ($regexp_valid) { - while ($line = db_fetch_assoc($result)) { - $id = $line['id']; + $rule['reg_exp'] = db_escape_string($rule['reg_exp']); - _debug("Updating: " . $line['access_url'] . " ($id)"); + switch ($rule["type"]) { + case "title": + $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('". + $rule['reg_exp'] . "')"; + break; + case "content": + $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('". + $rule['reg_exp'] . "')"; + break; + case "both": + $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('". + $rule['reg_exp'] . "') OR LOWER(" . + "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')"; + break; + case "tag": + $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('". + $rule['reg_exp'] . "')"; + break; + case "link": + $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('". + $rule['reg_exp'] . "')"; + break; + case "author": + $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('". + $rule['reg_exp'] . "')"; + break; + } - $fetch_url = $line['access_url'] . '/public.php?op=fbexport'; - $post_query = 'key=' . $line['access_key']; + if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) { + $qpart .= " AND feed_id = " . db_escape_string($rule["feed_id"]); + } - $feeds = fetch_file_contents($fetch_url, false, false, false, $post_query); + if (isset($rule["cat_id"])) { - // try doing it the old way - if (!$feeds) { - $fetch_url = $line['access_url'] . '/backend.php?op=fbexport'; - $feeds = fetch_file_contents($fetch_url, false, false, false, $post_query); - } + if ($rule["cat_id"] > 0) { + $children = getChildCategories($link, $rule["cat_id"], $owner_uid); + array_push($children, $rule["cat_id"]); - if ($feeds) { - $feeds = json_decode($feeds, true); + $children = join(",", $children); - if ($feeds) { - if ($feeds['error']) { - $status = $feeds['error']['code'] + 10; + $cat_qpart = "cat_id IN ($children)"; } else { - $status = 1; - - if (count($feeds['feeds']) > 0) { - - db_query($link, "DELETE FROM ttrss_linked_feeds - WHERE instance_id = '$id'"); - - foreach ($feeds['feeds'] as $feed) { - $feed_url = db_escape_string($feed['feed_url']); - $title = db_escape_string($feed['title']); - $subscribers = db_escape_string($feed['subscribers']); - $site_url = db_escape_string($feed['site_url']); - - db_query($link, "INSERT INTO ttrss_linked_feeds - (feed_url, site_url, title, subscribers, instance_id, created, updated) - VALUES - ('$feed_url', '$site_url', '$title', '$subscribers', '$id', NOW(), NOW())"); - } - } else { - // received 0 feeds, this might indicate that - // the instance on the other hand is rebuilding feedbrowser cache - // we will try again later - - // TODO: maybe perform expiration based on updated here? - } - - _debug("Processed " . count($feeds['feeds']) . " feeds."); + $cat_qpart = "cat_id IS NULL"; } - } else { - $status = 2; - } - - } else { - $status = 0; - } - - _debug("Status: $status"); - - db_query($link, "UPDATE ttrss_linked_instances SET - last_status_out = '$status', last_connected = NOW() WHERE id = '$id'"); - - } - } - - function make_feed_browser($link, $search, $limit, $mode = 1) { - - $owner_uid = $_SESSION["uid"]; - $rv = ''; - - if ($search) { - $search_qpart = "AND (UPPER(feed_url) LIKE UPPER('%$search%') OR - UPPER(title) LIKE UPPER('%$search%'))"; - } else { - $search_qpart = ""; - } - - if ($mode == 1) { - /* $result = db_query($link, "SELECT feed_url, subscribers FROM - ttrss_feedbrowser_cache WHERE (SELECT COUNT(id) = 0 FROM ttrss_feeds AS tf - WHERE tf.feed_url = ttrss_feedbrowser_cache.feed_url - AND owner_uid = '$owner_uid') $search_qpart - ORDER BY subscribers DESC LIMIT $limit"); */ - - $result = db_query($link, "SELECT feed_url, site_url, title, SUM(subscribers) AS subscribers FROM - (SELECT feed_url, site_url, title, subscribers FROM ttrss_feedbrowser_cache UNION ALL - SELECT feed_url, site_url, title, subscribers FROM ttrss_linked_feeds) AS qqq - WHERE - (SELECT COUNT(id) = 0 FROM ttrss_feeds AS tf - WHERE tf.feed_url = qqq.feed_url - AND owner_uid = '$owner_uid') $search_qpart - GROUP BY feed_url, site_url, title ORDER BY subscribers DESC LIMIT $limit"); - - } else if ($mode == 2) { - $result = db_query($link, "SELECT *, - (SELECT COUNT(*) FROM ttrss_user_entries WHERE - orig_feed_id = ttrss_archived_feeds.id) AS articles_archived - FROM - ttrss_archived_feeds - WHERE - (SELECT COUNT(*) FROM ttrss_feeds - WHERE ttrss_feeds.feed_url = ttrss_archived_feeds.feed_url AND - owner_uid = '$owner_uid') = 0 AND - owner_uid = '$owner_uid' $search_qpart - ORDER BY id DESC LIMIT $limit"); - } - - $feedctr = 0; - - while ($line = db_fetch_assoc($result)) { - if ($mode == 1) { - - $feed_url = htmlspecialchars($line["feed_url"]); - $site_url = htmlspecialchars($line["site_url"]); - $subscribers = $line["subscribers"]; - - $check_box = ""; - - $class = ($feedctr % 2) ? "even" : "odd"; - - $site_url = " - ". - htmlspecialchars($line["title"]).""; - - $feed_url = ""; - - $rv .= "
  • $check_box $feed_url $site_url". - " ($subscribers)
  • "; - - } else if ($mode == 2) { - $feed_url = htmlspecialchars($line["feed_url"]); - $site_url = htmlspecialchars($line["site_url"]); - $title = htmlspecialchars($line["title"]); - - $check_box = ""; - - $class = ($feedctr % 2) ? "even" : "odd"; - - if ($line['articles_archived'] > 0) { - $archived = sprintf(__("%d archived articles"), $line['articles_archived']); - $archived = " ($archived)"; - } else { - $archived = ''; + $qpart .= " AND $cat_qpart"; } - $site_url = " - ". - htmlspecialchars($line["title"]).""; - - $feed_url = ""; + array_push($query, "($qpart)"); - - $rv .= "
  • ". - "$check_box $feed_url $site_url $archived
  • "; } - - ++$feedctr; } - if ($feedctr == 0) { - $rv .= "
  • ".__('No feeds found.')."

  • "; + if (count($query) > 0) { + return "(" . join($filter["match_any_rule"] ? "OR" : "AND", $query) . ")"; + } else { + return "(false)"; } - - return $rv; } if (!function_exists('gzdecode')) { @@ -5324,214 +4937,6 @@ } } - function perform_data_import($link, $filename, $owner_uid) { - - $num_imported = 0; - $num_processed = 0; - $num_feeds_created = 0; - - $doc = @DOMDocument::load($filename); - - if (!$doc) { - $contents = file_get_contents($filename); - - if ($contents) { - $data = @gzuncompress($contents); - } - - if (!$data) { - $data = @gzdecode($contents); - } - - if ($data) - $doc = DOMDocument::loadXML($data); - } - - if ($doc) { - - $xpath = new DOMXpath($doc); - - $container = $doc->firstChild; - - if ($container && $container->hasAttribute('schema-version')) { - $schema_version = $container->getAttribute('schema-version'); - - if ($schema_version != SCHEMA_VERSION) { - print "

    " .__("Could not import: incorrect schema version.") . "

    "; - return; - } - - } else { - print "

    " . __("Could not import: unrecognized document format.") . "

    "; - return; - } - - $articles = $xpath->query("//article"); - - foreach ($articles as $article_node) { - if ($article_node->childNodes) { - - $ref_id = 0; - - $article = array(); - - foreach ($article_node->childNodes as $child) { - if ($child->nodeName != 'label_cache') - $article[$child->nodeName] = db_escape_string($child->nodeValue); - else - $article[$child->nodeName] = $child->nodeValue; - } - - //print_r($article); - - if ($article['guid']) { - - ++$num_processed; - - //db_query($link, "BEGIN"); - - //print 'GUID:' . $article['guid'] . "\n"; - - $result = db_query($link, "SELECT id FROM ttrss_entries - WHERE guid = '".$article['guid']."'"); - - if (db_num_rows($result) == 0) { - - $result = db_query($link, - "INSERT INTO ttrss_entries - (title, - guid, - link, - updated, - content, - content_hash, - no_orig_date, - date_updated, - date_entered, - comments, - num_comments, - author) - VALUES - ('".$article['title']."', - '".$article['guid']."', - '".$article['link']."', - '".$article['updated']."', - '".$article['content']."', - '".sha1($article['content'])."', - false, - NOW(), - NOW(), - '', - '0', - '')"); - - $result = db_query($link, "SELECT id FROM ttrss_entries - WHERE guid = '".$article['guid']."'"); - - if (db_num_rows($result) != 0) { - $ref_id = db_fetch_result($result, 0, "id"); - } - - } else { - $ref_id = db_fetch_result($result, 0, "id"); - } - - //print "Got ref ID: $ref_id\n"; - - if ($ref_id) { - - $feed_url = $article['feed_url']; - $feed_title = $article['feed_title']; - - $feed = 'NULL'; - - if ($feed_url && $feed_title) { - $result = db_query($link, "SELECT id FROM ttrss_feeds - WHERE feed_url = '$feed_url' AND owner_uid = '$owner_uid'"); - - if (db_num_rows($result) != 0) { - $feed = db_fetch_result($result, 0, "id"); - } else { - // try autocreating feed in Uncategorized... - - $result = db_query($link, "INSERT INTO ttrss_feeds (owner_uid, - feed_url, title) VALUES ($owner_uid, '$feed_url', '$feed_title')"); - - $result = db_query($link, "SELECT id FROM ttrss_feeds - WHERE feed_url = '$feed_url' AND owner_uid = '$owner_uid'"); - - if (db_num_rows($result) != 0) { - ++$num_feeds_created; - - $feed = db_fetch_result($result, 0, "id"); - } - } - } - - if ($feed != 'NULL') - $feed_qpart = "feed_id = $feed"; - else - $feed_qpart = "feed_id IS NULL"; - - //print "$ref_id / $feed / " . $article['title'] . "\n"; - - $result = db_query($link, "SELECT int_id FROM ttrss_user_entries - WHERE ref_id = '$ref_id' AND owner_uid = '$owner_uid' AND $feed_qpart"); - - if (db_num_rows($result) == 0) { - - $marked = bool_to_sql_bool(sql_bool_to_bool($article['marked'])); - $published = bool_to_sql_bool(sql_bool_to_bool($article['published'])); - $score = (int) $article['score']; - - $tag_cache = $article['tag_cache']; - $label_cache = db_escape_string($article['label_cache']); - $note = $article['note']; - - //print "Importing " . $article['title'] . "
    "; - - ++$num_imported; - - $result = db_query($link, - "INSERT INTO ttrss_user_entries - (ref_id, owner_uid, feed_id, unread, last_read, marked, - published, score, tag_cache, label_cache, uuid, note) - VALUES ($ref_id, $owner_uid, $feed, false, - NULL, $marked, $published, $score, '$tag_cache', - '$label_cache', '', '$note')"); - - $label_cache = json_decode($label_cache, true); - - if (is_array($label_cache) && $label_cache["no-labels"] != 1) { - foreach ($label_cache as $label) { - - label_create($link, $label[1], - $label[2], $label[3], $owner_uid); - - label_add_article($link, $ref_id, $label[1], $owner_uid); - - } - } - - //db_query($link, "COMMIT"); - } - } - } - } - } - - print "

    " . - T_sprintf("Finished: %d articles processed, %d imported, %d feeds created.", - $num_processed, $num_imported, $num_feeds_created) . - "

    "; - - } else { - - print "

    " . __("Could not load XML document.") . "

    "; - - } - } - function get_random_bytes($length) { if (function_exists('openssl_random_pseudo_bytes')) { return openssl_random_pseudo_bytes($length); @@ -5582,4 +4987,94 @@ } + function create_published_article($link, $title, $url, $content, $labels_str, + $owner_uid) { + + $guid = sha1($url . $owner_uid); // include owner_uid to prevent global GUID clash + $content_hash = sha1($content); + + if ($labels_str != "") { + $labels = explode(",", $labels_str); + } else { + $labels = array(); + } + + $rc = false; + + if (!$title) $title = $url; + if (!$title && !$url) return false; + + if (filter_var($url, FILTER_VALIDATE_URL) === FALSE) return false; + + db_query($link, "BEGIN"); + + // only check for our user data here, others might have shared this with different content etc + $result = db_query($link, "SELECT id FROM ttrss_entries, ttrss_user_entries WHERE + link = '$url' AND ref_id = id AND owner_uid = '$owner_uid' LIMIT 1"); + + if (db_num_rows($result) != 0) { + $ref_id = db_fetch_result($result, 0, "id"); + + $result = db_query($link, "SELECT int_id FROM ttrss_user_entries WHERE + ref_id = '$ref_id' AND owner_uid = '$owner_uid' LIMIT 1"); + + if (db_num_rows($result) != 0) { + $int_id = db_fetch_result($result, 0, "int_id"); + + db_query($link, "UPDATE ttrss_entries SET + content = '$content', content_hash = '$content_hash' WHERE id = '$ref_id'"); + + db_query($link, "UPDATE ttrss_user_entries SET published = true WHERE + int_id = '$int_id' AND owner_uid = '$owner_uid'"); + } else { + + db_query($link, "INSERT INTO ttrss_user_entries + (ref_id, uuid, feed_id, orig_feed_id, owner_uid, published, tag_cache, label_cache, last_read, note, unread) + VALUES + ('$ref_id', '', NULL, NULL, $owner_uid, true, '', '', NOW(), '', false)"); + } + + if (count($labels) != 0) { + foreach ($labels as $label) { + label_add_article($link, $ref_id, trim($label), $owner_uid); + } + } + + $rc = true; + + } else { + $result = db_query($link, "INSERT INTO ttrss_entries + (title, guid, link, updated, content, content_hash, date_entered, date_updated) + VALUES + ('$title', '$guid', '$url', NOW(), '$content', '$content_hash', NOW(), NOW())"); + + $result = db_query($link, "SELECT id FROM ttrss_entries WHERE guid = '$guid'"); + + if (db_num_rows($result) != 0) { + $ref_id = db_fetch_result($result, 0, "id"); + + db_query($link, "INSERT INTO ttrss_user_entries + (ref_id, uuid, feed_id, orig_feed_id, owner_uid, published, tag_cache, label_cache, last_read, note, unread) + VALUES + ('$ref_id', '', NULL, NULL, $owner_uid, true, '', '', NOW(), '', false)"); + + if (count($labels) != 0) { + foreach ($labels as $label) { + label_add_article($link, $ref_id, trim($label), $owner_uid); + } + } + + $rc = true; + } + } + + db_query($link, "COMMIT"); + + return $rc; + } + + function implements_interface($class, $interface) { + return in_array($interface, class_implements($class)); + } + ?>