diff options
Diffstat (limited to 'classes')
40 files changed, 4391 insertions, 2155 deletions
diff --git a/classes/.htaccess b/classes/.htaccess new file mode 100644 index 000000000..93169e4eb --- /dev/null +++ b/classes/.htaccess @@ -0,0 +1,2 @@ +Order deny,allow +Deny from all diff --git a/classes/api.php b/classes/api.php index a23f20ae2..f5e4a0c5c 100644 --- a/classes/api.php +++ b/classes/api.php @@ -2,7 +2,7 @@ class API extends Handler { - const API_LEVEL = 4; + const API_LEVEL = 6; const STATUS_OK = 0; const STATUS_ERR = 1; @@ -14,12 +14,12 @@ class API extends Handler { header("Content-Type: text/json"); if (!$_SESSION["uid"] && $method != "login" && $method != "isloggedin") { - print $this->wrap(self::STATUS_ERR, array("error" => 'NOT_LOGGED_IN')); + $this->wrap(self::STATUS_ERR, array("error" => 'NOT_LOGGED_IN')); return false; } - if ($_SESSION["uid"] && $method != "logout" && !get_pref($this->link, 'ENABLE_API_ACCESS')) { - print $this->wrap(self::STATUS_ERR, array("error" => 'API_DISABLED')); + if ($_SESSION["uid"] && $method != "logout" && !get_pref('ENABLE_API_ACCESS')) { + $this->wrap(self::STATUS_ERR, array("error" => 'API_DISABLED')); return false; } @@ -38,90 +38,94 @@ class API extends Handler { function getVersion() { $rv = array("version" => VERSION); - print $this->wrap(self::STATUS_OK, $rv); + $this->wrap(self::STATUS_OK, $rv); } function getApiLevel() { $rv = array("level" => self::API_LEVEL); - print $this->wrap(self::STATUS_OK, $rv); + $this->wrap(self::STATUS_OK, $rv); } function login() { - $login = db_escape_string($_REQUEST["user"]); + @session_destroy(); + @session_start(); + + $login = $this->dbh->escape_string($_REQUEST["user"]); $password = $_REQUEST["password"]; $password_base64 = base64_decode($_REQUEST["password"]); if (SINGLE_USER_MODE) $login = "admin"; - $result = db_query($this->link, "SELECT id FROM ttrss_users WHERE login = '$login'"); + $result = $this->dbh->query("SELECT id FROM ttrss_users WHERE login = '$login'"); - if (db_num_rows($result) != 0) { - $uid = db_fetch_result($result, 0, "id"); + if ($this->dbh->num_rows($result) != 0) { + $uid = $this->dbh->fetch_result($result, 0, "id"); } else { $uid = 0; } if (!$uid) { - print $this->wrap(self::STATUS_ERR, array("error" => "LOGIN_ERROR")); + $this->wrap(self::STATUS_ERR, array("error" => "LOGIN_ERROR")); return; } - if (get_pref($this->link, "ENABLE_API_ACCESS", $uid)) { - if (authenticate_user($this->link, $login, $password)) { // try login with normal password - print $this->wrap(self::STATUS_OK, array("session_id" => session_id(), + if (get_pref("ENABLE_API_ACCESS", $uid)) { + if (authenticate_user($login, $password)) { // try login with normal password + $this->wrap(self::STATUS_OK, array("session_id" => session_id(), "api_level" => self::API_LEVEL)); - } else if (authenticate_user($this->link, $login, $password_base64)) { // else try with base64_decoded password - print $this->wrap(self::STATUS_OK, array("session_id" => session_id(), + } else if (authenticate_user($login, $password_base64)) { // else try with base64_decoded password + $this->wrap(self::STATUS_OK, array("session_id" => session_id(), "api_level" => self::API_LEVEL)); } else { // else we are not logged in - print $this->wrap(self::STATUS_ERR, array("error" => "LOGIN_ERROR")); + $this->wrap(self::STATUS_ERR, array("error" => "LOGIN_ERROR")); } } else { - print $this->wrap(self::STATUS_ERR, array("error" => "API_DISABLED")); + $this->wrap(self::STATUS_ERR, array("error" => "API_DISABLED")); } } function logout() { logout_user(); - print $this->wrap(self::STATUS_OK, array("status" => "OK")); + $this->wrap(self::STATUS_OK, array("status" => "OK")); } function isLoggedIn() { - print $this->wrap(self::STATUS_OK, array("status" => $_SESSION["uid"] != '')); + $this->wrap(self::STATUS_OK, array("status" => $_SESSION["uid"] != '')); } function getUnread() { - $feed_id = db_escape_string($_REQUEST["feed_id"]); - $is_cat = db_escape_string($_REQUEST["is_cat"]); + $feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]); + $is_cat = $this->dbh->escape_string($_REQUEST["is_cat"]); if ($feed_id) { - print $this->wrap(self::STATUS_OK, array("unread" => getFeedUnread($this->link, $feed_id, $is_cat))); + $this->wrap(self::STATUS_OK, array("unread" => getFeedUnread($feed_id, $is_cat))); } else { - print $this->wrap(self::STATUS_OK, array("unread" => getGlobalUnread($this->link))); + $this->wrap(self::STATUS_OK, array("unread" => getGlobalUnread())); } } /* Method added for ttrss-reader for Android */ function getCounters() { - print $this->wrap(self::STATUS_OK, getAllCounters($this->link)); + $this->wrap(self::STATUS_OK, getAllCounters()); } function getFeeds() { - $cat_id = db_escape_string($_REQUEST["cat_id"]); - $unread_only = (bool)db_escape_string($_REQUEST["unread_only"]); - $limit = (int) db_escape_string($_REQUEST["limit"]); - $offset = (int) db_escape_string($_REQUEST["offset"]); - $include_nested = (bool)db_escape_string($_REQUEST["include_nested"]); + $cat_id = $this->dbh->escape_string($_REQUEST["cat_id"]); + $unread_only = sql_bool_to_bool($_REQUEST["unread_only"]); + $limit = (int) $this->dbh->escape_string($_REQUEST["limit"]); + $offset = (int) $this->dbh->escape_string($_REQUEST["offset"]); + $include_nested = sql_bool_to_bool($_REQUEST["include_nested"]); - $feeds = $this->api_get_feeds($this->link, $cat_id, $unread_only, $limit, $offset, $include_nested); + $feeds = $this->api_get_feeds($cat_id, $unread_only, $limit, $offset, $include_nested); - print $this->wrap(self::STATUS_OK, $feeds); + $this->wrap(self::STATUS_OK, $feeds); } function getCategories() { - $unread_only = (bool)db_escape_string($_REQUEST["unread_only"]); - $enable_nested = (bool)db_escape_string($_REQUEST["enable_nested"]); + $unread_only = sql_bool_to_bool($_REQUEST["unread_only"]); + $enable_nested = sql_bool_to_bool($_REQUEST["enable_nested"]); + $include_empty = sql_bool_to_bool($_REQUEST['include_empty']); // TODO do not return empty categories, return Uncategorized and standard virtual cats @@ -130,22 +134,25 @@ class API extends Handler { else $nested_qpart = "true"; - $result = db_query($this->link, "SELECT + $result = $this->dbh->query("SELECT id, title, order_id, (SELECT COUNT(id) FROM ttrss_feeds WHERE - ttrss_feed_categories.id IS NOT NULL AND cat_id = ttrss_feed_categories.id) AS num_feeds + ttrss_feed_categories.id IS NOT NULL AND cat_id = ttrss_feed_categories.id) AS num_feeds, + (SELECT COUNT(id) FROM + ttrss_feed_categories AS c2 WHERE + c2.parent_cat = ttrss_feed_categories.id) AS num_cats FROM ttrss_feed_categories WHERE $nested_qpart AND owner_uid = " . $_SESSION["uid"]); $cats = array(); - while ($line = db_fetch_assoc($result)) { - if ($line["num_feeds"] > 0) { - $unread = getFeedUnread($this->link, $line["id"], true); + while ($line = $this->dbh->fetch_assoc($result)) { + if ($include_empty || $line["num_feeds"] > 0 || $line["num_cats"] > 0) { + $unread = getFeedUnread($line["id"], true); if ($enable_nested) - $unread += getCategoryChildrenUnread($this->link, $line["id"]); + $unread += getCategoryChildrenUnread($line["id"]); if ($unread || !$unread_only) { array_push($cats, array("id" => $line["id"], @@ -158,60 +165,71 @@ class API extends Handler { } foreach (array(-2,-1,0) as $cat_id) { - $unread = getFeedUnread($this->link, $cat_id, true); + if ($include_empty || !$this->isCategoryEmpty($cat_id)) { + $unread = getFeedUnread($cat_id, true); - if ($unread || !$unread_only) { - array_push($cats, array("id" => $cat_id, - "title" => getCategoryTitle($this->link, $cat_id), - "unread" => $unread)); + if ($unread || !$unread_only) { + array_push($cats, array("id" => $cat_id, + "title" => getCategoryTitle($cat_id), + "unread" => $unread)); + } } } - print $this->wrap(self::STATUS_OK, $cats); + $this->wrap(self::STATUS_OK, $cats); } function getHeadlines() { - $feed_id = db_escape_string($_REQUEST["feed_id"]); + $feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]); if ($feed_id != "") { - $limit = (int)db_escape_string($_REQUEST["limit"]); + $limit = (int)$this->dbh->escape_string($_REQUEST["limit"]); - if (!$limit || $limit >= 60) $limit = 60; + if (!$limit || $limit >= 200) $limit = 200; - $offset = (int)db_escape_string($_REQUEST["skip"]); - $filter = db_escape_string($_REQUEST["filter"]); - $is_cat = (bool)db_escape_string($_REQUEST["is_cat"]); - $show_excerpt = (bool)db_escape_string($_REQUEST["show_excerpt"]); - $show_content = (bool)db_escape_string($_REQUEST["show_content"]); + $offset = (int)$this->dbh->escape_string($_REQUEST["skip"]); + $filter = $this->dbh->escape_string($_REQUEST["filter"]); + $is_cat = sql_bool_to_bool($_REQUEST["is_cat"]); + $show_excerpt = sql_bool_to_bool($_REQUEST["show_excerpt"]); + $show_content = sql_bool_to_bool($_REQUEST["show_content"]); /* all_articles, unread, adaptive, marked, updated */ - $view_mode = db_escape_string($_REQUEST["view_mode"]); - $include_attachments = (bool)db_escape_string($_REQUEST["include_attachments"]); - $since_id = (int)db_escape_string($_REQUEST["since_id"]); - $include_nested = (bool)db_escape_string($_REQUEST["include_nested"]); + $view_mode = $this->dbh->escape_string($_REQUEST["view_mode"]); + $include_attachments = sql_bool_to_bool($_REQUEST["include_attachments"]); + $since_id = (int)$this->dbh->escape_string($_REQUEST["since_id"]); + $include_nested = sql_bool_to_bool($_REQUEST["include_nested"]); $sanitize_content = true; + $override_order = false; + switch ($_REQUEST["order_by"]) { + case "date_reverse": + $override_order = "date_entered, updated"; + break; + case "feed_dates": + $override_order = "updated DESC"; + break; + } + /* do not rely on params below */ - $search = db_escape_string($_REQUEST["search"]); - $search_mode = db_escape_string($_REQUEST["search_mode"]); - $match_on = db_escape_string($_REQUEST["match_on"]); + $search = $this->dbh->escape_string($_REQUEST["search"]); + $search_mode = $this->dbh->escape_string($_REQUEST["search_mode"]); - $headlines = $this->api_get_headlines($this->link, $feed_id, $limit, $offset, - $filter, $is_cat, $show_excerpt, $show_content, $view_mode, false, - $include_attachments, $since_id, $search, $search_mode, $match_on, + $headlines = $this->api_get_headlines($feed_id, $limit, $offset, + $filter, $is_cat, $show_excerpt, $show_content, $view_mode, $override_order, + $include_attachments, $since_id, $search, $search_mode, $include_nested, $sanitize_content); - print $this->wrap(self::STATUS_OK, $headlines); + $this->wrap(self::STATUS_OK, $headlines); } else { - print $this->wrap(self::STATUS_ERR, array("error" => 'INCORRECT_USAGE')); + $this->wrap(self::STATUS_ERR, array("error" => 'INCORRECT_USAGE')); } } function updateArticle() { - $article_ids = array_filter(explode(",", db_escape_string($_REQUEST["article_ids"])), is_numeric); - $mode = (int) db_escape_string($_REQUEST["mode"]); - $data = db_escape_string($_REQUEST["data"]); - $field_raw = (int)db_escape_string($_REQUEST["field"]); + $article_ids = array_filter(explode(",", $this->dbh->escape_string($_REQUEST["article_ids"])), is_numeric); + $mode = (int) $this->dbh->escape_string($_REQUEST["mode"]); + $data = $this->dbh->escape_string($_REQUEST["data"]); + $field_raw = (int)$this->dbh->escape_string($_REQUEST["field"]); $field = ""; $set_to = ""; @@ -219,12 +237,15 @@ class API extends Handler { switch ($field_raw) { case 0: $field = "marked"; + $additional_fields = ",last_marked = NOW()"; break; case 1: $field = "published"; + $additional_fields = ",last_published = NOW()"; break; case 2: $field = "unread"; + $additional_fields = ",last_read = NOW()"; break; case 3: $field = "note"; @@ -248,62 +269,66 @@ class API extends Handler { $article_ids = join(", ", $article_ids); - if ($field == "unread") { - $result = db_query($this->link, "UPDATE ttrss_user_entries SET $field = $set_to, - last_read = NOW() - WHERE ref_id IN ($article_ids) AND owner_uid = " . $_SESSION["uid"]); - } else { - $result = db_query($this->link, "UPDATE ttrss_user_entries SET $field = $set_to - WHERE ref_id IN ($article_ids) AND owner_uid = " . $_SESSION["uid"]); - } + $result = $this->dbh->query("UPDATE ttrss_user_entries SET $field = $set_to $additional_fields WHERE ref_id IN ($article_ids) AND owner_uid = " . $_SESSION["uid"]); - $num_updated = db_affected_rows($this->link, $result); + $num_updated = $this->dbh->affected_rows($result); if ($num_updated > 0 && $field == "unread") { - $result = db_query($this->link, "SELECT DISTINCT feed_id FROM ttrss_user_entries + $result = $this->dbh->query("SELECT DISTINCT feed_id FROM ttrss_user_entries WHERE ref_id IN ($article_ids)"); - while ($line = db_fetch_assoc($result)) { - ccache_update($this->link, $line["feed_id"], $_SESSION["uid"]); + while ($line = $this->dbh->fetch_assoc($result)) { + ccache_update($line["feed_id"], $_SESSION["uid"]); } } - print $this->wrap(self::STATUS_OK, array("status" => "OK", + if ($num_updated > 0 && $field == "published") { + if (PUBSUBHUBBUB_HUB) { + $rss_link = get_self_url_prefix() . + "/public.php?op=rss&id=-2&key=" . + get_feed_access_key(-2, false); + + $p = new Publisher(PUBSUBHUBBUB_HUB); + $pubsub_result = $p->publish_update($rss_link); + } + } + + $this->wrap(self::STATUS_OK, array("status" => "OK", "updated" => $num_updated)); } else { - print $this->wrap(self::STATUS_ERR, array("error" => 'INCORRECT_USAGE')); + $this->wrap(self::STATUS_ERR, array("error" => 'INCORRECT_USAGE')); } } function getArticle() { - $article_id = join(",", array_filter(explode(",", db_escape_string($_REQUEST["article_id"])), is_numeric)); + $article_id = join(",", array_filter(explode(",", $this->dbh->escape_string($_REQUEST["article_id"])), is_numeric)); $query = "SELECT id,title,link,content,cached_content,feed_id,comments,int_id, - marked,unread,published, + marked,unread,published,score, ".SUBSTRING_FOR_DATE."(updated,1,16) as updated, - author + author,(SELECT title FROM ttrss_feeds WHERE id = feed_id) AS feed_title FROM ttrss_entries,ttrss_user_entries WHERE id IN ($article_id) AND ref_id = id AND owner_uid = " . $_SESSION["uid"] ; - $result = db_query($this->link, $query); + $result = $this->dbh->query($query); $articles = array(); - if (db_num_rows($result) != 0) { + if ($this->dbh->num_rows($result) != 0) { - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { - $attachments = get_article_enclosures($this->link, $line['id']); + $attachments = get_article_enclosures($line['id']); $article = array( "id" => $line["id"], "title" => $line["title"], "link" => $line["link"], - "labels" => get_article_labels($this->link, $line['id']), + "labels" => get_article_labels($line['id']), "unread" => sql_bool_to_bool($line["unread"]), "marked" => sql_bool_to_bool($line["marked"]), "published" => sql_bool_to_bool($line["published"]), @@ -312,15 +337,22 @@ class API extends Handler { "updated" => (int) strtotime($line["updated"]), "content" => $line["cached_content"] != "" ? $line["cached_content"] : $line["content"], "feed_id" => $line["feed_id"], - "attachments" => $attachments + "attachments" => $attachments, + "score" => (int)$line["score"], + "feed_title" => $line["feed_title"] ); + foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE_API) as $p) { + $article = $p->hook_render_article_api(array("article" => $article)); + } + + array_push($articles, $article); } } - print $this->wrap(self::STATUS_OK, $articles); + $this->wrap(self::STATUS_OK, $articles); } @@ -331,56 +363,58 @@ class API extends Handler { $config["daemon_is_running"] = file_is_locked("update_daemon.lock"); - $result = db_query($this->link, "SELECT COUNT(*) AS cf FROM + $result = $this->dbh->query("SELECT COUNT(*) AS cf FROM ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]); - $num_feeds = db_fetch_result($result, 0, "cf"); + $num_feeds = $this->dbh->fetch_result($result, 0, "cf"); $config["num_feeds"] = (int)$num_feeds; - print $this->wrap(self::STATUS_OK, $config); + $this->wrap(self::STATUS_OK, $config); } function updateFeed() { - $feed_id = db_escape_string($_REQUEST["feed_id"]); + require_once "include/rssfuncs.php"; + + $feed_id = (int) $this->dbh->escape_string($_REQUEST["feed_id"]); - update_rss_feed($this->link, $feed_id, true); + update_rss_feed($feed_id, true); - print $this->wrap(self::STATUS_OK, array("status" => "OK")); + $this->wrap(self::STATUS_OK, array("status" => "OK")); } function catchupFeed() { - $feed_id = db_escape_string($_REQUEST["feed_id"]); - $is_cat = db_escape_string($_REQUEST["is_cat"]); + $feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]); + $is_cat = $this->dbh->escape_string($_REQUEST["is_cat"]); - catchup_feed($this->link, $feed_id, $is_cat); + catchup_feed($feed_id, $is_cat); - print $this->wrap(self::STATUS_OK, array("status" => "OK")); + $this->wrap(self::STATUS_OK, array("status" => "OK")); } function getPref() { - $pref_name = db_escape_string($_REQUEST["pref_name"]); + $pref_name = $this->dbh->escape_string($_REQUEST["pref_name"]); - print $this->wrap(self::STATUS_OK, array("value" => get_pref($this->link, $pref_name))); + $this->wrap(self::STATUS_OK, array("value" => get_pref($pref_name))); } function getLabels() { - //$article_ids = array_filter(explode(",", db_escape_string($_REQUEST["article_ids"])), is_numeric); + //$article_ids = array_filter(explode(",", $this->dbh->escape_string($_REQUEST["article_ids"])), is_numeric); $article_id = (int)$_REQUEST['article_id']; $rv = array(); - $result = db_query($this->link, "SELECT id, caption, fg_color, bg_color + $result = $this->dbh->query("SELECT id, caption, fg_color, bg_color FROM ttrss_labels2 WHERE owner_uid = '".$_SESSION['uid']."' ORDER BY caption"); if ($article_id) - $article_labels = get_article_labels($this->link, $article_id); + $article_labels = get_article_labels($article_id); else $article_labels = array(); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $checked = false; foreach ($article_labels as $al) { @@ -398,16 +432,16 @@ class API extends Handler { "checked" => $checked)); } - print $this->wrap(self::STATUS_OK, $rv); + $this->wrap(self::STATUS_OK, $rv); } function setArticleLabel() { - $article_ids = array_filter(explode(",", db_escape_string($_REQUEST["article_ids"])), is_numeric); - $label_id = (int) db_escape_string($_REQUEST['label_id']); - $assign = (bool) db_escape_string($_REQUEST['assign']) == "true"; + $article_ids = array_filter(explode(",", $this->dbh->escape_string($_REQUEST["article_ids"])), is_numeric); + $label_id = (int) $this->dbh->escape_string($_REQUEST['label_id']); + $assign = (bool) $this->dbh->escape_string($_REQUEST['assign']) == "true"; - $label = db_escape_string(label_find_caption($this->link, + $label = $this->dbh->escape_string(label_find_caption( $label_id, $_SESSION["uid"])); $num_updated = 0; @@ -417,44 +451,53 @@ class API extends Handler { foreach ($article_ids as $id) { if ($assign) - label_add_article($this->link, $id, $label, $_SESSION["uid"]); + label_add_article($id, $label, $_SESSION["uid"]); else - label_remove_article($this->link, $id, $label, $_SESSION["uid"]); + label_remove_article($id, $label, $_SESSION["uid"]); ++$num_updated; } } - print $this->wrap(self::STATUS_OK, array("status" => "OK", + $this->wrap(self::STATUS_OK, array("status" => "OK", "updated" => $num_updated)); } - function index() { - print $this->wrap(self::STATUS_ERR, array("error" => 'UNKNOWN_METHOD')); + function index($method) { + $plugin = PluginHost::getInstance()->get_api_method(strtolower($method)); + + if ($plugin && method_exists($plugin, $method)) { + $reply = $plugin->$method(); + + $this->wrap($reply[0], $reply[1]); + + } else { + $this->wrap(self::STATUS_ERR, array("error" => 'UNKNOWN_METHOD', "method" => $method)); + } } function shareToPublished() { - $title = db_escape_string(strip_tags($_REQUEST["title"])); - $url = db_escape_string(strip_tags($_REQUEST["url"])); - $content = db_escape_string(strip_tags($_REQUEST["content"])); + $title = $this->dbh->escape_string(strip_tags($_REQUEST["title"])); + $url = $this->dbh->escape_string(strip_tags($_REQUEST["url"])); + $content = $this->dbh->escape_string(strip_tags($_REQUEST["content"])); - if (Article::create_published_article($this->link, $title, $url, $content, "", $_SESSION["uid"])) { - print $this->wrap(self::STATUS_OK, array("status" => 'OK')); + if (Article::create_published_article($title, $url, $content, "", $_SESSION["uid"])) { + $this->wrap(self::STATUS_OK, array("status" => 'OK')); } else { - print $this->wrap(self::STATUS_ERR, array("error" => 'Publishing failed')); + $this->wrap(self::STATUS_ERR, array("error" => 'Publishing failed')); } } - static function api_get_feeds($link, $cat_id, $unread_only, $limit, $offset, $include_nested = false) { + static function api_get_feeds($cat_id, $unread_only, $limit, $offset, $include_nested = false) { $feeds = array(); /* Labels */ if ($cat_id == -4 || $cat_id == -2) { - $counters = getLabelCounters($link, true); + $counters = getLabelCounters(true); foreach (array_values($counters) as $cv) { @@ -478,10 +521,10 @@ class API extends Handler { if ($cat_id == -4 || $cat_id == -1) { foreach (array(-1, -2, -3, -4, -6, 0) as $i) { - $unread = getFeedUnread($link, $i); + $unread = getFeedUnread($i); if ($unread || !$unread_only) { - $title = getFeedTitle($link, $i); + $title = getFeedTitle($i); $row = array( "id" => $i, @@ -498,14 +541,14 @@ class API extends Handler { /* Child cats */ if ($include_nested && $cat_id) { - $result = db_query($link, "SELECT + $result = db_query("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"]); + $unread = getFeedUnread($line["id"], true) + + getCategoryChildrenUnread($line["id"]); if ($unread || !$unread_only) { $row = array( @@ -528,7 +571,7 @@ class API extends Handler { } if ($cat_id == -4 || $cat_id == -3) { - $result = db_query($link, "SELECT + $result = db_query("SELECT id, feed_url, cat_id, title, order_id, ". SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated FROM ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"] . @@ -540,7 +583,7 @@ class API extends Handler { else $cat_qpart = "cat_id IS NULL"; - $result = db_query($link, "SELECT + $result = db_query("SELECT id, feed_url, cat_id, title, order_id, ". SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated FROM ttrss_feeds WHERE @@ -550,7 +593,7 @@ class API extends Handler { while ($line = db_fetch_assoc($result)) { - $unread = getFeedUnread($link, $line["id"]); + $unread = getFeedUnread($line["id"]); $has_icon = feed_has_icon($line['id']); @@ -574,14 +617,14 @@ class API extends Handler { return $feeds; } - static function api_get_headlines($link, $feed_id, $limit, $offset, + static function api_get_headlines($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 = "", $include_nested = false, $sanitize_content = true) { - $qfh_ret = queryFeedHeadlines($link, $feed_id, $limit, - $view_mode, $is_cat, $search, $search_mode, $match_on, + $qfh_ret = queryFeedHeadlines($feed_id, $limit, + $view_mode, $is_cat, $search, $search_mode, $order, $offset, 0, false, $since_id, $include_nested); $result = $qfh_ret[0]; @@ -596,8 +639,8 @@ class API extends Handler { $tags = explode(",", $line["tag_cache"]); $labels = json_decode($line["label_cache"], true); - //if (!$tags) $tags = get_article_tags($link, $line["id"]); - //if (!$labels) $labels = get_article_labels($link, $line["id"]); + //if (!$tags) $tags = get_article_tags($line["id"]); + //if (!$labels) $labels = get_article_labels($line["id"]); $headline_row = array( "id" => (int)$line["id"], @@ -613,7 +656,7 @@ class API extends Handler { ); if ($include_attachments) - $headline_row['attachments'] = get_article_enclosures($link, + $headline_row['attachments'] = get_article_enclosures( $line['id']); if ($show_excerpt) { @@ -628,8 +671,10 @@ class API extends Handler { } if ($sanitize_content) { - $headline_row["content"] = sanitize($link, - $line["content_preview"], false, false, $line["site_url"]); + $headline_row["content"] = sanitize( + $line["content_preview"], + sql_bool_to_bool($line['hide_images']), + false, $line["site_url"]); } else { $headline_row["content"] = $line["content_preview"]; } @@ -640,19 +685,95 @@ class API extends Handler { $headline_row["labels"] = $labels; - $headline_row["feed_title"] = $line["feed_title"]; + $headline_row["feed_title"] = $line["feed_title"] ? $line["feed_title"] : + $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"]); + $headline_row["author"] = $line["author"]; + $headline_row["score"] = (int)$line["score"]; + + foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE_API) as $p) { + $headline_row = $p->hook_render_article_api(array("headline" => $headline_row)); + } + array_push($headlines, $headline_row); } return $headlines; } + function unsubscribeFeed() { + $feed_id = (int) $this->dbh->escape_string($_REQUEST["feed_id"]); + + $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE + id = '$feed_id' AND owner_uid = ".$_SESSION["uid"]); + + if ($this->dbh->num_rows($result) != 0) { + Pref_Feeds::remove_feed($feed_id, $_SESSION["uid"]); + $this->wrap(self::STATUS_OK, array("status" => "OK")); + } else { + $this->wrap(self::STATUS_ERR, array("error" => "FEED_NOT_FOUND")); + } + } + + function subscribeToFeed() { + $feed_url = $this->dbh->escape_string($_REQUEST["feed_url"]); + $category_id = (int) $this->dbh->escape_string($_REQUEST["category_id"]); + $login = $this->dbh->escape_string($_REQUEST["login"]); + $password = $this->dbh->escape_string($_REQUEST["password"]); + + if ($feed_url) { + $rc = subscribe_to_feed($feed_url, $category_id, $login, $password); + + $this->wrap(self::STATUS_OK, array("status" => $rc)); + } else { + $this->wrap(self::STATUS_ERR, array("error" => 'INCORRECT_USAGE')); + } + } + + function getFeedTree() { + $include_empty = sql_bool_to_bool($_REQUEST['include_empty']); + + $pf = new Pref_Feeds($_REQUEST); + + $_REQUEST['mode'] = 2; + $_REQUEST['force_show_empty'] = $include_empty; + + if ($pf){ + $data = $pf->makefeedtree(); + $this->wrap(self::STATUS_OK, array("categories" => $data)); + } else { + $this->wrap(self::STATUS_ERR, array("error" => + 'UNABLE_TO_INSTANTIATE_OBJECT')); + } + + } + + // only works for labels or uncategorized for the time being + private function isCategoryEmpty($id) { + + if ($id == -2) { + $result = $this->dbh->query("SELECT COUNT(*) AS count FROM ttrss_labels2 + WHERE owner_uid = " . $_SESSION["uid"]); + + return $this->dbh->fetch_result($result, 0, "count") == 0; + + } else if ($id == 0) { + $result = $this->dbh->query("SELECT COUNT(*) AS count FROM ttrss_feeds + WHERE cat_id IS NULL AND owner_uid = " . $_SESSION["uid"]); + + return $this->dbh->fetch_result($result, 0, "count") == 0; + + } + + return false; + } + + } ?> diff --git a/classes/article.php b/classes/article.php index 2f49b1827..e9f86f298 100644 --- a/classes/article.php +++ b/classes/article.php @@ -2,20 +2,20 @@ class Article extends Handler_Protected { function csrf_ignore($method) { - $csrf_ignored = array("redirect"); + $csrf_ignored = array("redirect", "editarticletags"); return array_search($method, $csrf_ignored) !== false; } function redirect() { - $id = db_escape_string($_REQUEST['id']); + $id = $this->dbh->escape_string($_REQUEST['id']); - $result = db_query($this->link, "SELECT link FROM ttrss_entries, ttrss_user_entries + $result = $this->dbh->query("SELECT link FROM ttrss_entries, ttrss_user_entries WHERE id = '$id' AND id = ref_id AND owner_uid = '".$_SESSION['uid']."' LIMIT 1"); - if (db_num_rows($result) == 1) { - $article_url = db_fetch_result($result, 0, 'link'); + if ($this->dbh->num_rows($result) == 1) { + $article_url = $this->dbh->fetch_result($result, 0, 'link'); $article_url = str_replace("\n", "", $article_url); header("Location: $article_url"); @@ -27,10 +27,10 @@ class Article extends Handler_Protected { } function view() { - $id = db_escape_string($_REQUEST["id"]); - $cids = explode(",", db_escape_string($_REQUEST["cids"])); - $mode = db_escape_string($_REQUEST["mode"]); - $omode = db_escape_string($_REQUEST["omode"]); + $id = $this->dbh->escape_string($_REQUEST["id"]); + $cids = explode(",", $this->dbh->escape_string($_REQUEST["cids"])); + $mode = $this->dbh->escape_string($_REQUEST["mode"]); + $omode = $this->dbh->escape_string($_REQUEST["omode"]); // in prefetch mode we only output requested cids, main article // just gets marked as read (it already exists in client cache) @@ -38,26 +38,26 @@ class Article extends Handler_Protected { $articles = array(); if ($mode == "") { - array_push($articles, format_article($this->link, $id, false)); + array_push($articles, format_article($id, false)); } else if ($mode == "zoom") { - array_push($articles, format_article($this->link, $id, true, true)); + array_push($articles, format_article($id, true, true)); } else if ($mode == "raw") { if ($_REQUEST['html']) { header("Content-Type: text/html"); print '<link rel="stylesheet" type="text/css" href="tt-rss.css"/>'; } - $article = format_article($this->link, $id, false); + $article = format_article($id, false); print $article['content']; return; } - $this->catchupArticleById($this->link, $id, 0); + $this->catchupArticleById($id, 0); if (!$_SESSION["bw_limit"]) { foreach ($cids as $cid) { if ($cid) { - array_push($articles, format_article($this->link, $cid, false, false)); + array_push($articles, format_article($cid, false, false)); } } } @@ -65,30 +65,30 @@ class Article extends Handler_Protected { print json_encode($articles); } - private function catchupArticleById($link, $id, $cmode) { + private function catchupArticleById($id, $cmode) { if ($cmode == 0) { - db_query($link, "UPDATE ttrss_user_entries SET + $this->dbh->query("UPDATE ttrss_user_entries SET unread = false,last_read = NOW() WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); } else if ($cmode == 1) { - db_query($link, "UPDATE ttrss_user_entries SET + $this->dbh->query("UPDATE ttrss_user_entries SET unread = true WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); } else { - db_query($link, "UPDATE ttrss_user_entries SET + $this->dbh->query("UPDATE ttrss_user_entries SET unread = NOT unread,last_read = NOW() WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); } - $feed_id = getArticleFeed($link, $id); - ccache_update($link, $feed_id, $_SESSION["uid"]); + $feed_id = getArticleFeed($id); + ccache_update($feed_id, $_SESSION["uid"]); } - static function create_published_article($link, $title, $url, $content, $labels_str, + static function create_published_article($title, $url, $content, $labels_str, $owner_uid) { - $guid = sha1($url . $owner_uid); // include owner_uid to prevent global GUID clash + $guid = 'SHA1:' . sha1("ttshared:" . $url . $owner_uid); // include owner_uid to prevent global GUID clash $content_hash = sha1($content); if ($labels_str != "") { @@ -104,61 +104,64 @@ class Article extends Handler_Protected { if (filter_var($url, FILTER_VALIDATE_URL) === FALSE) return false; - db_query($link, "BEGIN"); + db_query("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 + $result = db_query("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 + $result = db_query("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 + db_query("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 + db_query("UPDATE ttrss_user_entries SET published = true, + last_published = NOW() 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) + db_query("INSERT INTO ttrss_user_entries + (ref_id, uuid, feed_id, orig_feed_id, owner_uid, published, tag_cache, label_cache, + last_read, note, unread, last_published) VALUES - ('$ref_id', '', NULL, NULL, $owner_uid, true, '', '', NOW(), '', false)"); + ('$ref_id', '', NULL, NULL, $owner_uid, true, '', '', NOW(), '', false, NOW())"); } if (count($labels) != 0) { foreach ($labels as $label) { - label_add_article($link, $ref_id, trim($label), $owner_uid); + label_add_article($ref_id, trim($label), $owner_uid); } } $rc = true; } else { - $result = db_query($link, "INSERT INTO ttrss_entries + $result = db_query("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'"); + $result = db_query("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) + db_query("INSERT INTO ttrss_user_entries + (ref_id, uuid, feed_id, orig_feed_id, owner_uid, published, tag_cache, label_cache, + last_read, note, unread, last_published) VALUES - ('$ref_id', '', NULL, NULL, $owner_uid, true, '', '', NOW(), '', false)"); + ('$ref_id', '', NULL, NULL, $owner_uid, true, '', '', NOW(), '', false, NOW())"); if (count($labels) != 0) { foreach ($labels as $label) { - label_add_article($link, $ref_id, trim($label), $owner_uid); + label_add_article($ref_id, trim($label), $owner_uid); } } @@ -166,11 +169,178 @@ class Article extends Handler_Protected { } } - db_query($link, "COMMIT"); + db_query("COMMIT"); return $rc; } + function editArticleTags() { + + print __("Tags for this article (separated by commas):")."<br>"; + + $param = $this->dbh->escape_string($_REQUEST['param']); + + $tags = get_article_tags($this->dbh->escape_string($param)); + + $tags_str = join(", ", $tags); + + print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"id\" value=\"$param\">"; + print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"article\">"; + print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"setArticleTags\">"; + + print "<table width='100%'><tr><td>"; + + print "<textarea dojoType=\"dijit.form.SimpleTextarea\" rows='4' + style='font-size : 12px; width : 100%' id=\"tags_str\" + name='tags_str'>$tags_str</textarea> + <div class=\"autocomplete\" id=\"tags_choices\" + style=\"display:none\"></div>"; + + print "</td></tr></table>"; + + print "<div class='dlgButtons'>"; + + print "<button dojoType=\"dijit.form.Button\" + onclick=\"dijit.byId('editTagsDlg').execute()\">".__('Save')."</button> "; + print "<button dojoType=\"dijit.form.Button\" + onclick=\"dijit.byId('editTagsDlg').hide()\">".__('Cancel')."</button>"; + print "</div>"; + + } + + function setScore() { + $ids = $this->dbh->escape_string($_REQUEST['id']); + $score = (int)$this->dbh->escape_string($_REQUEST['score']); + + $this->dbh->query("UPDATE ttrss_user_entries SET + score = '$score' WHERE ref_id IN ($ids) AND owner_uid = " . $_SESSION["uid"]); + + print json_encode(array("id" => $ids, + "score_pic" => get_score_pic($score))); + } + + + function setArticleTags() { + + $id = $this->dbh->escape_string($_REQUEST["id"]); + + $tags_str = $this->dbh->escape_string($_REQUEST["tags_str"]); + $tags = array_unique(trim_array(explode(",", $tags_str))); + + $this->dbh->query("BEGIN"); + + $result = $this->dbh->query("SELECT int_id FROM ttrss_user_entries WHERE + ref_id = '$id' AND owner_uid = '".$_SESSION["uid"]."' LIMIT 1"); + + if ($this->dbh->num_rows($result) == 1) { + + $tags_to_cache = array(); + + $int_id = $this->dbh->fetch_result($result, 0, "int_id"); + + $this->dbh->query("DELETE FROM ttrss_tags WHERE + post_int_id = $int_id AND owner_uid = '".$_SESSION["uid"]."'"); + + foreach ($tags as $tag) { + $tag = sanitize_tag($tag); + + if (!tag_is_valid($tag)) { + continue; + } + + if (preg_match("/^[0-9]*$/", $tag)) { + continue; + } + + // print "<!-- $id : $int_id : $tag -->"; + + if ($tag != '') { + $this->dbh->query("INSERT INTO ttrss_tags + (post_int_id, owner_uid, tag_name) VALUES ('$int_id', '".$_SESSION["uid"]."', '$tag')"); + } + + array_push($tags_to_cache, $tag); + } + + /* update tag cache */ + + sort($tags_to_cache); + $tags_str = join(",", $tags_to_cache); + + $this->dbh->query("UPDATE ttrss_user_entries + SET tag_cache = '$tags_str' WHERE ref_id = '$id' + AND owner_uid = " . $_SESSION["uid"]); + } + + $this->dbh->query("COMMIT"); + + $tags = get_article_tags($id); + $tags_str = format_tags_string($tags, $id); + $tags_str_full = join(", ", $tags); + + if (!$tags_str_full) $tags_str_full = __("no tags"); + + print json_encode(array("id" => (int)$id, + "content" => $tags_str, "content_full" => $tags_str_full)); + } + + + function completeTags() { + $search = $this->dbh->escape_string($_REQUEST["search"]); + + $result = $this->dbh->query("SELECT DISTINCT tag_name FROM ttrss_tags + WHERE owner_uid = '".$_SESSION["uid"]."' AND + tag_name LIKE '$search%' ORDER BY tag_name + LIMIT 10"); + + print "<ul>"; + while ($line = $this->dbh->fetch_assoc($result)) { + print "<li>" . $line["tag_name"] . "</li>"; + } + print "</ul>"; + } + + function assigntolabel() { + return $this->labelops(true); + } + + function removefromlabel() { + return $this->labelops(false); + } + + private function labelops($assign) { + $reply = array(); + + $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); + $label_id = $this->dbh->escape_string($_REQUEST["lid"]); + + $label = $this->dbh->escape_string(label_find_caption($label_id, + $_SESSION["uid"])); + + $reply["info-for-headlines"] = array(); + + if ($label) { + + foreach ($ids as $id) { + + if ($assign) + label_add_article($id, $label, $_SESSION["uid"]); + else + label_remove_article($id, $label, $_SESSION["uid"]); + + $labels = get_article_labels($id, $_SESSION["uid"]); + + array_push($reply["info-for-headlines"], + array("id" => $id, "labels" => format_article_labels($labels, $id))); + + } + } + + $reply["message"] = "UPDATE_COUNTERS"; + + print json_encode($reply); + } + } diff --git a/classes/auth/base.php b/classes/auth/base.php index aa9d657a4..69acd0985 100644 --- a/classes/auth/base.php +++ b/classes/auth/base.php @@ -1,9 +1,9 @@ <?php class Auth_Base { - protected $link; + private $dbh; - function __construct($link) { - $this->link = $link; + function __construct() { + $this->dbh = Db::get(); } function check_password($owner_uid, $password) { @@ -16,12 +16,14 @@ class Auth_Base { // Auto-creates specified user if allowed by system configuration // Can be used instead of find_user_by_login() by external auth modules - function auto_create_user($login) { + function auto_create_user($login, $password = false) { if ($login && defined('AUTH_AUTO_CREATE') && AUTH_AUTO_CREATE) { $user_id = $this->find_user_by_login($login); + if (!$password) $password = make_password(); + if (!$user_id) { - $login = db_escape_string($login); + $login = $this->dbh->escape_string($login); $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); $pwd_hash = encrypt_password($password, $salt, true); @@ -29,7 +31,7 @@ class Auth_Base { (login,access_level,last_login,created,pwd_hash,salt) VALUES ('$login', 0, null, NOW(), '$pwd_hash','$salt')"; - db_query($this->link, $query); + $this->dbh->query($query); return $this->find_user_by_login($login); @@ -42,13 +44,13 @@ class Auth_Base { } function find_user_by_login($login) { - $login = db_escape_string($login); + $login = $this->dbh->escape_string($login); - $result = db_query($this->link, "SELECT id FROM ttrss_users WHERE + $result = $this->dbh->query("SELECT id FROM ttrss_users WHERE login = '$login'"); - if (db_num_rows($result) > 0) { - return db_fetch_result($result, 0, "id"); + if ($this->dbh->num_rows($result) > 0) { + return $this->dbh->fetch_result($result, 0, "id"); } else { return false; } diff --git a/classes/backend.php b/classes/backend.php index 5473c5c79..7737df0f6 100644 --- a/classes/backend.php +++ b/classes/backend.php @@ -11,7 +11,7 @@ class Backend extends Handler { require_once "digest.php"; - $rv = prepare_headlines_digest($this->link, $_SESSION['uid'], 1, 1000); + $rv = prepare_headlines_digest($_SESSION['uid'], 1, 1000); $rv[3] = "<pre>" . $rv[3] . "</pre>"; @@ -19,8 +19,8 @@ class Backend extends Handler { } private function display_main_help() { - $info = get_hotkeys_info($this->link); - $imap = get_hotkeys_map($this->link); + $info = get_hotkeys_info(); + $imap = get_hotkeys_map(); $omap = array(); foreach ($imap[1] as $sequence => $action) { @@ -29,6 +29,10 @@ class Backend extends Handler { array_push($omap[$action], $sequence); } + print_notice("<a target=\"_blank\" href=\"http://tt-rss.org/wiki/InterfaceTips\">". + __("Other interface tips are available in the Tiny Tiny RSS wiki.") . + "</a>"); + print "<ul class='helpKbList' id='helpKbList'>"; print "<h2>" . __("Keyboard Shortcuts") . "</h2>"; @@ -45,6 +49,28 @@ class Backend extends Handler { $sequence = substr($sequence, strpos($sequence, "|")+1, strlen($sequence)); + } else { + $keys = explode(" ", $sequence); + + for ($i = 0; $i < count($keys); $i++) { + if (strlen($keys[$i]) > 1) { + $tmp = ''; + foreach (str_split($keys[$i]) as $c) { + switch ($c) { + case '*': + $tmp .= __('Shift') . '+'; + break; + case '^': + $tmp .= __('Ctrl') . '+'; + break; + default: + $tmp .= $c; + } + } + $keys[$i] = $tmp; + } + } + $sequence = join(" ", $keys); } print "<li>"; @@ -57,10 +83,6 @@ class Backend extends Handler { } print "</ul>"; - - print "<p><a target=\"_blank\" href=\"http://tt-rss.org/wiki/InterfaceTips\">". - __("Other interface tips are available in the Tiny Tiny RSS wiki.") . - "</a></p>"; } function help() { diff --git a/classes/db.php b/classes/db.php new file mode 100644 index 000000000..695ca6ea2 --- /dev/null +++ b/classes/db.php @@ -0,0 +1,98 @@ +<?php +class Db implements IDb { + private static $instance; + private $adapter; + private $link; + + private function __construct() { + + $er = error_reporting(E_ALL); + + if (defined('_ENABLE_PDO') && _ENABLE_PDO && class_exists("PDO")) { + $this->adapter = new Db_PDO(); + } else { + switch (DB_TYPE) { + case "mysql": + if (function_exists("mysqli_connect")) { + $this->adapter = new Db_Mysqli(); + } else { + $this->adapter = new Db_Mysql(); + } + break; + case "pgsql": + $this->adapter = new Db_Pgsql(); + break; + default: + die("Unknown DB_TYPE: " . DB_TYPE); + } + } + + if (!$this->adapter) die("Error initializing database adapter for " . DB_TYPE); + + $this->link = $this->adapter->connect(DB_HOST, DB_USER, DB_PASS, DB_NAME, defined('DB_PORT') ? DB_PORT : ""); + + if (!$this->link) { + die("Error connecting through adapter: " . $this->adapter->last_error()); + } + + error_reporting($er); + } + + private function __clone() { + // + } + + public static function get() { + if (self::$instance == null) + self::$instance = new self(); + + return self::$instance; + } + + static function quote($str){ + return("'$str'"); + } + + function reconnect() { + $this->link = $this->adapter->connect(DB_HOST, DB_USER, DB_PASS, DB_NAME, defined('DB_PORT') ? DB_PORT : ""); + } + + function connect($host, $user, $pass, $db, $port) { + //return $this->adapter->connect($host, $user, $pass, $db, $port); + return ; + } + + function escape_string($s, $strip_tags = true) { + return $this->adapter->escape_string($s, $strip_tags); + } + + function query($query, $die_on_error = true) { + return $this->adapter->query($query, $die_on_error); + } + + function fetch_assoc($result) { + return $this->adapter->fetch_assoc($result); + } + + function num_rows($result) { + return $this->adapter->num_rows($result); + } + + function fetch_result($result, $row, $param) { + return $this->adapter->fetch_result($result, $row, $param); + } + + function close() { + return $this->adapter->close(); + } + + function affected_rows($result) { + return $this->adapter->affected_rows($result); + } + + function last_error() { + return $this->adapter->last_error(); + } + +} +?> diff --git a/classes/db/mysql.php b/classes/db/mysql.php new file mode 100644 index 000000000..aab05aca2 --- /dev/null +++ b/classes/db/mysql.php @@ -0,0 +1,73 @@ +<?php +class Db_Mysql implements IDb { + private $link; + + function connect($host, $user, $pass, $db, $port) { + $this->link = mysql_connect($host, $user, $pass); + + if ($this->link) { + $result = mysql_select_db($db, $this->link); + if (!$result) { + die("Can't select DB: " . mysql_error($this->link)); + } + + $this->init(); + + return $this->link; + } else { + die("Unable to connect to database (as $user to $host, database $db): " . mysql_error()); + } + } + + function escape_string($s, $strip_tags = true) { + if ($strip_tags) $s = strip_tags($s); + + return mysql_real_escape_string($s, $this->link); + } + + function query($query, $die_on_error = true) { + $result = mysql_query($query, $this->link); + if (!$result) { + user_error("Query $query failed: " . ($this->link ? mysql_error($this->link) : "No connection"), + $die_on_error ? E_USER_ERROR : E_USER_WARNING); + } + return $result; + } + + function fetch_assoc($result) { + return mysql_fetch_assoc($result); + } + + + function num_rows($result) { + return mysql_num_rows($result); + } + + function fetch_result($result, $row, $param) { + return mysql_result($result, $row, $param); + } + + function close() { + return mysql_close($this->link); + } + + function affected_rows($result) { + return mysql_affected_rows($this->link); + } + + function last_error() { + return mysql_error(); + } + + function init() { + $this->query("SET time_zone = '+0:0'"); + + if (defined('MYSQL_CHARSET') && MYSQL_CHARSET) { + $this->query("SET NAMES " . MYSQL_CHARSET); + } + + return true; + } + +} +?> diff --git a/classes/db/mysqli.php b/classes/db/mysqli.php new file mode 100644 index 000000000..550df6f15 --- /dev/null +++ b/classes/db/mysqli.php @@ -0,0 +1,77 @@ +<?php +class Db_Mysqli implements IDb { + private $link; + + function connect($host, $user, $pass, $db, $port) { + if ($port) + $this->link = mysqli_connect($host, $user, $pass, $db, $port); + else + $this->link = mysqli_connect($host, $user, $pass, $db); + + if ($this->link) { + $this->init(); + + return $this->link; + } else { + die("Unable to connect to database (as $user to $host, database $db): " . mysqli_error()); + } + } + + function escape_string($s, $strip_tags = true) { + if ($strip_tags) $s = strip_tags($s); + + return mysqli_real_escape_string($this->link, $s); + } + + function query($query, $die_on_error = true) { + $result = mysqli_query($this->link, $query); + if (!$result) { + user_error("Query $query failed: " . ($this->link ? mysqli_error($this->link) : "No connection"), + $die_on_error ? E_USER_ERROR : E_USER_WARNING); + } + + return $result; + } + + function fetch_assoc($result) { + return mysqli_fetch_assoc($result); + } + + + function num_rows($result) { + return mysqli_num_rows($result); + } + + function fetch_result($result, $row, $param) { + if (mysqli_data_seek($result, $row)) { + $line = mysqli_fetch_assoc($result); + return $line[$param]; + } else { + return false; + } + } + + function close() { + return mysqli_close($this->link); + } + + function affected_rows($result) { + return mysqli_affected_rows($this->link); + } + + function last_error() { + return mysqli_error(); + } + + function init() { + $this->query("SET time_zone = '+0:0'"); + + if (defined('MYSQL_CHARSET') && MYSQL_CHARSET) { + $this->query("SET NAMES " . MYSQL_CHARSET); + } + + return true; + } + +} +?> diff --git a/classes/db/pdo.php b/classes/db/pdo.php new file mode 100644 index 000000000..126f5150a --- /dev/null +++ b/classes/db/pdo.php @@ -0,0 +1,100 @@ +<?php +class Db_PDO implements IDb { + private $pdo; + + function connect($host, $user, $pass, $db, $port) { + $connstr = DB_TYPE . ":host=$host;dbname=$db"; + + if (DB_TYPE == "mysql") $connstr .= ";charset=utf8"; + + try { + $this->pdo = new PDO($connstr, $user, $pass); + $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->init(); + } catch (PDOException $e) { + die($e->getMessage()); + } + + return $this->pdo; + } + + function escape_string($s, $strip_tags = true) { + if ($strip_tags) $s = strip_tags($s); + + $qs = $this->pdo->quote($s); + + return mb_substr($qs, 1, mb_strlen($qs)-2); + } + + function query($query, $die_on_error = true) { + try { + return new Db_Stmt($this->pdo->query($query)); + } catch (PDOException $e) { + user_error($e->getMessage(), $die_on_error ? E_USER_ERROR : E_USER_WARNING); + } + } + + function fetch_assoc($result) { + try { + if ($result) { + return $result->fetch(); + } else { + return null; + } + } catch (PDOException $e) { + user_error($e->getMessage(), E_USER_WARNING); + } + } + + function num_rows($result) { + try { + if ($result) { + return $result->rowCount(); + } else { + return false; + } + } catch (PDOException $e) { + user_error($e->getMessage(), E_USER_WARNING); + } + } + + function fetch_result($result, $row, $param) { + return $result->fetch_result($row, $param); + } + + function close() { + $this->pdo = null; + } + + function affected_rows($result) { + try { + if ($result) { + return $result->rowCount(); + } else { + return null; + } + } catch (PDOException $e) { + user_error($e->getMessage(), E_USER_WARNING); + } + } + + function last_error() { + return join(" ", $this->pdo->errorInfo()); + } + + function init() { + switch (DB_TYPE) { + case "pgsql": + $this->query("set client_encoding = 'UTF-8'"); + $this->query("set datestyle = 'ISO, european'"); + $this->query("set TIME ZONE 0"); + case "mysql": + $this->query("SET time_zone = '+0:0'"); + return; + } + + return true; + } + +} +?> diff --git a/classes/db/pgsql.php b/classes/db/pgsql.php new file mode 100644 index 000000000..4d860790b --- /dev/null +++ b/classes/db/pgsql.php @@ -0,0 +1,82 @@ +<?php +class Db_Pgsql implements IDb { + private $link; + + function connect($host, $user, $pass, $db, $port) { + $string = "dbname=$db user=$user"; + + if ($pass) { + $string .= " password=$pass"; + } + + if ($host) { + $string .= " host=$host"; + } + + if (is_numeric($port) && $port > 0) { + $string = "$string port=" . $port; + } + + $this->link = pg_connect($string); + + if (!$this->link) { + die("Unable to connect to database (as $user to $host, database $db):" . pg_last_error()); + } + + $this->init(); + + return $this->link; + } + + function escape_string($s, $strip_tags = true) { + if ($strip_tags) $s = strip_tags($s); + + return pg_escape_string($s); + } + + function query($query, $die_on_error = true) { + $result = pg_query($query); + + if (!$result) { + $query = htmlspecialchars($query); // just in case + user_error("Query $query failed: " . ($this->link ? pg_last_error($this->link) : "No connection"), + $die_on_error ? E_USER_ERROR : E_USER_WARNING); + } + return $result; + } + + function fetch_assoc($result) { + return pg_fetch_assoc($result); + } + + + function num_rows($result) { + return pg_num_rows($result); + } + + function fetch_result($result, $row, $param) { + return pg_fetch_result($result, $row, $param); + } + + function close() { + return pg_close($this->link); + } + + function affected_rows($result) { + return pg_affected_rows($result); + } + + function last_error() { + return pg_last_error($this->link); + } + + function init() { + $this->query("set client_encoding = 'UTF-8'"); + pg_set_client_encoding("UNICODE"); + $this->query("set datestyle = 'ISO, european'"); + $this->query("set TIME ZONE 0"); + + return true; + } +} +?> diff --git a/classes/db/prefs.php b/classes/db/prefs.php new file mode 100644 index 000000000..26fb4666b --- /dev/null +++ b/classes/db/prefs.php @@ -0,0 +1,190 @@ +<?php +class Db_Prefs { + private $dbh; + private static $instance; + private $cache; + + function __construct() { + $this->dbh = Db::get(); + $this->cache = array(); + + if ($_SESSION["uid"]) $this->cache(); + } + + private function __clone() { + // + } + + public static function get() { + if (self::$instance == null) + self::$instance = new self(); + + return self::$instance; + } + + function cache() { + $profile = false; + + $user_id = $_SESSION["uid"]; + @$profile = $_SESSION["profile"]; + + if ($profile) { + $profile_qpart = "profile = '$profile' AND"; + } else { + $profile_qpart = "profile IS NULL AND"; + } + + if (get_schema_version() < 63) $profile_qpart = ""; + + $result = db_query("SELECT + value,ttrss_prefs_types.type_name as type_name,ttrss_prefs.pref_name AS pref_name + FROM + ttrss_user_prefs,ttrss_prefs,ttrss_prefs_types + WHERE + $profile_qpart + ttrss_prefs.pref_name NOT LIKE '_MOBILE%' AND + ttrss_prefs_types.id = type_id AND + owner_uid = '$user_id' AND + ttrss_user_prefs.pref_name = ttrss_prefs.pref_name"); + + while ($line = db_fetch_assoc($result)) { + if ($user_id == $_SESSION["uid"]) { + $pref_name = $line["pref_name"]; + + $this->cache[$pref_name]["type"] = $line["type_name"]; + $this->cache[$pref_name]["value"] = $line["value"]; + } + } + } + + function read($pref_name, $user_id = false, $die_on_error = false) { + + $pref_name = db_escape_string($pref_name); + $profile = false; + + if (!$user_id) { + $user_id = $_SESSION["uid"]; + @$profile = $_SESSION["profile"]; + } else { + $user_id = sprintf("%d", $user_id); + } + + if (isset($this->cache[$pref_name])) { + $tuple = $this->cache[$pref_name]; + return $this->convert($tuple["value"], $tuple["type"]); + } + + if ($profile) { + $profile_qpart = "profile = '$profile' AND"; + } else { + $profile_qpart = "profile IS NULL AND"; + } + + if (get_schema_version() < 63) $profile_qpart = ""; + + $result = db_query("SELECT + value,ttrss_prefs_types.type_name as type_name + FROM + ttrss_user_prefs,ttrss_prefs,ttrss_prefs_types + WHERE + $profile_qpart + ttrss_user_prefs.pref_name = '$pref_name' AND + ttrss_prefs_types.id = type_id AND + owner_uid = '$user_id' AND + ttrss_user_prefs.pref_name = ttrss_prefs.pref_name"); + + if (db_num_rows($result) > 0) { + $value = db_fetch_result($result, 0, "value"); + $type_name = db_fetch_result($result, 0, "type_name"); + + if ($user_id == $_SESSION["uid"]) { + $this->cache[$pref_name]["type"] = $type_name; + $this->cache[$pref_name]["value"] = $value; + } + + return $this->convert($value, $type_name); + + } else { + user_error("Fatal error, unknown preferences key: $pref_name", $die_on_error ? E_USER_ERROR : E_USER_WARNING); + return null; + } + } + + function convert($value, $type_name) { + if ($type_name == "bool") { + return $value == "true"; + } else if ($type_name == "integer") { + return (int)$value; + } else { + return $value; + } + } + + function write($pref_name, $value, $user_id = false, $strip_tags = true) { + $pref_name = db_escape_string($pref_name); + $value = db_escape_string($value, $strip_tags); + + if (!$user_id) { + $user_id = $_SESSION["uid"]; + @$profile = $_SESSION["profile"]; + } else { + $user_id = sprintf("%d", $user_id); + $prefs_cache = false; + } + + if ($profile) { + $profile_qpart = "AND profile = '$profile'"; + } else { + $profile_qpart = "AND profile IS NULL"; + } + + if (get_schema_version() < 63) $profile_qpart = ""; + + $type_name = ""; + $current_value = ""; + + if (isset($this->cache[$pref_name])) { + $type_name = $this->cache[$pref_name]["type"]; + $current_value = $this->cache[$pref_name]["value"]; + } + + if (!$type_name) { + $result = db_query("SELECT type_name + FROM ttrss_prefs,ttrss_prefs_types + WHERE pref_name = '$pref_name' AND type_id = ttrss_prefs_types.id"); + + if (db_num_rows($result) > 0) + $type_name = db_fetch_result($result, 0, "type_name"); + } else if ($current_value == $value) { + return; + } + + if ($type_name) { + if ($type_name == "bool") { + if ($value == "1" || $value == "true") { + $value = "true"; + } else { + $value = "false"; + } + } else if ($type_name == "integer") { + $value = sprintf("%d", $value); + } + + if ($pref_name == 'USER_TIMEZONE' && $value == '') { + $value = 'UTC'; + } + + db_query("UPDATE ttrss_user_prefs SET + value = '$value' WHERE pref_name = '$pref_name' + $profile_qpart + AND owner_uid = " . $_SESSION["uid"]); + + if ($user_id == $_SESSION["uid"]) { + $this->cache[$pref_name]["type"] = $type_name; + $this->cache[$pref_name]["value"] = $value; + } + } + } + +} +?> diff --git a/classes/db/stmt.php b/classes/db/stmt.php new file mode 100644 index 000000000..4d3596ef1 --- /dev/null +++ b/classes/db/stmt.php @@ -0,0 +1,32 @@ +<?php +class Db_Stmt { + private $stmt; + private $cache; + + function __construct($stmt) { + $this->stmt = $stmt; + $this->cache = false; + } + + function fetch_result($row, $param) { + if (!$this->cache) { + $this->cache = $this->stmt->fetchAll(); + } + + if (isset($this->cache[$row])) { + return $this->cache[$row][$param]; + } else { + user_error("Unable to jump to row $row", E_USER_WARNING); + return false; + } + } + + function rowCount() { + return $this->stmt->rowCount(); + } + + function fetch() { + return $this->stmt->fetch(); + } +} +?> diff --git a/classes/dbupdater.php b/classes/dbupdater.php new file mode 100644 index 000000000..a319da03d --- /dev/null +++ b/classes/dbupdater.php @@ -0,0 +1,65 @@ +<?php +class DbUpdater { + + private $dbh; + private $db_type; + private $need_version; + + function __construct($dbh, $db_type, $need_version) { + $this->dbh = $dbh; + $this->db_type = $db_type; + $this->need_version = (int) $need_version; + } + + function getSchemaVersion() { + $result = db_query("SELECT schema_version FROM ttrss_version"); + return (int) db_fetch_result($result, 0, "schema_version"); + } + + function isUpdateRequired() { + return $this->getSchemaVersion() < $this->need_version; + } + + function getSchemaLines($version) { + $filename = "schema/versions/".$this->db_type."/$version.sql"; + + if (file_exists($filename)) { + return explode(";", preg_replace("/[\r\n]/", "", file_get_contents($filename))); + } else { + return false; + } + } + + function performUpdateTo($version) { + if ($this->getSchemaVersion() == $version - 1) { + + $lines = $this->getSchemaLines($version); + + if (is_array($lines)) { + + db_query("BEGIN"); + + foreach ($lines as $line) { + if (strpos($line, "--") !== 0 && $line) { + db_query($line); + } + } + + $db_version = $this->getSchemaVersion(); + + if ($db_version == $version) { + db_query("COMMIT"); + return true; + } else { + db_query("ROLLBACK"); + return false; + } + } else { + return true; + } + } else { + return false; + } + } + +} ?> diff --git a/classes/dlg.php b/classes/dlg.php index 74eb9f633..cfa960d9a 100644 --- a/classes/dlg.php +++ b/classes/dlg.php @@ -4,35 +4,29 @@ class Dlg extends Handler_Protected { function before($method) { if (parent::before($method)) { - header("Content-Type: text/xml; charset=utf-8"); - $this->param = db_escape_string($_REQUEST["param"]); - print "<dlg>"; + header("Content-Type: text/html"); # required for iframe + + $this->param = $this->dbh->escape_string($_REQUEST["param"]); return true; } return false; } - function after() { - print "</dlg>"; - } - function importOpml() { - header("Content-Type: text/html"); # required for iframe - print __("If you have imported labels and/or filters, you might need to reload preferences to see your new data.") . "</p>"; print "<div class=\"prefFeedOPMLHolder\">"; $owner_uid = $_SESSION["uid"]; - db_query($this->link, "BEGIN"); + $this->dbh->query("BEGIN"); print "<ul class='nomarks'>"; - $opml = new Opml($this->link, $_REQUEST); + $opml = new Opml($_REQUEST); $opml->opml_import($_SESSION["uid"]); - db_query($this->link, "COMMIT"); + $this->dbh->query("COMMIT"); print "</ul>"; print "</div>"; @@ -48,127 +42,8 @@ class Dlg extends Handler_Protected { //return; } - function editPrefProfiles() { - print "<div dojoType=\"dijit.Toolbar\">"; - - print "<div dojoType=\"dijit.form.DropDownButton\">". - "<span>" . __('Select')."</span>"; - print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">"; - print "<div onclick=\"selectTableRows('prefFeedProfileList', 'all')\" - dojoType=\"dijit.MenuItem\">".__('All')."</div>"; - print "<div onclick=\"selectTableRows('prefFeedProfileList', 'none')\" - dojoType=\"dijit.MenuItem\">".__('None')."</div>"; - print "</div></div>"; - - print "<div style=\"float : right\">"; - - print "<input name=\"newprofile\" dojoType=\"dijit.form.ValidationTextBox\" - required=\"1\"> - <button dojoType=\"dijit.form.Button\" - onclick=\"dijit.byId('profileEditDlg').addProfile()\">". - __('Create profile')."</button></div>"; - - print "</div>"; - - $result = db_query($this->link, "SELECT title,id FROM ttrss_settings_profiles - WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title"); - - print "<div class=\"prefFeedCatHolder\">"; - - print "<form id=\"profile_edit_form\" onsubmit=\"return false\">"; - - print "<table width=\"100%\" class=\"prefFeedProfileList\" - cellspacing=\"0\" id=\"prefFeedProfileList\">"; - - print "<tr class=\"placeholder\" id=\"FCATR-0\">"; #odd - - print "<td width='5%' align='center'><input - id='FCATC-0' - onclick='toggleSelectRow2(this);' - dojoType=\"dijit.form.CheckBox\" - type=\"checkbox\"></td>"; - - if (!$_SESSION["profile"]) { - $is_active = __("(active)"); - } else { - $is_active = ""; - } - - print "<td><span>" . - __("Default profile") . " $is_active</span></td>"; - - print "</tr>"; - - $lnum = 1; - - while ($line = db_fetch_assoc($result)) { - - $class = ($lnum % 2) ? "even" : "odd"; - - $profile_id = $line["id"]; - $this_row_id = "id=\"FCATR-$profile_id\""; - - print "<tr class=\"placeholder\" $this_row_id>"; - - $edit_title = htmlspecialchars($line["title"]); - - print "<td width='5%' align='center'><input - onclick='toggleSelectRow2(this);' - id='FCATC-$profile_id' - dojoType=\"dijit.form.CheckBox\" - type=\"checkbox\"></td>"; - - if ($_SESSION["profile"] == $line["id"]) { - $is_active = __("(active)"); - } else { - $is_active = ""; - } - - print "<td><span dojoType=\"dijit.InlineEditBox\" - width=\"300px\" autoSave=\"false\" - profile-id=\"$profile_id\">" . $edit_title . - "<script type=\"dojo/method\" event=\"onChange\" args=\"item\"> - var elem = this; - dojo.xhrPost({ - url: 'backend.php', - content: {op: 'rpc', method: 'saveprofile', - value: this.value, - id: this.srcNodeRef.getAttribute('profile-id')}, - load: function(response) { - elem.attr('value', response); - } - }); - </script> - </span> $is_active</td>"; - - print "</tr>"; - - ++$lnum; - } - - print "</table>"; - print "</form>"; - print "</div>"; - - print "<div class='dlgButtons'> - <div style='float : left'> - <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('profileEditDlg').removeSelected()\">". - __('Remove selected profiles')."</button> - <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('profileEditDlg').activateProfile()\">". - __('Activate profile')."</button> - </div>"; - - print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('profileEditDlg').hide()\">". - __('Close this window')."</button>"; - print "</div>"; - - } - function pubOPMLUrl() { - print "<title>".__('Public OPML URL')."</title>"; - print "<content><![CDATA["; - - $url_path = Opml::opml_publish_url($this->link); + $url_path = Opml::opml_publish_url(); print __("Your Public OPML URL is:"); @@ -185,15 +60,11 @@ class Dlg extends Handler_Protected { __('Close this window')."</button>"; print "</div>"; - print "]]></content>"; //return; } function explainError() { - print "<title>".__('Notice')."</title>"; - print "<content><![CDATA["; - print "<div class=\"errorExplained\">"; if ($this->param == 1) { @@ -222,231 +93,11 @@ class Dlg extends Handler_Protected { __('Close this window')."</button>"; print "</div>"; - print "]]></content>"; //return; } - function quickAddFeed() { - print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"rpc\">"; - print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"addfeed\">"; - - print "<div class=\"dlgSec\">".__("Feed or site URL")."</div>"; - print "<div class=\"dlgSecCont\">"; - - print "<div style='float : right'> - <img style='display : none' - id='feed_add_spinner' src='". - theme_image($this->link, 'images/indicator_white.gif')."'></div>"; - - print "<input style=\"font-size : 16px; width : 20em;\" - placeHolder=\"".__("Feed or site URL")."\" - dojoType=\"dijit.form.ValidationTextBox\" required=\"1\" name=\"feed\" id=\"feedDlg_feedUrl\">"; - - print "<hr/>"; - - if (get_pref($this->link, 'ENABLE_FEED_CATS')) { - print __('Place in category:') . " "; - print_feed_cat_select($this->link, "cat", false, 'dojoType="dijit.form.Select"'); - } - - print "</div>"; - - print '<div id="feedDlg_feedsContainer" style="display : none"> - - <div class="dlgSec">' . __('Available feeds') . '</div> - <div class="dlgSecCont">'. - '<select id="feedDlg_feedContainerSelect" - dojoType="dijit.form.Select" size="3"> - <script type="dojo/method" event="onChange" args="value"> - dijit.byId("feedDlg_feedUrl").attr("value", value); - </script> - </select>'. - '</div></div>'; - - print "<div id='feedDlg_loginContainer' style='display : none'> - - <div class=\"dlgSec\">".__("Authentication")."</div> - <div class=\"dlgSecCont\">". - - " <input dojoType=\"dijit.form.TextBox\" name='login'\" - placeHolder=\"".__("Login")."\" - style=\"width : 10em;\"> ". - " <input - placeHolder=\"".__("Password")."\" - dojoType=\"dijit.form.TextBox\" type='password' - style=\"width : 10em;\" name='pass'\"> - </div></div>"; - - - print "<div style=\"clear : both\"> - <input type=\"checkbox\" name=\"need_auth\" dojoType=\"dijit.form.CheckBox\" id=\"feedDlg_loginCheck\" - onclick='checkboxToggleElement(this, \"feedDlg_loginContainer\")'> - <label for=\"feedDlg_loginCheck\">". - __('This feed requires authentication.')."</div>"; - - print "</form>"; - - print "<div class=\"dlgButtons\"> - <button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedAddDlg').execute()\">".__('Subscribe')."</button>"; - - if (!(defined('_DISABLE_FEED_BROWSER') && _DISABLE_FEED_BROWSER)) { - print "<button dojoType=\"dijit.form.Button\" onclick=\"return feedBrowser()\">".__('More feeds')."</button>"; - } - - print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedAddDlg').hide()\">".__('Cancel')."</button> - </div>"; - - //return; - } - - function feedBrowser() { - if (defined('_DISABLE_FEED_BROWSER') && _DISABLE_FEED_BROWSER) return; - - $browser_search = db_escape_string($_REQUEST["search"]); - - print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"rpc\">"; - print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"updateFeedBrowser\">"; - - print "<div dojoType=\"dijit.Toolbar\"> - <div style='float : right'> - <img style='display : none' - id='feed_browser_spinner' src='". - theme_image($this->link, 'images/indicator_white.gif')."'> - <input name=\"search\" dojoType=\"dijit.form.TextBox\" size=\"20\" type=\"search\" - onchange=\"dijit.byId('feedBrowserDlg').update()\" value=\"$browser_search\"> - <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('feedBrowserDlg').update()\">".__('Search')."</button> - </div>"; - - print " <select name=\"mode\" dojoType=\"dijit.form.Select\" onchange=\"dijit.byId('feedBrowserDlg').update()\"> - <option value='1'>" . __('Popular feeds') . "</option> - <option value='2'>" . __('Feed archive') . "</option> - </select> "; - - print __("limit:"); - - print " <select dojoType=\"dijit.form.Select\" name=\"limit\" onchange=\"dijit.byId('feedBrowserDlg').update()\">"; - - foreach (array(25, 50, 100, 200) as $l) { - $issel = ($l == $limit) ? "selected=\"1\"" : ""; - print "<option $issel value=\"$l\">$l</option>"; - } - - print "</select> "; - - print "</div>"; - - $owner_uid = $_SESSION["uid"]; - - require_once "feedbrowser.php"; - - print "<ul class='browseFeedList' id='browseFeedList'>"; - print make_feed_browser($this->link, $search, 25); - print "</ul>"; - - print "<div align='center'> - <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('feedBrowserDlg').execute()\">".__('Subscribe')."</button> - <button dojoType=\"dijit.form.Button\" style='display : none' id='feed_archive_remove' onclick=\"dijit.byId('feedBrowserDlg').removeFromArchive()\">".__('Remove')."</button> - <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('feedBrowserDlg').hide()\" >".__('Cancel')."</button></div>"; - - } - - function search() { - $this->params = explode(":", db_escape_string($_REQUEST["param"]), 2); - - $active_feed_id = sprintf("%d", $this->params[0]); - $is_cat = $this->params[1] != "false"; - - print "<div class=\"dlgSec\">".__('Look for')."</div>"; - - print "<div class=\"dlgSecCont\">"; - - print "<input dojoType=\"dijit.form.ValidationTextBox\" - style=\"font-size : 16px; width : 20em;\" - required=\"1\" name=\"query\" type=\"search\" value=''>"; - - print "<hr/>".__('Limit search to:')." "; - - print "<select name=\"search_mode\" dojoType=\"dijit.form.Select\"> - <option value=\"all_feeds\">".__('All feeds')."</option>"; - - $feed_title = getFeedTitle($this->link, $active_feed_id); - - if (!$is_cat) { - $feed_cat_title = getFeedCatTitle($this->link, $active_feed_id); - } else { - $feed_cat_title = getCategoryTitle($this->link, $active_feed_id); - } - - if ($active_feed_id && !$is_cat) { - print "<option selected=\"1\" value=\"this_feed\">$feed_title</option>"; - } else { - print "<option disabled=\"1\" value=\"false\">".__('This feed')."</option>"; - } - - if ($is_cat) { - $cat_preselected = "selected=\"1\""; - } - - if (get_pref($this->link, 'ENABLE_FEED_CATS') && ($active_feed_id > 0 || $is_cat)) { - print "<option $cat_preselected value=\"this_cat\">$feed_cat_title</option>"; - } else { - //print "<option disabled>".__('This category')."</option>"; - } - - print "</select>"; - - print "</div>"; - - print "<div class=\"dlgButtons\">"; - - if (!SPHINX_ENABLED) { - print "<div style=\"float : left\"> - <a class=\"visibleLink\" target=\"_blank\" href=\"http://tt-rss.org/wiki/SearchSyntax\">Search syntax</a> - </div>"; - } - - print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('searchDlg').execute()\">".__('Search')."</button> - <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('searchDlg').hide()\">".__('Cancel')."</button> - </div>"; - } - - function editArticleTags() { - - print __("Tags for this article (separated by commas):")."<br>"; - - $tags = get_article_tags($this->link, $this->param); - - $tags_str = join(", ", $tags); - - print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"id\" value=\"$this->param\">"; - print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"rpc\">"; - print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"setArticleTags\">"; - - print "<table width='100%'><tr><td>"; - - print "<textarea dojoType=\"dijit.form.SimpleTextarea\" rows='4' - style='font-size : 12px; width : 100%' id=\"tags_str\" - name='tags_str'>$tags_str</textarea> - <div class=\"autocomplete\" id=\"tags_choices\" - style=\"display:none\"></div>"; - - print "</td></tr></table>"; - - print "<div class='dlgButtons'>"; - - print "<button dojoType=\"dijit.form.Button\" - onclick=\"dijit.byId('editTagsDlg').execute()\">".__('Save')."</button> "; - print "<button dojoType=\"dijit.form.Button\" - onclick=\"dijit.byId('editTagsDlg').hide()\">".__('Cancel')."</button>"; - print "</div>"; - - } - function printTagCloud() { - print "<title>".__('Tag Cloud')."</title>"; - print "<content><![CDATA["; - print "<div class=\"tagCloudContainer\">"; // from here: http://www.roscripts.com/Create_tag_cloud-71.html @@ -455,15 +106,15 @@ class Dlg extends Handler_Protected { FROM ttrss_tags WHERE owner_uid = ".$_SESSION["uid"]." GROUP BY tag_name ORDER BY count DESC LIMIT 50"; - $result = db_query($this->link, $query); + $result = $this->dbh->query($query); $tags = array(); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $tags[$line["tag_name"]] = $line["count"]; } - if( count($tags) == 0 ){ return; } + if(count($tags) == 0 ){ return; } ksort($tags); @@ -508,14 +159,10 @@ class Dlg extends Handler_Protected { __('Close this window')."</button>"; print "</div>"; - print "]]></content>"; } function printTagSelect() { - print "<title>" . __('Select item(s) by tags') . "</title>"; - print "<content><![CDATA["; - print __("Match:"). " " . "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\" type=\"radio\" checked value=\"any\" name=\"tag_mode\" id=\"tag_mode_any\">"; print "<label for=\"tag_mode_any\">".__("Any")."</label>"; @@ -524,10 +171,10 @@ class Dlg extends Handler_Protected { print "<label for=\"tag_mode_all\">".__("All tags.")."</input>"; print "<select id=\"all_tags\" name=\"all_tags\" title=\"" . __('Which Tags?') . "\" multiple=\"multiple\" size=\"10\" style=\"width : 100%\">"; - $result = db_query($this->link, "SELECT DISTINCT tag_name FROM ttrss_tags WHERE owner_uid = ".$_SESSION['uid']." + $result = $this->dbh->query("SELECT DISTINCT tag_name FROM ttrss_tags WHERE owner_uid = ".$_SESSION['uid']." AND LENGTH(tag_name) <= 30 ORDER BY tag_name ASC"); - while ($row = db_fetch_assoc($result)) { + while ($row = $this->dbh->fetch_assoc($result)) { $tmp = htmlspecialchars($row["tag_name"]); print "<option value=\"" . str_replace(" ", "%20", $tmp) . "\">$tmp</option>"; } @@ -543,23 +190,19 @@ class Dlg extends Handler_Protected { __('Close this window') . "</button>"; print "</div>"; - print "]]></content>"; } function generatedFeed() { - print "<title>".__('View as RSS')."</title>"; - print "<content><![CDATA["; - $this->params = explode(":", $this->param, 3); - $feed_id = db_escape_string($this->params[0]); + $feed_id = $this->dbh->escape_string($this->params[0]); $is_cat = (bool) $this->params[1]; - $key = get_feed_access_key($this->link, $feed_id, $is_cat); + $key = get_feed_access_key($feed_id, $is_cat); $url_path = htmlspecialchars($this->params[2]) . "&key=" . $key; - print "<div class=\"dialogNotice\">" . __("You can view this feed as RSS using the following URL:") . "</div>"; + print "<h2>".__("You can view this feed as RSS using the following URL:")."</h2>"; print "<div class=\"tagCloudContainer\">"; print "<a id='gen_feed_url' href='$url_path' target='_blank'>$url_path</a>"; @@ -574,14 +217,13 @@ class Dlg extends Handler_Protected { __('Close this window')."</button>"; print "</div>"; - print "]]></content>"; //return; } function newVersion() { - $version_data = check_for_update($this->link); + $version_data = check_for_update(); $version = $version_data['version']; $id = $version_data['version_id']; @@ -600,7 +242,7 @@ class Dlg extends Handler_Protected { print "<div style='text-align : center'>"; print "<button dojoType=\"dijit.form.Button\" - onclick=\"return window.open('$details')\">".__("Details")."</button>"; + onclick=\"return window.open('$details')\">".__("See the release notes")."</button>"; print "<button dojoType=\"dijit.form.Button\" onclick=\"return window.open('$download')\">".__("Download")."</button>"; print "<button dojoType=\"dijit.form.Button\" @@ -625,132 +267,6 @@ class Dlg extends Handler_Protected { } - function customizeCSS() { - $value = get_pref($this->link, "USER_STYLESHEET"); - - $value = str_replace("<br/>", "\n", $value); - - print "<div class=\"dialogNotice\">"; - print T_sprintf("You can override colors, fonts and layout of your currently selected theme with custom CSS declarations here. <a target=\"_blank\" class=\"visibleLink\" href=\"%s\">This file</a> can be used as a baseline.", "tt-rss.css"); - print "</div>"; - - print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"rpc\">"; - print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"setpref\">"; - print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"key\" value=\"USER_STYLESHEET\">"; - - print "<table width='100%'><tr><td>"; - print "<textarea dojoType=\"dijit.form.SimpleTextarea\" - style='font-size : 12px; width : 100%; height: 200px;' - placeHolder='body#ttrssMain { font-size : 14px; };' - name='value'>$value</textarea>"; - print "</td></tr></table>"; - - print "<div class='dlgButtons'>"; - print "<button dojoType=\"dijit.form.Button\" - onclick=\"dijit.byId('cssEditDlg').execute()\">".__('Save')."</button> "; - print "<button dojoType=\"dijit.form.Button\" - onclick=\"dijit.byId('cssEditDlg').hide()\">".__('Cancel')."</button>"; - print "</div>"; - - } - - function addInstance() { - print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-instances\">"; - print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"add\">"; - - print "<div class=\"dlgSec\">".__("Instance")."</div>"; - - print "<div class=\"dlgSecCont\">"; - - /* URL */ - - print __("URL:") . " "; - - print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\" - placeHolder=\"".__("Instance URL")."\" - regExp='^(http|https)://.*' - style=\"font-size : 16px; width: 20em\" name=\"access_url\">"; - - print "<hr/>"; - - $access_key = sha1(uniqid(rand(), true)); - - /* Access key */ - - print __("Access key:") . " "; - - print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\" - placeHolder=\"".__("Access key")."\" regExp='\w{40}' - style=\"width: 20em\" name=\"access_key\" id=\"instance_add_key\" - value=\"$access_key\">"; - - print "<p class='insensitive'>" . __("Use one access key for both linked instances."); - - print "</div>"; - - print "<div class=\"dlgButtons\"> - <div style='float : left'> - <button dojoType=\"dijit.form.Button\" - onclick=\"return dijit.byId('instanceAddDlg').regenKey()\">". - __('Generate new key')."</button> - </div> - <button dojoType=\"dijit.form.Button\" - onclick=\"return dijit.byId('instanceAddDlg').execute()\">". - __('Create link')."</button> - <button dojoType=\"dijit.form.Button\" - onclick=\"return dijit.byId('instanceAddDlg').hide()\"\">". - __('Cancel')."</button></div>"; - - return; - } - - function batchSubscribe() { - print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"rpc\">"; - print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"batchaddfeeds\">"; - - print "<table width='100%'><tr><td> - ".__("Add one valid RSS feed per line (no feed detection is done)")." - </td><td align='right'>"; - if (get_pref($this->link, 'ENABLE_FEED_CATS')) { - print __('Place in category:') . " "; - print_feed_cat_select($this->link, "cat", false, 'dojoType="dijit.form.Select"'); - } - print "</td></tr><tr><td colspan='2'>"; - print "<textarea - style='font-size : 12px; width : 100%; height: 200px;' - placeHolder=\"".__("Feeds to subscribe, One per line")."\" - dojoType=\"dijit.form.SimpleTextarea\" required=\"1\" name=\"feeds\"></textarea>"; - - print "</td></tr><tr><td colspan='2'>"; - - print "<div id='feedDlg_loginContainer' style='display : none'> - " . - " <input dojoType=\"dijit.form.TextBox\" name='login'\" - placeHolder=\"".__("Login")."\" - style=\"width : 10em;\"> ". - " <input - placeHolder=\"".__("Password")."\" - dojoType=\"dijit.form.TextBox\" type='password' - style=\"width : 10em;\" name='pass'\">". - "</div>"; - - print "</td></tr><tr><td colspan='2'>"; - - print "<div style=\"clear : both\"> - <input type=\"checkbox\" name=\"need_auth\" dojoType=\"dijit.form.CheckBox\" id=\"feedDlg_loginCheck\" - onclick='checkboxToggleElement(this, \"feedDlg_loginContainer\")'> - <label for=\"feedDlg_loginCheck\">". - __('Feeds require authentication.')."</div>"; - - print "</form>"; - - print "</td></tr></table>"; - - print "<div class=\"dlgButtons\"> - <button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('batchSubDlg').execute()\">".__('Subscribe')."</button> - <button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('batchSubDlg').hide()\">".__('Cancel')."</button> - </div>"; - } } ?> diff --git a/classes/feedenclosure.php b/classes/feedenclosure.php new file mode 100644 index 000000000..d610dd7c8 --- /dev/null +++ b/classes/feedenclosure.php @@ -0,0 +1,7 @@ +<?php +class FeedEnclosure { + public $link; + public $type; + public $length; +} +?> diff --git a/classes/feeditem.php b/classes/feeditem.php new file mode 100644 index 000000000..e64a13fdc --- /dev/null +++ b/classes/feeditem.php @@ -0,0 +1,15 @@ +<?php +abstract class FeedItem { + abstract function get_id(); + abstract function get_date(); + abstract function get_link(); + abstract function get_title(); + abstract function get_description(); + abstract function get_content(); + abstract function get_comments_url(); + abstract function get_comments_count(); + abstract function get_categories(); + abstract function get_enclosures(); + abstract function get_author(); +} +?> diff --git a/classes/feeditem/atom.php b/classes/feeditem/atom.php new file mode 100644 index 000000000..b981dc319 --- /dev/null +++ b/classes/feeditem/atom.php @@ -0,0 +1,109 @@ +<?php +class FeedItem_Atom extends FeedItem_Common { + function get_id() { + $id = $this->elem->getElementsByTagName("id")->item(0); + + if ($id) { + return $id->nodeValue; + } else { + return $this->get_link(); + } + } + + function get_date() { + $updated = $this->elem->getElementsByTagName("updated")->item(0); + + if ($updated) { + return strtotime($updated->nodeValue); + } + } + + function get_link() { + $links = $this->elem->getElementsByTagName("link"); + + foreach ($links as $link) { + if ($link && $link->hasAttribute("href") && (!$link->hasAttribute("rel") + || $link->getAttribute("rel") == "alternate")) { + return $link->getAttribute("href"); + } + } + } + + function get_title() { + $title = $this->elem->getElementsByTagName("title")->item(0); + + if ($title) { + return $title->nodeValue; + } + } + + function get_content() { + $content = $this->elem->getElementsByTagName("content")->item(0); + + if ($content) { + return $content->nodeValue; + } + } + + function get_description() { + $summary = $this->elem->getElementsByTagName("summary")->item(0); + + if ($summary) { + return $summary->nodeValue; + } + } + + function get_categories() { + $categories = $this->elem->getElementsByTagName("category"); + $cats = array(); + + foreach ($categories as $cat) { + if ($cat->hasAttribute("term")) + array_push($cats, $cat->getAttribute("term")); + } + + $categories = $this->xpath->query("dc:subject", $this->elem); + + foreach ($categories as $cat) { + array_push($cats, $cat->nodeValue); + } + + return $cats; + } + + function get_enclosures() { + $links = $this->elem->getElementsByTagName("link"); + + $encs = array(); + + foreach ($links as $link) { + if ($link && $link->hasAttribute("href") && $link->hasAttribute("rel")) { + if ($link->getAttribute("rel") == "enclosure") { + $enc = new FeedEnclosure(); + + $enc->type = $link->getAttribute("type"); + $enc->link = $link->getAttribute("href"); + $enc->length = $link->getAttribute("length"); + + array_push($encs, $enc); + } + } + } + + $enclosures = $this->xpath->query("media:content", $this->elem); + + foreach ($enclosures as $enclosure) { + $enc = new FeedEnclosure(); + + $enc->type = $enclosure->getAttribute("type"); + $enc->link = $enclosure->getAttribute("url"); + $enc->length = $enclosure->getAttribute("length"); + + array_push($encs, $enc); + } + + return $encs; + } + +} +?> diff --git a/classes/feeditem/common.php b/classes/feeditem/common.php new file mode 100644 index 000000000..0787a42cb --- /dev/null +++ b/classes/feeditem/common.php @@ -0,0 +1,51 @@ +<?php +abstract class FeedItem_Common extends FeedItem { + protected $elem; + protected $xpath; + protected $doc; + + function __construct($elem, $doc, $xpath) { + $this->elem = $elem; + $this->xpath = $xpath; + $this->doc = $doc; + } + + function get_author() { + $author = $this->elem->getElementsByTagName("author")->item(0); + + if ($author) { + $name = $author->getElementsByTagName("name")->item(0); + + if ($name) return $name->nodeValue; + + $email = $author->getElementsByTagName("email")->item(0); + + if ($email) return $email->nodeValue; + + if ($author->nodeValue) + return $author->nodeValue; + } + + $author = $this->xpath->query("dc:creator", $this->elem)->item(0); + + if ($author) { + return $author->nodeValue; + } + } + + // todo + function get_comments_url() { + + } + + function get_comments_count() { + $comments = $this->xpath->query("slash:comments", $this->elem)->item(0); + + if ($comments) { + return $comments->nodeValue; + } + } + + +} +?> diff --git a/classes/feeditem/rss.php b/classes/feeditem/rss.php new file mode 100644 index 000000000..2f363b4f6 --- /dev/null +++ b/classes/feeditem/rss.php @@ -0,0 +1,107 @@ +<?php +class FeedItem_RSS extends FeedItem_Common { + function get_id() { + $id = $this->elem->getElementsByTagName("guid")->item(0); + + if ($id) { + return $id->nodeValue; + } else { + return $this->get_link(); + } + } + + function get_date() { + $pubDate = $this->elem->getElementsByTagName("pubDate")->item(0); + + if ($pubDate) { + return strtotime($pubDate->nodeValue); + } + } + + function get_link() { + $link = $this->elem->getElementsByTagName("link")->item(0); + + if ($link) { + return $link->nodeValue; + } + } + + function get_title() { + $title = $this->elem->getElementsByTagName("title")->item(0); + + if ($title) { + return $title->nodeValue; + } + } + + function get_content() { + $content = $this->xpath->query("content:encoded", $this->elem)->item(0); + + if ($content) { + return $content->nodeValue; + } + + $content = $this->elem->getElementsByTagName("description")->item(0); + + if ($content) { + return $content->nodeValue; + } + } + + function get_description() { + $summary = $this->elem->getElementsByTagName("description")->item(0); + + if ($summary) { + return $summary->nodeValue; + } + } + + function get_categories() { + $categories = $this->elem->getElementsByTagName("category"); + $cats = array(); + + foreach ($categories as $cat) { + array_push($cats, $cat->nodeValue); + } + + $categories = $this->xpath->query("dc:subject", $this->elem); + + foreach ($categories as $cat) { + array_push($cats, $cat->nodeValue); + } + + return $cats; + } + + function get_enclosures() { + $enclosures = $this->elem->getElementsByTagName("enclosure"); + + $encs = array(); + + foreach ($enclosures as $enclosure) { + $enc = new FeedEnclosure(); + + $enc->type = $enclosure->getAttribute("type"); + $enc->link = $enclosure->getAttribute("url"); + $enc->length = $enclosure->getAttribute("length"); + + array_push($encs, $enc); + } + + $enclosures = $this->xpath->query("media:content", $this->elem); + + foreach ($enclosures as $enclosure) { + $enc = new FeedEnclosure(); + + $enc->type = $enclosure->getAttribute("type"); + $enc->link = $enclosure->getAttribute("url"); + $enc->length = $enclosure->getAttribute("length"); + + array_push($encs, $enc); + } + + return $encs; + } + +} +?> diff --git a/classes/feedparser.php b/classes/feedparser.php new file mode 100644 index 000000000..bd67ca39d --- /dev/null +++ b/classes/feedparser.php @@ -0,0 +1,196 @@ +<?php +class FeedParser { + private $doc; + private $error; + private $items; + private $link; + private $title; + private $type; + private $xpath; + + const FEED_RDF = 0; + const FEED_RSS = 1; + const FEED_ATOM = 2; + + function __construct($data) { + libxml_use_internal_errors(true); + libxml_clear_errors(); + $this->doc = new DOMDocument(); + $this->doc->loadXML($data); + $this->error = $this->format_error(libxml_get_last_error()); + libxml_clear_errors(); + + $this->items = array(); + } + + function init() { + $root = $this->doc->firstChild; + $xpath = new DOMXPath($this->doc); + $xpath->registerNamespace('atom', 'http://www.w3.org/2005/Atom'); + $xpath->registerNamespace('atom03', 'http://purl.org/atom/ns#'); + $xpath->registerNamespace('media', 'http://search.yahoo.com/mrss/'); + $xpath->registerNamespace('rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'); + $xpath->registerNamespace('slash', 'http://purl.org/rss/1.0/modules/slash/'); + $xpath->registerNamespace('dc', 'http://purl.org/dc/elements/1.1/'); + $xpath->registerNamespace('content', 'http://purl.org/rss/1.0/modules/content/'); + + $this->xpath = $xpath; + + $root = $xpath->query("(//atom03:feed|//atom:feed|//channel|//rdf:rdf|//rdf:RDF)")->item(0); + + if ($root) { + switch (mb_strtolower($root->tagName)) { + case "rdf:rdf": + $this->type = $this::FEED_RDF; + break; + case "channel": + $this->type = $this::FEED_RSS; + break; + case "feed": + $this->type = $this::FEED_ATOM; + break; + default: + if( !isset($this->error) ){ + $this->error = "Unknown/unsupported feed type"; + } + return; + } + + switch ($this->type) { + case $this::FEED_ATOM: + + $title = $xpath->query("//atom:feed/atom:title")->item(0); + + if (!$title) + $title = $xpath->query("//atom03:feed/atom03:title")->item(0); + + + if ($title) { + $this->title = $title->nodeValue; + } + + $link = $xpath->query("//atom:feed/atom:link[not(@rel)]")->item(0); + + if (!$link) + $link = $xpath->query("//atom03:feed/atom03:link[not(@rel)]")->item(0); + + + if ($link && $link->hasAttributes()) { + $this->link = $link->getAttribute("href"); + } + + $articles = $xpath->query("//atom:entry"); + + if (!$articles || $articles->length == 0) + $articles = $xpath->query("//atom03:entry"); + + foreach ($articles as $article) { + array_push($this->items, new FeedItem_Atom($article, $this->doc, $this->xpath)); + } + + break; + case $this::FEED_RSS: + + $title = $xpath->query("//channel/title")->item(0); + + if ($title) { + $this->title = $title->nodeValue; + } + + $link = $xpath->query("//channel/link")->item(0); + + if ($link && $link->hasAttributes()) { + $this->link = $link->getAttribute("href"); + } + + $articles = $xpath->query("//channel/item"); + + foreach ($articles as $article) { + array_push($this->items, new FeedItem_RSS($article, $this->doc, $this->xpath)); + } + + break; + case $this::FEED_RDF: + $xpath->registerNamespace('rssfake', 'http://purl.org/rss/1.0/'); + + $title = $xpath->query("//rssfake:channel/rssfake:title")->item(0); + + if ($title) { + $this->title = $title->nodeValue; + } + + $link = $xpath->query("//rssfake:channel/rssfake:link")->item(0); + + if ($link) { + $this->link = $link->nodeValue; + } + + $articles = $xpath->query("//rssfake:item"); + + foreach ($articles as $article) { + array_push($this->items, new FeedItem_RSS($article, $this->doc, $this->xpath)); + } + + break; + + } + } else { + if( !isset($this->error) ){ + $this->error = "Unknown/unsupported feed type"; + } + return; + } + } + + function format_error($error) { + if ($error) { + return sprintf("LibXML error %s at line %d (column %d): %s", + $error->code, $error->line, $error->column, + $error->message); + } else { + return ""; + } + } + + function error() { + return $this->error; + } + + function get_link() { + return $this->link; + } + + function get_title() { + return $this->title; + } + + function get_items() { + return $this->items; + } + + function get_links($rel) { + $rv = array(); + + switch ($this->type) { + case $this::FEED_ATOM: + $links = $this->xpath->query("//atom:feed/atom:link"); + + foreach ($links as $link) { + if (!$rel || $link->hasAttribute('rel') && $link->getAttribute('rel') == $rel) { + array_push($rv, $link->getAttribute('href')); + } + } + break; + case $this::FEED_RSS: + $links = $this->xpath->query("//channel/link"); + foreach ($links as $link) { + if (!$rel || $link->hasAttribute('rel') && $link->getAttribute('rel') == $rel) { + array_push($rv, $link->getAttribute('href')); + } + } + break; + } + + return $rv; + } +} ?> diff --git a/classes/feeds.php b/classes/feeds.php index e96aaba30..5ca607c58 100644 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -4,26 +4,14 @@ require_once "colors.php"; class Feeds extends Handler_Protected { function csrf_ignore($method) { - $csrf_ignored = array("index"); + $csrf_ignored = array("index", "feedbrowser", "quickaddfeed", "search"); return array_search($method, $csrf_ignored) !== false; } - private function make_gradient($end, $class) { - $start = $class == "even" ? "#f0f0f0" : "#ffffff"; - - return "style='background: linear-gradient(left , $start 6%, $end 100%); - background: -o-linear-gradient(left , $start 6%, $end 100%); - background: -moz-linear-gradient(left , $start 6%, $end 100%); - background: -webkit-linear-gradient(left , $start 6%, $end 100%); - background: -ms-linear-gradient(left , $start 6%, $end 100%); - background: -webkit-gradient(linear, left top, right top, - color-stop(0.06, $start), color-stop(1, $end));'"; - } - private function format_headline_subtoolbar($feed_site_url, $feed_title, - $feed_id, $is_cat, $search, $match_on, - $search_mode, $view_mode, $error) { + $feed_id, $is_cat, $search, + $search_mode, $view_mode, $error, $feed_last_updated) { $page_prev_link = "viewFeedGoPage(-1)"; $page_next_link = "viewFeedGoPage(1)"; @@ -50,7 +38,7 @@ class Feeds extends Handler_Protected { if ($is_cat) $cat_q = "&is_cat=$is_cat"; if ($search) { - $search_q = "&q=$search&m=$match_on&smode=$search_mode"; + $search_q = "&q=$search&smode=$search_mode"; } else { $search_q = ""; } @@ -61,11 +49,15 @@ class Feeds extends Handler_Protected { // right part $reply .= "<span class='r'>"; + $reply .= "<span id='selected_prompt'></span>"; $reply .= "<span id='feed_title'>"; if ($feed_site_url) { + $last_updated = T_sprintf("Last updated: %s", + $feed_last_updated); + $target = "target=\"_blank\""; - $reply .= "<a title=\"".__("Visit the website")."\" $target href=\"$feed_site_url\">". + $reply .= "<a title=\"$last_updated\" $target href=\"$feed_site_url\">". truncate_string($feed_title,30)."</a>"; if ($error) { @@ -81,7 +73,7 @@ class Feeds extends Handler_Protected { $reply .= " <a href=\"#\" title=\"".__("View as RSS feed")."\" - onclick=\"displayDlg('generatedFeed', '$feed_id:$is_cat:$rss_link')\"> + onclick=\"displayDlg('".__("View as RSS")."','generatedFeed', '$feed_id:$is_cat:$rss_link')\"> <img class=\"noborder\" style=\"vertical-align : middle\" src=\"images/pub_set.svg\"></a>"; $reply .= "</span>"; @@ -119,18 +111,21 @@ class Feeds extends Handler_Protected { } - global $pluginhost; - - if ($pluginhost->get_plugin("mail")) { + if (PluginHost::getInstance()->get_plugin("mail")) { $reply .= "<option value=\"emailArticle(false)\">".__('Forward by email'). "</option>"; } + if (PluginHost::getInstance()->get_plugin("mailto")) { + $reply .= "<option value=\"mailtoArticle(false)\">".__('Forward by email'). + "</option>"; + } + $reply .= "<option value=\"0\" disabled=\"1\">".__('Feed:')."</option>"; - $reply .= "<option value=\"catchupPage()\">".__('Mark as read')."</option>"; + //$reply .= "<option value=\"catchupPage()\">".__('Mark as read')."</option>"; - $reply .= "<option value=\"displayDlg('generatedFeed', '$feed_id:$is_cat:$rss_link')\">".__('View as RSS')."</option>"; + $reply .= "<option value=\"displayDlg('".__("View as RSS")."','generatedFeed', '$feed_id:$is_cat:$rss_link')\">".__('View as RSS')."</option>"; $reply .= "</select>"; @@ -138,6 +133,10 @@ class Feeds extends Handler_Protected { //$reply .= "</h2"; + foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_HEADLINE_TOOLBAR_BUTTON) as $p) { + echo $p->hook_headline_toolbar_button($feed_id, $is_cat); + } + return $reply; } @@ -152,6 +151,8 @@ class Feeds extends Handler_Protected { $reply = array(); + $rgba_cache = array(); + $timing_info = microtime(true); $topmost_article_ids = array(); @@ -164,58 +165,84 @@ class Feeds extends Handler_Protected { if ($method == "ForceUpdate" && $feed > 0 && is_numeric($feed)) { // Update the feed if required with some basic flood control - $result = db_query($this->link, + $result = $this->dbh->query( "SELECT cache_images,".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated FROM ttrss_feeds WHERE id = '$feed'"); - if (db_num_rows($result) != 0) { - $last_updated = strtotime(db_fetch_result($result, 0, "last_updated")); - $cache_images = sql_bool_to_bool(db_fetch_result($result, 0, "cache_images")); + if ($this->dbh->num_rows($result) != 0) { + $last_updated = strtotime($this->dbh->fetch_result($result, 0, "last_updated")); + $cache_images = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "cache_images")); if (!$cache_images && time() - $last_updated > 120 || isset($_REQUEST['DevForceUpdate'])) { include "rssfuncs.php"; - update_rss_feed($this->link, $feed, true, true); + update_rss_feed($feed, true, true); } else { - db_query($this->link, "UPDATE ttrss_feeds SET last_updated = '1970-01-01', last_update_started = '1970-01-01' + $this->dbh->query("UPDATE ttrss_feeds SET last_updated = '1970-01-01', last_update_started = '1970-01-01' WHERE id = '$feed'"); } } } if ($method_split[0] == "MarkAllReadGR") { - catchup_feed($this->link, $method_split[1], false); + catchup_feed($method_split[1], false); } // FIXME: might break tag display? if (is_numeric($feed) && $feed > 0 && !$cat_view) { - $result = db_query($this->link, + $result = $this->dbh->query( "SELECT id FROM ttrss_feeds WHERE id = '$feed' LIMIT 1"); - if (db_num_rows($result) == 0) { + if ($this->dbh->num_rows($result) == 0) { $reply['content'] = "<div align='center'>".__('Feed not found.')."</div>"; } } - @$search = db_escape_string($_REQUEST["query"]); + @$search = $this->dbh->escape_string($_REQUEST["query"]); if ($search) { $disable_cache = true; } - @$search_mode = db_escape_string($_REQUEST["search_mode"]); - $match_on = "both"; // deprecated, TODO: remove + @$search_mode = $this->dbh->escape_string($_REQUEST["search_mode"]); if ($_REQUEST["debug"]) $timing_info = print_checkpoint("H0", $timing_info); // error_log("format_headlines_list: [" . $feed . "] method [" . $method . "]"); - if( $search_mode == '' && $method != '' ){ + if($search_mode == '' && $method != '' ){ $search_mode = $method; } // error_log("search_mode: " . $search_mode); - $qfh_ret = queryFeedHeadlines($this->link, $feed, $limit, $view_mode, $cat_view, - $search, $search_mode, $match_on, $override_order, $offset, 0, - false, 0, $include_children); + + if (!$cat_view && is_numeric($feed) && $feed < PLUGIN_FEED_BASE_INDEX && $feed > LABEL_BASE_INDEX) { + $handler = PluginHost::getInstance()->get_feed_handler( + PluginHost::feed_to_pfeed_id($feed)); + + // function queryFeedHeadlines($feed, $limit, $view_mode, $cat_view, $search, $search_mode, $override_order = false, $offset = 0, $owner_uid = 0, $filter = false, $since_id = 0, $include_children = false, $ignore_vfeed_group = false) { + + if ($handler) { + $options = array( + "limit" => $limit, + "view_mode" => $view_mode, + "cat_view" => $cat_view, + "search" => $search, + "search_mode" => $search_mode, + "override_order" => $override_order, + "offset" => $offset, + "owner_uid" => $_SESSION["uid"], + "filter" => false, + "since_id" => 0, + "include_children" => $include_children); + + $qfh_ret = $handler->get_headlines(PluginHost::feed_to_pfeed_id($feed), + $options); + } + + } else { + $qfh_ret = queryFeedHeadlines($feed, $limit, $view_mode, $cat_view, + $search, $search_mode, $override_order, $offset, 0, + false, 0, $include_children); + } if ($_REQUEST["debug"]) $timing_info = print_checkpoint("H1", $timing_info); @@ -223,49 +250,48 @@ class Feeds extends Handler_Protected { $feed_title = $qfh_ret[1]; $feed_site_url = $qfh_ret[2]; $last_error = $qfh_ret[3]; + $last_updated = strpos($qfh_ret[4], '1970-') === FALSE ? + make_local_datetime($qfh_ret[4], false) : __("Never"); $vgroup_last_feed = $vgr_last_feed; $reply['toolbar'] = $this->format_headline_subtoolbar($feed_site_url, $feed_title, - $feed, $cat_view, $search, $match_on, $search_mode, $view_mode, - $last_error); + $feed, $cat_view, $search, $search_mode, $view_mode, + $last_error, $last_updated); - $headlines_count = db_num_rows($result); + $headlines_count = $this->dbh->num_rows($result); - /* if (get_pref($this->link, 'COMBINED_DISPLAY_MODE')) { + /* if (get_pref('COMBINED_DISPLAY_MODE')) { $button_plugins = array(); foreach (explode(",", ARTICLE_BUTTON_PLUGINS) as $p) { $pclass = "button_" . trim($p); if (class_exists($pclass)) { - $plugin = new $pclass($link); + $plugin = new $pclass(); array_push($button_plugins, $plugin); } } } */ - global $pluginhost; - - if (db_num_rows($result) > 0) { + if ($this->dbh->num_rows($result) > 0) { $lnum = $offset; $num_unread = 0; $cur_feed_title = ''; - $fresh_intl = get_pref($this->link, "FRESH_ARTICLE_MAX_AGE") * 60 * 60; + $fresh_intl = get_pref("FRESH_ARTICLE_MAX_AGE") * 60 * 60; if ($_REQUEST["debug"]) $timing_info = print_checkpoint("PS", $timing_info); - while ($line = db_fetch_assoc($result)) { - $class = ($lnum % 2) ? "even" : "odd"; + $expand_cdm = get_pref('CDM_EXPANDED'); + while ($line = $this->dbh->fetch_assoc($result)) { $id = $line["id"]; $feed_id = $line["feed_id"]; $label_cache = $line["label_cache"]; $labels = false; - $label_row_style = ""; if ($label_cache) { $label_cache = json_decode($label_cache, true); @@ -278,23 +304,7 @@ class Feeds extends Handler_Protected { } } - if (!is_array($labels)) $labels = get_article_labels($this->link, $id); - - if (count($labels) > 0) { - for ($i = 0; $i < min(4, count($labels)); $i++) { - $bg = rgb2hsl(_color_unpack($labels[$i][3])); - - if ($bg && $bg[1] > 0) { - $bg[1] = 0.1; - $bg[2] = 1; - - $bg = _color_pack(hsl2rgb($bg)); - $label_row_style = $this->make_gradient($bg, $class);; - - break; - } - } - } + if (!is_array($labels)) $labels = get_article_labels($id); $labels_str = "<span id=\"HLLCTR-$id\">"; $labels_str .= format_article_labels($labels, $id); @@ -304,36 +314,35 @@ class Feeds extends Handler_Protected { array_push($topmost_article_ids, $id); } - if ($line["unread"] == "t" || $line["unread"] == "1") { + $class = ""; + + if (sql_bool_to_bool($line["unread"])) { $class .= " Unread"; ++$num_unread; - $is_unread = true; - } else { - $is_unread = false; } - if ($line["marked"] == "t" || $line["marked"] == "1") { - $marked_pic = "<img id=\"FMPIC-$id\" - src=\"".theme_image($this->link, 'images/mark_set.svg')."\" + if (sql_bool_to_bool($line["marked"])) { + $marked_pic = "<img + src=\"images/mark_set.svg\" class=\"markedPic\" alt=\"Unstar article\" - onclick='javascript:toggleMark($id)'>"; + onclick='toggleMark($id)'>"; + $class .= " marked"; } else { - $marked_pic = "<img id=\"FMPIC-$id\" - src=\"".theme_image($this->link, 'images/mark_unset.svg')."\" + $marked_pic = "<img + src=\"images/mark_unset.svg\" class=\"markedPic\" alt=\"Star article\" - onclick='javascript:toggleMark($id)'>"; + onclick='toggleMark($id)'>"; } - if ($line["published"] == "t" || $line["published"] == "1") { - $published_pic = "<img id=\"FPPIC-$id\" src=\"".theme_image($this->link, - 'images/pub_set.svg')."\" - class=\"markedPic\" - alt=\"Unpublish article\" onclick='javascript:togglePub($id)'>"; + if (sql_bool_to_bool($line["published"])) { + $published_pic = "<img src=\"images/pub_set.svg\" + class=\"pubPic\" + alt=\"Unpublish article\" onclick='togglePub($id)'>"; + $class .= " published"; } else { - $published_pic = "<img id=\"FPPIC-$id\" src=\"".theme_image($this->link, - 'images/pub_unset.svg')."\" - class=\"markedPic\" - alt=\"Publish article\" onclick='javascript:togglePub($id)'>"; + $published_pic = "<img src=\"images/pub_unset.svg\" + class=\"pubPic\" + alt=\"Publish article\" onclick='togglePub($id)'>"; } # $content_link = "<a target=\"_blank\" href=\"".$line["link"]."\">" . @@ -347,17 +356,18 @@ class Feeds extends Handler_Protected { # $content_link = "<a href=\"javascript:viewContentUrl('".$line["link"]."');\">" . # $line["title"] . "</a>"; - $updated_fmt = make_local_datetime($this->link, $line["updated_noms"], false); + $updated_fmt = make_local_datetime($line["updated"], false); + $date_entered_fmt = T_sprintf("Imported at %s", + make_local_datetime($line["date_entered"], false)); - if (get_pref($this->link, 'SHOW_CONTENT_PREVIEW')) { + if (get_pref('SHOW_CONTENT_PREVIEW')) { $content_preview = truncate_string(strip_tags($line["content_preview"]), - 100); + 250); } $score = $line["score"]; - $score_pic = theme_image($this->link, - "images/" . get_score_pic($score)); + $score_pic = "images/" . get_score_pic($score); /* $score_title = __("(Click to change)"); $score_pic = "<img class='hlScorePic' src=\"images/$score_pic\" @@ -377,7 +387,7 @@ class Feeds extends Handler_Protected { $entry_author = $line["author"]; if ($entry_author) { - $entry_author = " - $entry_author"; + $entry_author = " — $entry_author"; } $has_feed_icon = feed_has_icon($feed_id); @@ -388,9 +398,22 @@ class Feeds extends Handler_Protected { $feed_icon_img = "<img class=\"tinyFeedIcon\" src=\"images/pub_set.svg\" alt=\"\">"; } - if (!get_pref($this->link, 'COMBINED_DISPLAY_MODE')) { + $entry_site_url = $line["site_url"]; + + //setting feed headline background color, needs to change text color based on dark/light + $fav_color = $line['favicon_avg_color']; + + require_once "colors.php"; + + if ($fav_color && $fav_color != 'fail') { + if (!isset($rgba_cache[$feed_id])) { + $rgba_cache[$feed_id] = join(",", _color_unpack($fav_color)); + } + } + + if (!get_pref('COMBINED_DISPLAY_MODE')) { - if (get_pref($this->link, 'VFEED_GROUP_BY_FEED')) { + if (get_pref('VFEED_GROUP_BY_FEED')) { if ($feed_id != $vgroup_last_feed && $line["feed_title"]) { $cur_feed_title = $line["feed_title"]; @@ -398,26 +421,26 @@ class Feeds extends Handler_Protected { $cur_feed_title = htmlspecialchars($cur_feed_title); - $vf_catchup_link = "(<a onclick='catchupFeedInGroup($feed_id);' href='#'>".__('mark as read')."</a>)"; + $vf_catchup_link = "(<a class='catchup' onclick='catchupFeedInGroup($feed_id);' href='#'>".__('Mark as read')."</a>)"; $reply['content'] .= "<div class='cdmFeedTitle'>". "<div style=\"float : right\">$feed_icon_img</div>". - "<a href=\"#\" onclick=\"viewfeed($feed_id)\">". + "<a class='title' href=\"#\" onclick=\"viewfeed($feed_id)\">". $line["feed_title"]."</a> $vf_catchup_link</div>"; } } - $mouseover_attrs = "onmouseover='postMouseIn($id)' + $mouseover_attrs = "onmouseover='postMouseIn(event, $id)' onmouseout='postMouseOut($id)'"; - $reply['content'] .= "<div class='$class' id='RROW-$id' $label_row_style $mouseover_attrs>"; + $reply['content'] .= "<div class='hl $class' id='RROW-$id' $mouseover_attrs>"; $reply['content'] .= "<div class='hlLeft'>"; $reply['content'] .= "<input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" onclick=\"toggleSelectRow2(this)\" - id=\"RCHK-$id\">"; + class='rchk'>"; $reply['content'] .= "$marked_pic"; $reply['content'] .= "$published_pic"; @@ -426,12 +449,12 @@ class Feeds extends Handler_Protected { $reply['content'] .= "<div onclick='return hlClicked(event, $id)' class=\"hlTitle\"><span class='hlContent$hlc_suffix'>"; - $reply['content'] .= "<a id=\"RTITLE-$id\" + $reply['content'] .= "<a id=\"RTITLE-$id\" class=\"title\" href=\"" . htmlspecialchars($line["link"]) . "\" onclick=\"\">" . truncate_string($line["title"], 200); - if (get_pref($this->link, 'SHOW_CONTENT_PREVIEW')) { + if (get_pref('SHOW_CONTENT_PREVIEW')) { if ($content_preview) { $reply['content'] .= "<span class=\"contentPreview\"> - $content_preview</span>"; } @@ -445,19 +468,23 @@ class Feeds extends Handler_Protected { $reply['content'] .= "<span class=\"hlUpdated\">"; - if (@$line["feed_title"]) { - $reply['content'] .= "<span class=\"hlFeed\"> - <a href=\"#\" onclick=\"viewfeed($feed_id)\">". - $line["feed_title"]."</a><br/> - </span>"; + if (!get_pref('VFEED_GROUP_BY_FEED')) { + if (@$line["feed_title"]) { + $rgba = @$rgba_cache[$feed_id]; + + $reply['content'] .= "<a class=\"hlFeed\" style=\"background : rgba($rgba, 0.3)\" href=\"#\" onclick=\"viewfeed($feed_id)\">". + truncate_string($line["feed_title"],30)."</a>"; } + } + + $reply['content'] .= "<div title='$date_entered_fmt'>$updated_fmt</div> + </span>"; - $reply['content'] .= "$updated_fmt</span>"; $reply['content'] .= "<div class=\"hlRight\">"; $reply['content'] .= $score_pic; - if ($line["feed_title"] && !get_pref($this->link, 'VFEED_GROUP_BY_FEED')) { + if ($line["feed_title"] && !get_pref('VFEED_GROUP_BY_FEED')) { $reply['content'] .= "<span onclick=\"viewfeed($feed_id)\" style=\"cursor : pointer\" @@ -470,17 +497,19 @@ class Feeds extends Handler_Protected { } else { - $line["tags"] = get_article_tags($this->link, $id, $_SESSION["uid"], $line["tag_cache"]); - unset($line["tag_cache"]); + if ($line["tag_cache"]) + $tags = explode(",", $line["tag_cache"]); + else + $tags = false; - $line["content"] = sanitize($this->link, $line["content_preview"], - false, false, $feed_site_url); + $line["content"] = sanitize($line["content_preview"], + sql_bool_to_bool($line['hide_images']), false, $entry_site_url); - foreach ($pluginhost->get_hooks($pluginhost::HOOK_RENDER_ARTICLE_CDM) as $p) { + foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE_CDM) as $p) { $line = $p->hook_render_article_cdm($line); } - if (get_pref($this->link, 'VFEED_GROUP_BY_FEED') && $line["feed_title"]) { + if (get_pref('VFEED_GROUP_BY_FEED') && $line["feed_title"]) { if ($feed_id != $vgroup_last_feed) { $cur_feed_title = $line["feed_title"]; @@ -488,7 +517,7 @@ class Feeds extends Handler_Protected { $cur_feed_title = htmlspecialchars($cur_feed_title); - $vf_catchup_link = "(<a onclick='javascript:catchupFeedInGroup($feed_id);' href='#'>".__('mark as read')."</a>)"; + $vf_catchup_link = "(<a class='catchup' onclick='javascript:catchupFeedInGroup($feed_id);' href='#'>".__('mark as read')."</a>)"; $has_feed_icon = feed_has_icon($feed_id); @@ -500,47 +529,46 @@ class Feeds extends Handler_Protected { $reply['content'] .= "<div class='cdmFeedTitle'>". "<div style=\"float : right\">$feed_icon_img</div>". - "<a href=\"#\" onclick=\"viewfeed($feed_id)\">". + "<a href=\"#\" class='title' onclick=\"viewfeed($feed_id)\">". $line["feed_title"]."</a> $vf_catchup_link</div>"; } } - $expand_cdm = get_pref($this->link, 'CDM_EXPANDED'); - - $mouseover_attrs = "onmouseover='postMouseIn($id)' + $mouseover_attrs = "onmouseover='postMouseIn(event, $id)' onmouseout='postMouseOut($id)'"; - $reply['content'] .= "<div class=\"cdm $class\" - id=\"RROW-$id\" $mouseover_attrs'>"; + $expanded_class = $expand_cdm ? "expanded" : "expandable"; - $reply['content'] .= "<div class=\"cdmHeader\">"; + $reply['content'] .= "<div class=\"cdm $expanded_class $class\" + id=\"RROW-$id\" $mouseover_attrs>"; + $reply['content'] .= "<div class=\"cdmHeader\" style=\"$row_background\">"; $reply['content'] .= "<div style=\"vertical-align : middle\">"; $reply['content'] .= "<input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" onclick=\"toggleSelectRow2(this, false, true)\" - id=\"RCHK-$id\">"; + class='rchk'>"; $reply['content'] .= "$marked_pic"; $reply['content'] .= "$published_pic"; $reply['content'] .= "</div>"; - $reply['content'] .= "<div id=\"PTITLE-FULL-$id\" style=\"display : none\">" . - htmlspecialchars(strip_tags($line['title'])) . "</div>"; - $reply['content'] .= "<span id=\"RTITLE-$id\" onclick=\"return cdmClicked(event, $id);\" class=\"titleWrap$hlc_suffix\"> <a class=\"title\" - title=\"".htmlspecialchars($line['title'])."\" target=\"_blank\" href=\"". htmlspecialchars($line["link"])."\">". $line["title"] . - " $entry_author</a>"; + " <span class=\"author\">$entry_author</span></a>"; $reply['content'] .= $labels_str; + $reply['content'] .= "<span class='collapseBtn' style='display : none'> + <img src=\"images/collapse.png\" onclick=\"cdmCollapseArticle(event, $id)\" + title=\"".__("Collapse article")."\"/></span>"; + if (!$expand_cdm) $content_hidden = "style=\"display : none\""; else @@ -548,24 +576,27 @@ class Feeds extends Handler_Protected { $reply['content'] .= "<span $excerpt_hidden id=\"CEXC-$id\" class=\"cdmExcerpt\"> - $content_preview</span>"; - $reply['content'] .= "</span>"; - if (!get_pref($this->link, 'VFEED_GROUP_BY_FEED')) { + if (!get_pref('VFEED_GROUP_BY_FEED')) { if (@$line["feed_title"]) { + $rgba = @$rgba_cache[$feed_id]; + $reply['content'] .= "<div class=\"hlFeed\"> - <a href=\"#\" onclick=\"viewfeed($feed_id)\">". - $line["feed_title"]."</a> + <a href=\"#\" style=\"background-color: rgba($rgba,0.3)\" + onclick=\"viewfeed($feed_id)\">". + truncate_string($line["feed_title"],30)."</a> </div>"; } } - $reply['content'] .= "<span class='updated'>$updated_fmt</span>"; + $reply['content'] .= "<span class='updated' title='$date_entered_fmt'> + $updated_fmt</span>"; $reply['content'] .= "<div style=\"vertical-align : middle\">"; $reply['content'] .= "$score_pic"; - if (!get_pref($this->link, "VFEED_GROUP_BY_FEED") && $line["feed_title"]) { + if (!get_pref("VFEED_GROUP_BY_FEED") && $line["feed_title"]) { $reply['content'] .= "<span style=\"cursor : pointer\" title=\"".htmlspecialchars($line["feed_title"])."\" onclick=\"viewfeed($feed_id)\">$feed_icon_img</span>"; @@ -584,22 +615,21 @@ class Feeds extends Handler_Protected { } $reply['content'] .= "</div>"; - $reply['content'] .= "<div class=\"cdmContentInner\">"; if ($line["orig_feed_id"]) { - $tmp_result = db_query($this->link, "SELECT * FROM ttrss_archived_feeds + $tmp_result = $this->dbh->query("SELECT * FROM ttrss_archived_feeds WHERE id = ".$line["orig_feed_id"]); - if (db_num_rows($tmp_result) != 0) { + if ($this->dbh->num_rows($tmp_result) != 0) { $reply['content'] .= "<div clear='both'>"; $reply['content'] .= __("Originally from:"); $reply['content'] .= " "; - $tmp_line = db_fetch_assoc($tmp_result); + $tmp_line = $this->dbh->fetch_assoc($tmp_result); $reply['content'] .= "<a target='_blank' href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" . @@ -614,36 +644,37 @@ class Feeds extends Handler_Protected { } } - $feed_site_url = $line["site_url"]; - $reply['content'] .= "<span id=\"CWRAP-$id\">"; - $reply['content'] .= $line["content"]; - $reply['content'] .= "</span>"; -/* $tmp_result = db_query($this->link, "SELECT always_display_enclosures FROM - ttrss_feeds WHERE id = ". - (($line['feed_id'] == null) ? $line['orig_feed_id'] : - $line['feed_id'])." AND owner_uid = ".$_SESSION["uid"]); +// if (!$expand_cdm) { + $reply['content'] .= "<span id=\"CENCW-$id\" style=\"display : none\">"; + $reply['content'] .= htmlspecialchars($line["content"]); + $reply['content'] .= "</span."; - $always_display_enclosures = sql_bool_to_bool(db_fetch_result($tmp_result, - 0, "always_display_enclosures")); */ +// } else { +// $reply['content'] .= $line["content"]; +// } + + $reply['content'] .= "</span>"; $always_display_enclosures = sql_bool_to_bool($line["always_display_enclosures"]); - $reply['content'] .= format_article_enclosures($this->link, $id, $always_display_enclosures, - $line["content"]); + $reply['content'] .= format_article_enclosures($id, $always_display_enclosures, $line["content"], sql_bool_to_bool($line["hide_images"])); $reply['content'] .= "</div>"; $reply['content'] .= "<div class=\"cdmFooter\">"; - $tags_str = format_tags_string($line["tags"], $id); + foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_LEFT_BUTTON) as $p) { + $reply['content'] .= $p->hook_article_left_button($line); + } + + $tags_str = format_tags_string($tags, $id); - $reply['content'] .= "<img src='".theme_image($this->link, - 'images/tag.png')."' alt='Tags' title='Tags'> + $reply['content'] .= "<img src='images/tag.png' alt='Tags' title='Tags'> <span id=\"ATSTR-$id\">$tags_str</span> <a title=\"".__('Edit tags for this article')."\" - href=\"#\" onclick=\"editArticleTags($id, $feed_id, true)\">(+)</a>"; + href=\"#\" onclick=\"editArticleTags($id)\">(+)</a>"; $num_comments = $line["num_comments"]; $entry_comments = ""; @@ -665,14 +696,17 @@ class Feeds extends Handler_Protected { $reply['content'] .= "<div style=\"float : right\">"; - foreach ($pluginhost->get_hooks($pluginhost::HOOK_ARTICLE_BUTTON) as $p) { +// $reply['content'] .= "$marked_pic"; +// $reply['content'] .= "$published_pic"; + + foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_BUTTON) as $p) { $reply['content'] .= $p->hook_article_button($line); } $reply['content'] .= "</div>"; $reply['content'] .= "</div>"; - $reply['content'] .= "</div>"; + $reply['content'] .= "</div><hr/>"; $reply['content'] .= "</div>"; @@ -697,8 +731,8 @@ class Feeds extends Handler_Protected { $message = __("No starred articles found to display."); break; default: - if ($feed < -10) { - $message = __("No articles found to display. You can assign articles to labels manually (see the Actions menu above) or use a filter."); + if ($feed < LABEL_BASE_INDEX) { + $message = __("No articles found to display. You can assign articles to labels manually from article header context menu (applies to all selected articles) or use a filter."); } else { $message = __("No articles found to display."); } @@ -707,20 +741,20 @@ class Feeds extends Handler_Protected { if (!$offset && $message) { $reply['content'] .= "<div class='whiteBox'>$message"; - $reply['content'] .= "<p class=\"small\"><span class=\"insensitive\">"; + $reply['content'] .= "<p><span class=\"insensitive\">"; - $result = db_query($this->link, "SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds + $result = $this->dbh->query("SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds WHERE owner_uid = " . $_SESSION['uid']); - $last_updated = db_fetch_result($result, 0, "last_updated"); - $last_updated = make_local_datetime($this->link, $last_updated, false); + $last_updated = $this->dbh->fetch_result($result, 0, "last_updated"); + $last_updated = make_local_datetime($last_updated, false); $reply['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated); - $result = db_query($this->link, "SELECT COUNT(id) AS num_errors + $result = $this->dbh->query("SELECT COUNT(id) AS num_errors FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]); - $num_errors = db_fetch_result($result, 0, "num_errors"); + $num_errors = $this->dbh->fetch_result($result, 0, "num_errors"); if ($num_errors > 0) { $reply['content'] .= "<br/>"; @@ -738,9 +772,9 @@ class Feeds extends Handler_Protected { } function catchupAll() { - db_query($this->link, "UPDATE ttrss_user_entries SET + $this->dbh->query("UPDATE ttrss_user_entries SET last_read = NOW(), unread = false WHERE unread = true AND owner_uid = " . $_SESSION["uid"]); - ccache_zero_all($this->link, $_SESSION["uid"]); + ccache_zero_all($_SESSION["uid"]); } function view() { @@ -750,17 +784,17 @@ class Feeds extends Handler_Protected { if ($_REQUEST["debug"]) $timing_info = print_checkpoint("0", $timing_info); - $omode = db_escape_string($_REQUEST["omode"]); + $omode = $this->dbh->escape_string($_REQUEST["omode"]); - $feed = db_escape_string($_REQUEST["feed"]); - $method = db_escape_string($_REQUEST["m"]); - $view_mode = db_escape_string($_REQUEST["view_mode"]); - $limit = (int) get_pref($this->link, "DEFAULT_ARTICLE_LIMIT"); + $feed = $this->dbh->escape_string($_REQUEST["feed"]); + $method = $this->dbh->escape_string($_REQUEST["m"]); + $view_mode = $this->dbh->escape_string($_REQUEST["view_mode"]); + $limit = 30; @$cat_view = $_REQUEST["cat"] == "true"; - @$next_unread_feed = db_escape_string($_REQUEST["nuf"]); - @$offset = db_escape_string($_REQUEST["skip"]); - @$vgroup_last_feed = db_escape_string($_REQUEST["vgrlf"]); - $order_by = db_escape_string($_REQUEST["order_by"]); + @$next_unread_feed = $this->dbh->escape_string($_REQUEST["nuf"]); + @$offset = $this->dbh->escape_string($_REQUEST["skip"]); + @$vgroup_last_feed = $this->dbh->escape_string($_REQUEST["vgrlf"]); + $order_by = $this->dbh->escape_string($_REQUEST["order_by"]); if (is_numeric($feed)) $feed = (int) $feed; @@ -768,26 +802,26 @@ class Feeds extends Handler_Protected { * when there's nothing to load - e.g. no stuff in fresh feed */ if ($feed == -5) { - print json_encode($this->generate_dashboard_feed($this->link)); + print json_encode($this->generate_dashboard_feed()); return; } $result = false; - if ($feed < -10) { - $label_feed = -11-$feed; - $result = db_query($this->link, "SELECT id FROM ttrss_labels2 WHERE + if ($feed < LABEL_BASE_INDEX) { + $label_feed = feed_to_label_id($feed); + $result = $this->dbh->query("SELECT id FROM ttrss_labels2 WHERE id = '$label_feed' AND owner_uid = " . $_SESSION['uid']); } else if (!$cat_view && is_numeric($feed) && $feed > 0) { - $result = db_query($this->link, "SELECT id FROM ttrss_feeds WHERE + $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = " . $_SESSION['uid']); } else if ($cat_view && is_numeric($feed) && $feed > 0) { - $result = db_query($this->link, "SELECT id FROM ttrss_feed_categories WHERE + $result = $this->dbh->query("SELECT id FROM ttrss_feed_categories WHERE id = '$feed' AND owner_uid = " . $_SESSION['uid']); } - if ($result && db_num_rows($result) == 0) { - print json_encode($this->generate_error_feed($this->link, __("Feed not found."))); + if ($result && $this->dbh->num_rows($result) == 0) { + print json_encode($this->generate_error_feed(__("Feed not found."))); return; } @@ -795,15 +829,21 @@ class Feeds extends Handler_Protected { * so for performance reasons we don't do that here */ if ($feed >= 0) { - ccache_update($this->link, $feed, $_SESSION["uid"], $cat_view); + ccache_update($feed, $_SESSION["uid"], $cat_view); } - set_pref($this->link, "_DEFAULT_VIEW_MODE", $view_mode); - set_pref($this->link, "_DEFAULT_VIEW_LIMIT", $limit); - set_pref($this->link, "_DEFAULT_VIEW_ORDER_BY", $order_by); + set_pref("_DEFAULT_VIEW_MODE", $view_mode); + set_pref("_DEFAULT_VIEW_ORDER_BY", $order_by); + + /* bump login timestamp if needed */ + if (time() - $_SESSION["last_login_update"] > 3600) { + $this->dbh->query("UPDATE ttrss_users SET last_login = NOW() WHERE id = " . + $_SESSION["uid"]); + $_SESSION["last_login_update"] = time(); + } if (!$cat_view && is_numeric($feed) && $feed > 0) { - db_query($this->link, "UPDATE ttrss_feeds SET last_viewed = NOW() + $this->dbh->query("UPDATE ttrss_feeds SET last_viewed = NOW() WHERE id = '$feed' AND owner_uid = ".$_SESSION["uid"]); } @@ -818,36 +858,16 @@ class Feeds extends Handler_Protected { $override_order = false; - if (get_pref($this->link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) { - $date_sort_field = "updated"; - } else { - $date_sort_field = "date_entered"; - } - switch ($order_by) { - case "date": - if (get_pref($this->link, 'REVERSE_HEADLINES', $owner_uid)) { - $override_order = "$date_sort_field"; - } else { - $override_order = "$date_sort_field DESC"; - } - break; - - case "title": - if (get_pref($this->link, 'REVERSE_HEADLINES', $owner_uid)) { - $override_order = "title DESC, $date_sort_field"; - } else { - $override_order = "title, $date_sort_field DESC"; - } - break; - - case "score": - if (get_pref($this->link, 'REVERSE_HEADLINES', $owner_uid)) { - $override_order = "score, $date_sort_field"; - } else { - $override_order = "score DESC, $date_sort_field DESC"; - } - break; + case "title": + $override_order = "ttrss_entries.title"; + break; + case "date_reverse": + $override_order = "date_entered, updated"; + break; + case "feed_dates": + $override_order = "updated DESC"; + break; } if ($_REQUEST["debug"]) $timing_info = print_checkpoint("04", $timing_info); @@ -873,13 +893,13 @@ class Feeds extends Handler_Protected { if ($_REQUEST["debug"]) $timing_info = print_checkpoint("30", $timing_info); - $reply['runtime-info'] = make_runtime_info($this->link); + $reply['runtime-info'] = make_runtime_info(); print json_encode($reply); } - private function generate_dashboard_feed($link) { + private function generate_dashboard_feed() { $reply = array(); $reply['headlines']['id'] = -5; @@ -888,20 +908,20 @@ class Feeds extends Handler_Protected { $reply['headlines']['toolbar'] = ''; $reply['headlines']['content'] = "<div class='whiteBox'>".__('No feed selected.'); - $reply['headlines']['content'] .= "<p class=\"small\"><span class=\"insensitive\">"; + $reply['headlines']['content'] .= "<p><span class=\"insensitive\">"; - $result = db_query($link, "SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds + $result = $this->dbh->query("SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds WHERE owner_uid = " . $_SESSION['uid']); - $last_updated = db_fetch_result($result, 0, "last_updated"); - $last_updated = make_local_datetime($link, $last_updated, false); + $last_updated = $this->dbh->fetch_result($result, 0, "last_updated"); + $last_updated = make_local_datetime($last_updated, false); $reply['headlines']['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated); - $result = db_query($link, "SELECT COUNT(id) AS num_errors + $result = $this->dbh->query("SELECT COUNT(id) AS num_errors FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]); - $num_errors = db_fetch_result($result, 0, "num_errors"); + $num_errors = $this->dbh->fetch_result($result, 0, "num_errors"); if ($num_errors > 0) { $reply['headlines']['content'] .= "<br/>"; @@ -918,7 +938,7 @@ class Feeds extends Handler_Protected { return $reply; } - private function generate_error_feed($link, $error) { + private function generate_error_feed($error) { $reply = array(); $reply['headlines']['id'] = -6; @@ -935,6 +955,188 @@ class Feeds extends Handler_Protected { return $reply; } + function quickAddFeed() { + print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"rpc\">"; + print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"addfeed\">"; + + print "<div class=\"dlgSec\">".__("Feed or site URL")."</div>"; + print "<div class=\"dlgSecCont\">"; + + print "<div style='float : right'> + <img style='display : none' + id='feed_add_spinner' src='images/indicator_white.gif'></div>"; + + print "<input style=\"font-size : 16px; width : 20em;\" + placeHolder=\"".__("Feed or site URL")."\" + dojoType=\"dijit.form.ValidationTextBox\" required=\"1\" name=\"feed\" id=\"feedDlg_feedUrl\">"; + + print "<hr/>"; + + if (get_pref('ENABLE_FEED_CATS')) { + print __('Place in category:') . " "; + print_feed_cat_select("cat", false, 'dojoType="dijit.form.Select"'); + } + + print "</div>"; + + print '<div id="feedDlg_feedsContainer" style="display : none"> + + <div class="dlgSec">' . __('Available feeds') . '</div> + <div class="dlgSecCont">'. + '<select id="feedDlg_feedContainerSelect" + dojoType="dijit.form.Select" size="3"> + <script type="dojo/method" event="onChange" args="value"> + dijit.byId("feedDlg_feedUrl").attr("value", value); + </script> + </select>'. + '</div></div>'; + + print "<div id='feedDlg_loginContainer' style='display : none'> + + <div class=\"dlgSec\">".__("Authentication")."</div> + <div class=\"dlgSecCont\">". + + " <input dojoType=\"dijit.form.TextBox\" name='login'\" + placeHolder=\"".__("Login")."\" + style=\"width : 10em;\"> ". + " <input + placeHolder=\"".__("Password")."\" + dojoType=\"dijit.form.TextBox\" type='password' + style=\"width : 10em;\" name='pass'\"> + </div></div>"; + + + print "<div style=\"clear : both\"> + <input type=\"checkbox\" name=\"need_auth\" dojoType=\"dijit.form.CheckBox\" id=\"feedDlg_loginCheck\" + onclick='checkboxToggleElement(this, \"feedDlg_loginContainer\")'> + <label for=\"feedDlg_loginCheck\">". + __('This feed requires authentication.')."</div>"; + + print "</form>"; + + print "<div class=\"dlgButtons\"> + <button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedAddDlg').execute()\">".__('Subscribe')."</button>"; + + if (!(defined('_DISABLE_FEED_BROWSER') && _DISABLE_FEED_BROWSER)) { + print "<button dojoType=\"dijit.form.Button\" onclick=\"return feedBrowser()\">".__('More feeds')."</button>"; + } + + print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedAddDlg').hide()\">".__('Cancel')."</button> + </div>"; + + //return; + } + + function feedBrowser() { + if (defined('_DISABLE_FEED_BROWSER') && _DISABLE_FEED_BROWSER) return; + + $browser_search = $this->dbh->escape_string($_REQUEST["search"]); + + print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"rpc\">"; + print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"updateFeedBrowser\">"; + + print "<div dojoType=\"dijit.Toolbar\"> + <div style='float : right'> + <img style='display : none' + id='feed_browser_spinner' src='images/indicator_white.gif'> + <input name=\"search\" dojoType=\"dijit.form.TextBox\" size=\"20\" type=\"search\" + onchange=\"dijit.byId('feedBrowserDlg').update()\" value=\"$browser_search\"> + <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('feedBrowserDlg').update()\">".__('Search')."</button> + </div>"; + + print " <select name=\"mode\" dojoType=\"dijit.form.Select\" onchange=\"dijit.byId('feedBrowserDlg').update()\"> + <option value='1'>" . __('Popular feeds') . "</option> + <option value='2'>" . __('Feed archive') . "</option> + </select> "; + + print __("limit:"); + + print " <select dojoType=\"dijit.form.Select\" name=\"limit\" onchange=\"dijit.byId('feedBrowserDlg').update()\">"; + + foreach (array(25, 50, 100, 200) as $l) { + $issel = ($l == $limit) ? "selected=\"1\"" : ""; + print "<option $issel value=\"$l\">$l</option>"; + } + + print "</select> "; + + print "</div>"; + + $owner_uid = $_SESSION["uid"]; + + require_once "feedbrowser.php"; + + print "<ul class='browseFeedList' id='browseFeedList'>"; + print make_feed_browser($search, 25); + print "</ul>"; + + print "<div align='center'> + <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('feedBrowserDlg').execute()\">".__('Subscribe')."</button> + <button dojoType=\"dijit.form.Button\" style='display : none' id='feed_archive_remove' onclick=\"dijit.byId('feedBrowserDlg').removeFromArchive()\">".__('Remove')."</button> + <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('feedBrowserDlg').hide()\" >".__('Cancel')."</button></div>"; + + } + + function search() { + $this->params = explode(":", $this->dbh->escape_string($_REQUEST["param"]), 2); + + $active_feed_id = sprintf("%d", $this->params[0]); + $is_cat = $this->params[1] != "false"; + + print "<div class=\"dlgSec\">".__('Look for')."</div>"; + + print "<div class=\"dlgSecCont\">"; + + print "<input dojoType=\"dijit.form.ValidationTextBox\" + style=\"font-size : 16px; width : 20em;\" + required=\"1\" name=\"query\" type=\"search\" value=''>"; + + print "<hr/>".__('Limit search to:')." "; + + print "<select name=\"search_mode\" dojoType=\"dijit.form.Select\"> + <option value=\"all_feeds\">".__('All feeds')."</option>"; + + $feed_title = getFeedTitle($active_feed_id); + + if (!$is_cat) { + $feed_cat_title = getFeedCatTitle($active_feed_id); + } else { + $feed_cat_title = getCategoryTitle($active_feed_id); + } + + if ($active_feed_id && !$is_cat) { + print "<option selected=\"1\" value=\"this_feed\">$feed_title</option>"; + } else { + print "<option disabled=\"1\" value=\"false\">".__('This feed')."</option>"; + } + + if ($is_cat) { + $cat_preselected = "selected=\"1\""; + } + + if (get_pref('ENABLE_FEED_CATS') && ($active_feed_id > 0 || $is_cat)) { + print "<option $cat_preselected value=\"this_cat\">$feed_cat_title</option>"; + } else { + //print "<option disabled>".__('This category')."</option>"; + } + + print "</select>"; + + print "</div>"; + + print "<div class=\"dlgButtons\">"; + + if (!SPHINX_ENABLED) { + print "<div style=\"float : left\"> + <a class=\"visibleLink\" target=\"_blank\" href=\"http://tt-rss.org/wiki/SearchSyntax\">Search syntax</a> + </div>"; + } + + print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('searchDlg').execute()\">".__('Search')."</button> + <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('searchDlg').hide()\">".__('Cancel')."</button> + </div>"; + } + } ?> diff --git a/classes/handler.php b/classes/handler.php index 68b16eac1..b20932045 100644 --- a/classes/handler.php +++ b/classes/handler.php @@ -1,10 +1,10 @@ <?php class Handler implements IHandler { - protected $link; + protected $dbh; protected $args; - function __construct($link, $args) { - $this->link = $link; + function __construct($args) { + $this->dbh = Db::get(); $this->args = $args; } diff --git a/classes/handler/public.php b/classes/handler/public.php index 4a9b0c48e..7fa744107 100644 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -2,7 +2,7 @@ class Handler_Public extends Handler { private function generate_syndicated_feed($owner_uid, $feed, $is_cat, - $limit, $offset, $search, $search_mode, $match_on, + $limit, $offset, $search, $search_mode, $view_mode = false, $format = 'atom') { require_once "lib/MiniTemplator.class.php"; @@ -12,22 +12,41 @@ class Handler_Public extends Handler { "padding : 5px; border-style : dashed; border-color : #e7d796;". "margin-bottom : 1em; color : #9a8c59;"; - if (!$limit) $limit = 100; + if (!$limit) $limit = 60; - if (get_pref($this->link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) { - $date_sort_field = "updated"; - } else { - $date_sort_field = "date_entered"; - } + $date_sort_field = "date_entered DESC, updated DESC"; if ($feed == -2) - $date_sort_field = "last_read"; + $date_sort_field = "last_published DESC"; + else if ($feed == -1) + $date_sort_field = "last_marked DESC"; + + $qfh_ret = queryFeedHeadlines($feed, + 1, $view_mode, $is_cat, $search, $search_mode, + $date_sort_field, $offset, $owner_uid, + false, 0, false, true); + + $result = $qfh_ret[0]; + + if ($this->dbh->num_rows($result) != 0) { + $ts = strtotime($this->dbh->fetch_result($result, 0, "date_entered")); - $qfh_ret = queryFeedHeadlines($this->link, $feed, + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && + strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $ts) { + header('HTTP/1.0 304 Not Modified'); + return; + } + + $last_modified = gmdate("D, d M Y H:i:s", $ts) . " GMT"; + header("Last-Modified: $last_modified", true); + } + + $qfh_ret = queryFeedHeadlines($feed, $limit, $view_mode, $is_cat, $search, $search_mode, - $match_on, "$date_sort_field DESC", $offset, $owner_uid, + $date_sort_field, $offset, $owner_uid, false, 0, false, true); + $result = $qfh_ret[0]; $feed_title = htmlspecialchars($qfh_ret[1]); $feed_site_url = $qfh_ret[2]; @@ -35,7 +54,7 @@ class Handler_Public extends Handler { $feed_self_url = get_self_url_prefix() . "/public.php?op=rss&id=-2&key=" . - get_feed_access_key($this->link, -2, false, $owner_uid); + get_feed_access_key(-2, false, $owner_uid); if (!$feed_site_url) $feed_site_url = get_self_url_prefix(); @@ -55,19 +74,21 @@ class Handler_Public extends Handler { $tpl->setVariable('SELF_URL', htmlspecialchars(get_self_url_prefix()), true); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { + $tpl->setVariable('ARTICLE_ID', htmlspecialchars($line['link']), true); $tpl->setVariable('ARTICLE_LINK', htmlspecialchars($line['link']), true); $tpl->setVariable('ARTICLE_TITLE', htmlspecialchars($line['title']), true); $tpl->setVariable('ARTICLE_EXCERPT', truncate_string(strip_tags($line["content_preview"]), 100, '...'), true); - $content = sanitize($this->link, $line["content_preview"], false, $owner_uid); + $content = sanitize($line["content_preview"], false, $owner_uid); if ($line['note']) { $content = "<div style=\"$note_style\">Article note: " . $line['note'] . "</div>" . $content; -} + $tpl->setVariable('ARTICLE_NOTE', htmlspecialchars($line['note']), true); + } $tpl->setVariable('ARTICLE_CONTENT', $content, true); @@ -78,14 +99,14 @@ class Handler_Public extends Handler { $tpl->setVariable('ARTICLE_AUTHOR', htmlspecialchars($line['author']), true); - $tags = get_article_tags($this->link, $line["id"], $owner_uid); + $tags = get_article_tags($line["id"], $owner_uid); foreach ($tags as $tag) { $tpl->setVariable('ARTICLE_CATEGORY', htmlspecialchars($tag), true); $tpl->addBlock('category'); } - $enclosures = get_article_enclosures($this->link, $line["id"]); + $enclosures = get_article_enclosures($line["id"]); foreach ($enclosures as $e) { $type = htmlspecialchars($e['content_type']); @@ -130,20 +151,20 @@ class Handler_Public extends Handler { $feed['articles'] = array(); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $article = array(); $article['id'] = $line['link']; $article['link'] = $line['link']; $article['title'] = $line['title']; $article['excerpt'] = truncate_string(strip_tags($line["content_preview"]), 100, '...'); - $article['content'] = sanitize($this->link, $line["content_preview"], false, $owner_uid); + $article['content'] = sanitize($line["content_preview"], false, $owner_uid); $article['updated'] = date('c', strtotime($line["updated"])); if ($line['note']) $article['note'] = $line['note']; if ($article['author']) $article['author'] = $line['author']; - $tags = get_article_tags($this->link, $line["id"], $owner_uid); + $tags = get_article_tags($line["id"], $owner_uid); if (count($tags) > 0) { $article['tags'] = array(); @@ -153,7 +174,7 @@ class Handler_Public extends Handler { } } - $enclosures = get_article_enclosures($this->link, $line["id"]); + $enclosures = get_article_enclosures($line["id"]); if (count($enclosures) > 0) { $article['enclosures'] = array(); @@ -180,19 +201,19 @@ class Handler_Public extends Handler { } function getUnread() { - $login = db_escape_string($_REQUEST["login"]); + $login = $this->dbh->escape_string($_REQUEST["login"]); $fresh = $_REQUEST["fresh"] == "1"; - $result = db_query($this->link, "SELECT id FROM ttrss_users WHERE login = '$login'"); + $result = $this->dbh->query("SELECT id FROM ttrss_users WHERE login = '$login'"); - if (db_num_rows($result) == 1) { - $uid = db_fetch_result($result, 0, "id"); + if ($this->dbh->num_rows($result) == 1) { + $uid = $this->dbh->fetch_result($result, 0, "id"); - print getGlobalUnread($this->link, $uid); + print getGlobalUnread($uid); if ($fresh) { print ";"; - print getFeedArticles($this->link, -3, false, true, $uid); + print getFeedArticles(-3, false, true, $uid); } } else { @@ -202,16 +223,16 @@ class Handler_Public extends Handler { } function getProfiles() { - $login = db_escape_string($_REQUEST["login"]); + $login = $this->dbh->escape_string($_REQUEST["login"]); - $result = db_query($this->link, "SELECT * FROM ttrss_settings_profiles,ttrss_users + $result = $this->dbh->query("SELECT * FROM ttrss_settings_profiles,ttrss_users WHERE ttrss_users.id = ttrss_settings_profiles.owner_uid AND login = '$login' ORDER BY title"); print "<select dojoType='dijit.form.Select' style='width : 220px; margin : 0px' name='profile'>"; print "<option value='0'>" . __("Default profile") . "</option>"; - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $id = $line["id"]; $title = $line["title"]; @@ -222,9 +243,9 @@ class Handler_Public extends Handler { } function pubsub() { - $mode = db_escape_string($_REQUEST['hub_mode']); - $feed_id = (int) db_escape_string($_REQUEST['id']); - $feed_url = db_escape_string($_REQUEST['hub_topic']); + $mode = $this->dbh->escape_string($_REQUEST['hub_mode']); + $feed_id = (int) $this->dbh->escape_string($_REQUEST['id']); + $feed_url = $this->dbh->escape_string($_REQUEST['hub_topic']); if (!PUBSUBHUBBUB_ENABLED) { header('HTTP/1.0 404 Not Found'); @@ -234,17 +255,17 @@ class Handler_Public extends Handler { // TODO: implement hub_verifytoken checking - $result = db_query($this->link, "SELECT feed_url FROM ttrss_feeds + $result = $this->dbh->query("SELECT feed_url FROM ttrss_feeds WHERE id = '$feed_id'"); - if (db_num_rows($result) != 0) { + if ($this->dbh->num_rows($result) != 0) { - $check_feed_url = db_fetch_result($result, 0, "feed_url"); + $check_feed_url = $this->dbh->fetch_result($result, 0, "feed_url"); if ($check_feed_url && ($check_feed_url == $feed_url || !$feed_url)) { if ($mode == "subscribe") { - db_query($this->link, "UPDATE ttrss_feeds SET pubsub_state = 2 + $this->dbh->query("UPDATE ttrss_feeds SET pubsub_state = 2 WHERE id = '$feed_id'"); print $_REQUEST['hub_challenge']; @@ -252,7 +273,7 @@ class Handler_Public extends Handler { } else if ($mode == "unsubscribe") { - db_query($this->link, "UPDATE ttrss_feeds SET pubsub_state = 0 + $this->dbh->query("UPDATE ttrss_feeds SET pubsub_state = 0 WHERE id = '$feed_id'"); print $_REQUEST['hub_challenge']; @@ -261,9 +282,9 @@ class Handler_Public extends Handler { } else if (!$mode) { // Received update ping, schedule feed update. - //update_rss_feed($this->link, $feed_id, true, true); + //update_rss_feed($feed_id, true, true); - db_query($this->link, "UPDATE ttrss_feeds SET + $this->dbh->query("UPDATE ttrss_feeds SET last_update_started = '1970-01-01', last_updated = '1970-01-01' WHERE id = '$feed_id'"); @@ -285,18 +306,18 @@ class Handler_Public extends Handler { } function share() { - $uuid = db_escape_string($_REQUEST["key"]); + $uuid = $this->dbh->escape_string($_REQUEST["key"]); - $result = db_query($this->link, "SELECT ref_id, owner_uid FROM ttrss_user_entries WHERE + $result = $this->dbh->query("SELECT ref_id, owner_uid FROM ttrss_user_entries WHERE uuid = '$uuid'"); - if (db_num_rows($result) != 0) { + if ($this->dbh->num_rows($result) != 0) { header("Content-Type: text/html"); - $id = db_fetch_result($result, 0, "ref_id"); - $owner_uid = db_fetch_result($result, 0, "owner_uid"); + $id = $this->dbh->fetch_result($result, 0, "ref_id"); + $owner_uid = $this->dbh->fetch_result($result, 0, "owner_uid"); - $article = format_article($this->link, $id, false, true, $owner_uid); + $article = format_article($id, false, true, $owner_uid); print_r($article['content']); @@ -307,38 +328,37 @@ class Handler_Public extends Handler { } function rss() { - $feed = db_escape_string($_REQUEST["id"]); - $key = db_escape_string($_REQUEST["key"]); + $feed = $this->dbh->escape_string($_REQUEST["id"]); + $key = $this->dbh->escape_string($_REQUEST["key"]); $is_cat = $_REQUEST["is_cat"] != false; - $limit = (int)db_escape_string($_REQUEST["limit"]); - $offset = (int)db_escape_string($_REQUEST["offset"]); + $limit = (int)$this->dbh->escape_string($_REQUEST["limit"]); + $offset = (int)$this->dbh->escape_string($_REQUEST["offset"]); - $search = db_escape_string($_REQUEST["q"]); - $match_on = db_escape_string($_REQUEST["m"]); - $search_mode = db_escape_string($_REQUEST["smode"]); - $view_mode = db_escape_string($_REQUEST["view-mode"]); + $search = $this->dbh->escape_string($_REQUEST["q"]); + $search_mode = $this->dbh->escape_string($_REQUEST["smode"]); + $view_mode = $this->dbh->escape_string($_REQUEST["view-mode"]); - $format = db_escape_string($_REQUEST['format']); + $format = $this->dbh->escape_string($_REQUEST['format']); if (!$format) $format = 'atom'; if (SINGLE_USER_MODE) { - authenticate_user($this->link, "admin", null); + authenticate_user("admin", null); } $owner_id = false; if ($key) { - $result = db_query($this->link, "SELECT owner_uid FROM + $result = $this->dbh->query("SELECT owner_uid FROM ttrss_access_keys WHERE access_key = '$key' AND feed_id = '$feed'"); - if (db_num_rows($result) == 1) - $owner_id = db_fetch_result($result, 0, "owner_uid"); + if ($this->dbh->num_rows($result) == 1) + $owner_id = $this->dbh->fetch_result($result, 0, "owner_uid"); } if ($owner_id) { $this->generate_syndicated_feed($owner_id, $feed, $is_cat, $limit, - $offset, $search, $search_mode, $match_on, $view_mode, $format); + $offset, $search, $search_mode, $view_mode, $format); } else { header('HTTP/1.1 403 Forbidden'); } @@ -347,24 +367,26 @@ class Handler_Public extends Handler { function globalUpdateFeeds() { include "rssfuncs.php"; // Update all feeds needing a update. - update_daemon_common($this->link, 0, true, false); + update_daemon_common(0, true, false); + housekeeping_common(false); + + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_UPDATE_TASK, "hook_update_task", $op); + } function sharepopup() { if (SINGLE_USER_MODE) { - login_sequence($this->link); + login_sequence(); } header('Content-Type: text/html; charset=utf-8'); - print "<html> - <head> - <title>Tiny Tiny RSS</title> - <link rel=\"stylesheet\" type=\"text/css\" href=\"utility.css\"> - <script type=\"text/javascript\" src=\"lib/prototype.js\"></script> - <script type=\"text/javascript\" src=\"lib/scriptaculous/scriptaculous.js?load=effects,dragdrop,controls\"></script> - <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/> - </head> - <body id='sharepopup'>"; + print "<html><head><title>Tiny Tiny RSS</title>"; + + stylesheet_tag("utility.css"); + javascript_tag("lib/prototype.js"); + javascript_tag("lib/scriptaculous/scriptaculous.js?load=effects,dragdrop,controls"); + print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/> + </head><body id='sharepopup'>"; $action = $_REQUEST["action"]; @@ -372,12 +394,12 @@ class Handler_Public extends Handler { if ($action == 'share') { - $title = db_escape_string(strip_tags($_REQUEST["title"])); - $url = db_escape_string(strip_tags($_REQUEST["url"])); - $content = db_escape_string(strip_tags($_REQUEST["content"])); - $labels = db_escape_string(strip_tags($_REQUEST["labels"])); + $title = $this->dbh->escape_string(strip_tags($_REQUEST["title"])); + $url = $this->dbh->escape_string(strip_tags($_REQUEST["url"])); + $content = $this->dbh->escape_string(strip_tags($_REQUEST["content"])); + $labels = $this->dbh->escape_string(strip_tags($_REQUEST["labels"])); - Article::create_published_article($this->link, $title, $url, $content, $labels, + Article::create_published_article($title, $url, $content, $labels, $_SESSION["uid"]); print "<script type='text/javascript'>"; @@ -456,14 +478,6 @@ class Handler_Public extends Handler { <tr><td align="right"><?php echo __("Password:") ?></td> <td align="right"><input type="password" name="password" value="<?php echo $_SESSION["fake_password"] ?>"></td></tr> - <tr><td align="right"><?php echo __("Language:") ?></td> - <td align="right"> - <?php - print_select_hash("language", $_COOKIE["ttrss_lang"], get_translations(), - "style='width : 100%''"); - - ?> - </td></tr> <tr><td colspan='2'> <button type="submit"> <?php echo __('Log in') ?></button> @@ -479,32 +493,39 @@ class Handler_Public extends Handler { } function login() { - - $_SESSION["prefs_cache"] = array(); - if (!SINGLE_USER_MODE) { - $login = db_escape_string($_POST["login"]); + $login = $this->dbh->escape_string($_POST["login"]); $password = $_POST["password"]; $remember_me = $_POST["remember_me"]; - if (authenticate_user($this->link, $login, $password)) { + if ($remember_me) { + session_set_cookie_params(SESSION_COOKIE_LIFETIME); + } else { + session_set_cookie_params(0); + } + + @session_start(); + + if (authenticate_user($login, $password)) { $_POST["password"] = ""; - $_SESSION["language"] = $_POST["language"]; - $_SESSION["ref_schema_version"] = get_schema_version($this->link, true); + if (get_schema_version() >= 120) { + $_SESSION["language"] = get_pref("USER_LANGUAGE", $_SESSION["uid"]); + } + + $_SESSION["ref_schema_version"] = get_schema_version(true); $_SESSION["bw_limit"] = !!$_POST["bw_limit"]; if ($_POST["profile"]) { - $profile = db_escape_string($_POST["profile"]); + $profile = $this->dbh->escape_string($_POST["profile"]); - $result = db_query($this->link, "SELECT id FROM ttrss_settings_profiles + $result = $this->dbh->query("SELECT id FROM ttrss_settings_profiles WHERE id = '$profile' AND owner_uid = " . $_SESSION["uid"]); - if (db_num_rows($result) != 0) { + if ($this->dbh->num_rows($result) != 0) { $_SESSION["profile"] = $profile; - $_SESSION["prefs_cache"] = array(); } } } else { @@ -521,12 +542,12 @@ class Handler_Public extends Handler { function subscribe() { if (SINGLE_USER_MODE) { - login_sequence($this->link); + login_sequence(); } if ($_SESSION["uid"]) { - $feed_url = db_escape_string(trim($_REQUEST["feed_url"])); + $feed_url = $this->dbh->escape_string(trim($_REQUEST["feed_url"])); header('Content-Type: text/html; charset=utf-8'); print "<html> @@ -536,11 +557,11 @@ class Handler_Public extends Handler { <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/> </head> <body> - <img class=\"floatingLogo\" src=\"images/logo_wide.png\" + <img class=\"floatingLogo\" src=\"images/logo_small.png\" alt=\"Tiny Tiny RSS\"/> - <h1>".__("Subscribe to feed...")."</h1>"; + <h1>".__("Subscribe to feed...")."</h1><div class='content'>"; - $rc = subscribe_to_feed($this->link, $feed_url); + $rc = subscribe_to_feed($feed_url); switch ($rc['code']) { case 0: @@ -588,10 +609,10 @@ class Handler_Public extends Handler { $tt_uri = get_self_url_prefix(); if ($rc['code'] <= 2){ - $result = db_query($this->link, "SELECT id FROM ttrss_feeds WHERE + $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE feed_url = '$feed_url' AND owner_uid = " . $_SESSION["uid"]); - $feed_id = db_fetch_result($result, 0, "id"); + $feed_id = $this->dbh->fetch_result($result, 0, "id"); } else { $feed_id = 0; } @@ -611,24 +632,25 @@ class Handler_Public extends Handler { <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\"> </form></p>"; - print "</body></html>"; + print "</div></body></html>"; } else { - render_login_form($this->link); + render_login_form(); } } function subscribe2() { - $feed_url = db_escape_string(trim($_REQUEST["feed_url"])); - $cat_id = db_escape_string($_REQUEST["cat_id"]); - $from = db_escape_string($_REQUEST["from"]); + $feed_url = $this->dbh->escape_string(trim($_REQUEST["feed_url"])); + $cat_id = $this->dbh->escape_string($_REQUEST["cat_id"]); + $from = $this->dbh->escape_string($_REQUEST["from"]); + $feed_urls = array(); /* only read authentication information from POST */ - $auth_login = db_escape_string(trim($_POST["auth_login"])); - $auth_pass = db_escape_string(trim($_POST["auth_pass"])); + $auth_login = $this->dbh->escape_string(trim($_POST["auth_login"])); + $auth_pass = $this->dbh->escape_string(trim($_POST["auth_pass"])); - $rc = subscribe_to_feed($this->link, $feed_url, $cat_id, $auth_login, $auth_pass); + $rc = subscribe_to_feed($feed_url, $cat_id, $auth_login, $auth_pass); switch ($rc) { case 1: @@ -645,8 +667,10 @@ class Handler_Public extends Handler { break; case 4: print_notice(__("Multiple feed URLs found.")); - - $feed_urls = get_feeds_from_html($feed_url); + $contents = @fetch_file_contents($url, false, $auth_login, $auth_pass); + if (is_html($contents)) { + $feed_urls = get_feeds_from_html($url, $contents); + } break; case 5: print_error(T_sprintf("Could not subscribe to <b>%s</b>.<br>Can't download the Feed URL.", $feed_url)); @@ -675,10 +699,10 @@ class Handler_Public extends Handler { $tt_uri = get_self_url_prefix(); if ($rc <= 2){ - $result = db_query($this->link, "SELECT id FROM ttrss_feeds WHERE + $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE feed_url = '$feed_url' AND owner_uid = " . $_SESSION["uid"]); - $feed_id = db_fetch_result($result, 0, "id"); + $feed_id = $this->dbh->fetch_result($result, 0, "id"); } else { $feed_id = 0; } @@ -707,5 +731,210 @@ class Handler_Public extends Handler { print json_encode(array("error" => array("code" => 7))); } + function forgotpass() { + header('Content-Type: text/html; charset=utf-8'); + print "<html><head><title>Tiny Tiny RSS</title>"; + + stylesheet_tag("utility.css"); + javascript_tag("lib/prototype.js"); + + print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/> + </head><body id='forgotpass'>"; + + print '<div class="floatingLogo"><img src="images/logo_small.png"></div>'; + print "<h1>".__("Password recovery")."</h1>"; + print "<div class='content'>"; + + @$method = $_POST['method']; + + if (!$method) { + print_notice(__("You will need to provide valid account name and email. New password will be sent on your email address.")); + + print "<form method='POST' action='public.php'>"; + print "<input type='hidden' name='method' value='do'>"; + print "<input type='hidden' name='op' value='forgotpass'>"; + + print "<fieldset>"; + print "<label>".__("Login:")."</label>"; + print "<input type='text' name='login' value='' required>"; + print "</fieldset>"; + + print "<fieldset>"; + print "<label>".__("Email:")."</label>"; + print "<input type='email' name='email' value='' required>"; + print "</fieldset>"; + + print "<fieldset>"; + print "<label>".__("How much is two plus two:")."</label>"; + print "<input type='text' name='test' value='' required>"; + print "</fieldset>"; + + print "<p/>"; + print "<button type='submit'>".__("Reset password")."</button>"; + + print "</form>"; + } else if ($method == 'do') { + + $login = $this->dbh->escape_string($_POST["login"]); + $email = $this->dbh->escape_string($_POST["email"]); + $test = $this->dbh->escape_string($_POST["test"]); + + if (($test != 4 && $test != 'four') || !$email || !$login) { + print_error(__('Some of the required form parameters are missing or incorrect.')); + + print "<form method=\"GET\" action=\"public.php\"> + <input type=\"hidden\" name=\"op\" value=\"forgotpass\"> + <input type=\"submit\" value=\"".__("Go back")."\"> + </form>"; + + } else { + + $result = $this->dbh->query("SELECT id FROM ttrss_users + WHERE login = '$login' AND email = '$email'"); + + if ($this->dbh->num_rows($result) != 0) { + $id = $this->dbh->fetch_result($result, 0, "id"); + + Pref_Users::resetUserPassword($id, false); + + print "<p>"; + + print "<p>"."Completed."."</p>"; + + print "<form method=\"GET\" action=\"index.php\"> + <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\"> + </form>"; + + } else { + print_error(__("Sorry, login and email combination not found.")); + + print "<form method=\"GET\" action=\"public.php\"> + <input type=\"hidden\" name=\"op\" value=\"forgotpass\"> + <input type=\"submit\" value=\"".__("Go back")."\"> + </form>"; + + } + } + + } + + print "</div>"; + print "</body>"; + print "</html>"; + + } + + function dbupdate() { + if (!SINGLE_USER_MODE && $_SESSION["access_level"] < 10) { + $_SESSION["login_error_msg"] = __("Your access level is insufficient to run this script."); + render_login_form(); + exit; + } + + ?><html> + <head> + <title>Database Updater</title> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> + <link rel="stylesheet" type="text/css" href="utility.css"/> + </head> + <style type="text/css"> + span.ok { color : #009000; font-weight : bold; } + span.err { color : #ff0000; font-weight : bold; } + </style> + <body> + <script type='text/javascript'> + function confirmOP() { + return confirm("Update the database?"); + } + </script> + + <div class="floatingLogo"><img src="images/logo_small.png"></div> + + <h1><?php echo __("Database Updater") ?></h1> + + <div class="content"> + + <?php + @$op = $_REQUEST["subop"]; + $updater = new DbUpdater(Db::get(), DB_TYPE, SCHEMA_VERSION); + + if ($op == "performupdate") { + if ($updater->isUpdateRequired()) { + + print "<h2>Performing updates</h2>"; + + print "<h3>Updating to schema version " . SCHEMA_VERSION . "</h3>"; + + print "<ul>"; + + for ($i = $updater->getSchemaVersion() + 1; $i <= SCHEMA_VERSION; $i++) { + print "<li>Performing update up to version $i..."; + + $result = $updater->performUpdateTo($i); + + if (!$result) { + print "<span class='err'>FAILED!</span></li></ul>"; + + print_warning("One of the updates failed. Either retry the process or perform updates manually."); + print "<p><form method=\"GET\" action=\"index.php\"> + <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\"> + </form>"; + + break; + } else { + print "<span class='ok'>OK!</span></li>"; + } + } + + print "</ul>"; + + print_notice("Your Tiny Tiny RSS database is now updated to the latest version."); + + print "<p><form method=\"GET\" action=\"index.php\"> + <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\"> + </form>"; + + } else { + print "<h2>Your database is up to date.</h2>"; + + print "<p><form method=\"GET\" action=\"index.php\"> + <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\"> + </form>"; + } + } else { + if ($updater->isUpdateRequired()) { + + print "<h2>Database update required</h2>"; + + print "<h3>"; + printf("Your Tiny Tiny RSS database needs update to the latest version: %d to %d.", + $updater->getSchemaVersion(), SCHEMA_VERSION); + print "</h3>"; + + print_warning("Please backup your database before proceeding."); + + print "<form method='POST'> + <input type='hidden' name='subop' value='performupdate'> + <input type='submit' onclick='return confirmOP()' value='".__("Perform updates")."'> + </form>"; + + } else { + + print_notice("Tiny Tiny RSS database is up to date."); + + print "<p><form method=\"GET\" action=\"index.php\"> + <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\"> + </form>"; + + } + } + ?> + + </div> + </body> + </html> + <?php + } + } ?> diff --git a/classes/idb.php b/classes/idb.php new file mode 100644 index 000000000..16f760bf6 --- /dev/null +++ b/classes/idb.php @@ -0,0 +1,13 @@ +<?php +interface IDb { + function connect($host, $user, $pass, $db, $port); + function escape_string($s, $strip_tags = true); + function query($query, $die_on_error = true); + function fetch_assoc($result); + function num_rows($result); + function fetch_result($result, $row, $param); + function close(); + function affected_rows($result); + function last_error(); +} +?> diff --git a/classes/logger.php b/classes/logger.php new file mode 100644 index 000000000..4a9c1df82 --- /dev/null +++ b/classes/logger.php @@ -0,0 +1,65 @@ +<?php +class Logger { + private static $instance; + private $adapter; + + public static $errornames = array( + 1 => 'E_ERROR', + 2 => 'E_WARNING', + 4 => 'E_PARSE', + 8 => 'E_NOTICE', + 16 => 'E_CORE_ERROR', + 32 => 'E_CORE_WARNING', + 64 => 'E_COMPILE_ERROR', + 128 => 'E_COMPILE_WARNING', + 256 => 'E_USER_ERROR', + 512 => 'E_USER_WARNING', + 1024 => 'E_USER_NOTICE', + 2048 => 'E_STRICT', + 4096 => 'E_RECOVERABLE_ERROR', + 8192 => 'E_DEPRECATED', + 16384 => 'E_USER_DEPRECATED', + 32767 => 'E_ALL'); + + function log_error($errno, $errstr, $file, $line, $context) { + if ($errno == E_NOTICE) return false; + + if ($this->adapter) + return $this->adapter->log_error($errno, $errstr, $file, $line, $context); + else + return false; + } + + function log($string) { + if ($this->adapter) + return $this->adapter->log($string); + else + return false; + } + + private function __clone() { + // + } + + function __construct() { + switch (LOG_DESTINATION) { + case "sql": + $this->adapter = new Logger_SQL(); + break; + case "syslog": + $this->adapter = new Logger_Syslog(); + break; + default: + $this->adapter = false; + } + } + + public static function get() { + if (self::$instance == null) + self::$instance = new self(); + + return self::$instance; + } + +} +?> diff --git a/classes/logger/sql.php b/classes/logger/sql.php new file mode 100644 index 000000000..c0f8b4598 --- /dev/null +++ b/classes/logger/sql.php @@ -0,0 +1,28 @@ +<?php +class Logger_SQL { + + function log_error($errno, $errstr, $file, $line, $context) { + if (Db::get() && get_schema_version() > 117) { + + $errno = Db::get()->escape_string($errno); + $errstr = Db::get()->escape_string($errstr); + $file = Db::get()->escape_string($file); + $line = Db::get()->escape_string($line); + $context = ''; // backtrace is a lot of data which is not really critical to store + //$context = $this->dbh->escape_string(serialize($context)); + + $owner_uid = $_SESSION["uid"] ? $_SESSION["uid"] : "NULL"; + + $result = Db::get()->query( + "INSERT INTO ttrss_error_log + (errno, errstr, filename, lineno, context, owner_uid, created_at) VALUES + ($errno, '$errstr', '$file', '$line', '$context', $owner_uid, NOW())"); + + return Db::get()->affected_rows($result) != 0; + } + + return false; + } + +} +?> diff --git a/classes/logger/syslog.php b/classes/logger/syslog.php new file mode 100644 index 000000000..b8b5260a0 --- /dev/null +++ b/classes/logger/syslog.php @@ -0,0 +1,31 @@ +<?php +class Logger_Syslog { + + function log_error($errno, $errstr, $file, $line, $context) { + + switch ($errno) { + case E_ERROR: + case E_PARSE: + case E_CORE_ERROR: + case E_COMPILE_ERROR: + case E_USER_ERROR: + $priority = LOG_ERR; + break; + case E_WARNING: + case E_CORE_WARNING: + case E_COMPILE_WARNING: + case E_USER_WARNING: + $priority = LOG_WARNING; + break; + default: + $priority = LOG_INFO; + } + + $errname = Logger::$errornames[$errno] . " ($errno)"; + + syslog($priority, "[tt-rss] $errname ($file:$line) $errstr"); + + } + +} +?> diff --git a/classes/opml.php b/classes/opml.php index b91e395d1..3f4030dea 100644 --- a/classes/opml.php +++ b/classes/opml.php @@ -29,10 +29,10 @@ class Opml extends Handler_Protected { <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/> </head> <body> - <div class=\"floatingLogo\"><img src=\"images/logo_wide.png\"></div> - <h1>".__('OPML Utility')."</h1>"; + <div class=\"floatingLogo\"><img src=\"images/logo_small.png\"></div> + <h1>".__('OPML Utility')."</h1><div class='content'>"; - add_feed_category($this->link, "Imported feeds"); + add_feed_category("Imported feeds"); $this->opml_notice(__("Importing OPML...")); $this->opml_import($owner_uid); @@ -41,7 +41,7 @@ class Opml extends Handler_Protected { <input type=\"submit\" value=\"".__("Return to preferences")."\"> </form>"; - print "</body></html>"; + print "</div></body></html>"; } @@ -66,27 +66,27 @@ class Opml extends Handler_Protected { $out = ""; if ($cat_id) { - $result = db_query($this->link, "SELECT title FROM ttrss_feed_categories WHERE id = '$cat_id' + $result = $this->dbh->query("SELECT title FROM ttrss_feed_categories WHERE id = '$cat_id' AND owner_uid = '$owner_uid'"); - $cat_title = htmlspecialchars(db_fetch_result($result, 0, "title")); + $cat_title = htmlspecialchars($this->dbh->fetch_result($result, 0, "title")); } if ($cat_title) $out .= "<outline text=\"$cat_title\">\n"; - $result = db_query($this->link, "SELECT id,title + $result = $this->dbh->query("SELECT id,title FROM ttrss_feed_categories WHERE $cat_qpart AND owner_uid = '$owner_uid' ORDER BY order_id, title"); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $title = htmlspecialchars($line["title"]); $out .= $this->opml_export_category($owner_uid, $line["id"], $hide_private_feeds); } - $feeds_result = db_query($this->link, "select title, feed_url, site_url + $feeds_result = $this->dbh->query("select title, feed_url, site_url from ttrss_feeds where $feed_cat_qpart AND owner_uid = '$owner_uid' AND $hide_qpart order by order_id, title"); - while ($fline = db_fetch_assoc($feeds_result)) { + while ($fline = $this->dbh->fetch_assoc($feeds_result)) { $title = htmlspecialchars($fline["title"]); $url = htmlspecialchars($fline["feed_url"]); $site_url = htmlspecialchars($fline["site_url"]); @@ -131,10 +131,10 @@ class Opml extends Handler_Protected { if ($include_settings) { $out .= "<outline text=\"tt-rss-prefs\" schema-version=\"".SCHEMA_VERSION."\">"; - $result = db_query($this->link, "SELECT pref_name, value FROM ttrss_user_prefs WHERE + $result = $this->dbh->query("SELECT pref_name, value FROM ttrss_user_prefs WHERE profile IS NULL AND owner_uid = " . $_SESSION["uid"] . " ORDER BY pref_name"); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $name = $line["pref_name"]; $value = htmlspecialchars($line["value"]); @@ -145,10 +145,10 @@ class Opml extends Handler_Protected { $out .= "<outline text=\"tt-rss-labels\" schema-version=\"".SCHEMA_VERSION."\">"; - $result = db_query($this->link, "SELECT * FROM ttrss_labels2 WHERE + $result = $this->dbh->query("SELECT * FROM ttrss_labels2 WHERE owner_uid = " . $_SESSION['uid']); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $name = htmlspecialchars($line['caption']); $fg_color = htmlspecialchars($line['fg_color']); $bg_color = htmlspecialchars($line['bg_color']); @@ -161,10 +161,10 @@ class Opml extends Handler_Protected { $out .= "<outline text=\"tt-rss-filters\" schema-version=\"".SCHEMA_VERSION."\">"; - $result = db_query($this->link, "SELECT * FROM ttrss_filters2 + $result = $this->dbh->query("SELECT * FROM ttrss_filters2 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY id"); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { foreach (array('enabled', 'match_any_rule') as $b) { $line[$b] = sql_bool_to_bool($line[$b]); } @@ -172,17 +172,17 @@ class Opml extends Handler_Protected { $line["rules"] = array(); $line["actions"] = array(); - $tmp_result = db_query($this->link, "SELECT * FROM ttrss_filters2_rules + $tmp_result = $this->dbh->query("SELECT * FROM ttrss_filters2_rules WHERE filter_id = ".$line["id"]); - while ($tmp_line = db_fetch_assoc($tmp_result)) { + while ($tmp_line = $this->dbh->fetch_assoc($tmp_result)) { unset($tmp_line["id"]); unset($tmp_line["filter_id"]); $cat_filter = sql_bool_to_bool($tmp_line["cat_filter"]); if ($cat_filter && $tmp_line["cat_id"] || $tmp_line["feed_id"]) { - $tmp_line["feed"] = getFeedTitle($this->link, + $tmp_line["feed"] = getFeedTitle( $cat_filter ? $tmp_line["cat_id"] : $tmp_line["feed_id"], $cat_filter); } else { @@ -197,10 +197,10 @@ class Opml extends Handler_Protected { array_push($line["rules"], $tmp_line); } - $tmp_result = db_query($this->link, "SELECT * FROM ttrss_filters2_actions + $tmp_result = $this->dbh->query("SELECT * FROM ttrss_filters2_actions WHERE filter_id = ".$line["id"]); - while ($tmp_line = db_fetch_assoc($tmp_result)) { + while ($tmp_line = $this->dbh->fetch_assoc($tmp_result)) { unset($tmp_line["id"]); unset($tmp_line["filter_id"]); @@ -253,19 +253,19 @@ class Opml extends Handler_Protected { private function opml_import_feed($doc, $node, $cat_id, $owner_uid) { $attrs = $node->attributes; - $feed_title = db_escape_string($attrs->getNamedItem('text')->nodeValue); - if (!$feed_title) $feed_title = db_escape_string($attrs->getNamedItem('title')->nodeValue); + $feed_title = $this->dbh->escape_string(mb_substr($attrs->getNamedItem('text')->nodeValue, 0, 250)); + if (!$feed_title) $feed_title = $this->dbh->escape_string(mb_substr($attrs->getNamedItem('title')->nodeValue, 0, 250)); - $feed_url = db_escape_string($attrs->getNamedItem('xmlUrl')->nodeValue); - if (!$feed_url) $feed_url = db_escape_string($attrs->getNamedItem('xmlURL')->nodeValue); + $feed_url = $this->dbh->escape_string(mb_substr($attrs->getNamedItem('xmlUrl')->nodeValue, 0, 250)); + if (!$feed_url) $feed_url = $this->dbh->escape_string(mb_substr($attrs->getNamedItem('xmlURL')->nodeValue, 0, 250)); - $site_url = db_escape_string($attrs->getNamedItem('htmlUrl')->nodeValue); + $site_url = $this->dbh->escape_string(mb_substr($attrs->getNamedItem('htmlUrl')->nodeValue, 0, 250)); if ($feed_url && $feed_title) { - $result = db_query($this->link, "SELECT id FROM ttrss_feeds WHERE + $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE feed_url = '$feed_url' AND owner_uid = '$owner_uid'"); - if (db_num_rows($result) == 0) { + if ($this->dbh->num_rows($result) == 0) { #$this->opml_notice("[FEED] [$feed_title/$feed_url] dst_CAT=$cat_id"); $this->opml_notice(T_sprintf("Adding feed: %s", $feed_title)); @@ -275,7 +275,7 @@ class Opml extends Handler_Protected { (title, feed_url, owner_uid, cat_id, site_url, order_id) VALUES ('$feed_title', '$feed_url', '$owner_uid', $cat_id, '$site_url', 0)"; - db_query($this->link, $query); + $this->dbh->query($query); } else { $this->opml_notice(T_sprintf("Duplicate feed: %s", $feed_title)); @@ -285,15 +285,15 @@ class Opml extends Handler_Protected { private function opml_import_label($doc, $node, $owner_uid) { $attrs = $node->attributes; - $label_name = db_escape_string($attrs->getNamedItem('label-name')->nodeValue); + $label_name = $this->dbh->escape_string($attrs->getNamedItem('label-name')->nodeValue); if ($label_name) { - $fg_color = db_escape_string($attrs->getNamedItem('label-fg-color')->nodeValue); - $bg_color = db_escape_string($attrs->getNamedItem('label-bg-color')->nodeValue); + $fg_color = $this->dbh->escape_string($attrs->getNamedItem('label-fg-color')->nodeValue); + $bg_color = $this->dbh->escape_string($attrs->getNamedItem('label-bg-color')->nodeValue); - if (!label_find_id($this->link, $label_name, $_SESSION['uid'])) { + if (!label_find_id($label_name, $_SESSION['uid'])) { $this->opml_notice(T_sprintf("Adding label %s", htmlspecialchars($label_name))); - label_create($this->link, $label_name, $fg_color, $bg_color, $owner_uid); + label_create($label_name, $fg_color, $bg_color, $owner_uid); } else { $this->opml_notice(T_sprintf("Duplicate label: %s", htmlspecialchars($label_name))); } @@ -302,22 +302,22 @@ class Opml extends Handler_Protected { private function opml_import_preference($doc, $node, $owner_uid) { $attrs = $node->attributes; - $pref_name = db_escape_string($attrs->getNamedItem('pref-name')->nodeValue); + $pref_name = $this->dbh->escape_string($attrs->getNamedItem('pref-name')->nodeValue); if ($pref_name) { - $pref_value = db_escape_string($attrs->getNamedItem('value')->nodeValue); + $pref_value = $this->dbh->escape_string($attrs->getNamedItem('value')->nodeValue); $this->opml_notice(T_sprintf("Setting preference key %s to %s", $pref_name, $pref_value)); - set_pref($this->link, $pref_name, $pref_value); + set_pref($pref_name, $pref_value); } } private function opml_import_filter($doc, $node, $owner_uid) { $attrs = $node->attributes; - $filter_type = db_escape_string($attrs->getNamedItem('filter-type')->nodeValue); + $filter_type = $this->dbh->escape_string($attrs->getNamedItem('filter-type')->nodeValue); if ($filter_type == '2') { $filter = json_decode($node->nodeValue, true); @@ -326,14 +326,14 @@ class Opml extends Handler_Protected { $match_any_rule = bool_to_sql_bool($filter["match_any_rule"]); $enabled = bool_to_sql_bool($filter["enabled"]); - db_query($this->link, "BEGIN"); + $this->dbh->query("BEGIN"); - db_query($this->link, "INSERT INTO ttrss_filters2 (match_any_rule,enabled,owner_uid) + $this->dbh->query("INSERT INTO ttrss_filters2 (match_any_rule,enabled,owner_uid) VALUES ($match_any_rule, $enabled,".$_SESSION["uid"].")"); - $result = db_query($this->link, "SELECT MAX(id) AS id FROM ttrss_filters2 WHERE + $result = $this->dbh->query("SELECT MAX(id) AS id FROM ttrss_filters2 WHERE owner_uid = ".$_SESSION["uid"]); - $filter_id = db_fetch_result($result, 0, "id"); + $filter_id = $this->dbh->fetch_result($result, 0, "id"); if ($filter_id) { $this->opml_notice(T_sprintf("Adding filter...")); @@ -343,39 +343,39 @@ class Opml extends Handler_Protected { $cat_id = "NULL"; if (!$rule["cat_filter"]) { - $tmp_result = db_query($this->link, "SELECT id FROM ttrss_feeds - WHERE title = '".db_escape_string($rule["feed"])."' AND owner_uid = ".$_SESSION["uid"]); - if (db_num_rows($tmp_result) > 0) { - $feed_id = db_fetch_result($tmp_result, 0, "id"); + $tmp_result = $this->dbh->query("SELECT id FROM ttrss_feeds + WHERE title = '".$this->dbh->escape_string($rule["feed"])."' AND owner_uid = ".$_SESSION["uid"]); + if ($this->dbh->num_rows($tmp_result) > 0) { + $feed_id = $this->dbh->fetch_result($tmp_result, 0, "id"); } } else { - $tmp_result = db_query($this->link, "SELECT id FROM ttrss_feed_categories - WHERE title = '".db_escape_string($rule["feed"])."' AND owner_uid = ".$_SESSION["uid"]); + $tmp_result = $this->dbh->query("SELECT id FROM ttrss_feed_categories + WHERE title = '".$this->dbh->escape_string($rule["feed"])."' AND owner_uid = ".$_SESSION["uid"]); - if (db_num_rows($tmp_result) > 0) { - $cat_id = db_fetch_result($tmp_result, 0, "id"); + if ($this->dbh->num_rows($tmp_result) > 0) { + $cat_id = $this->dbh->fetch_result($tmp_result, 0, "id"); } } $cat_filter = bool_to_sql_bool($rule["cat_filter"]); - $reg_exp = db_escape_string($rule["reg_exp"]); + $reg_exp = $this->dbh->escape_string($rule["reg_exp"]); $filter_type = (int)$rule["filter_type"]; - db_query($this->link, "INSERT INTO ttrss_filters2_rules (feed_id,cat_id,filter_id,filter_type,reg_exp,cat_filter) + $this->dbh->query("INSERT INTO ttrss_filters2_rules (feed_id,cat_id,filter_id,filter_type,reg_exp,cat_filter) VALUES ($feed_id, $cat_id, $filter_id, $filter_type, '$reg_exp', $cat_filter)"); } foreach ($filter["actions"] as $action) { $action_id = (int)$action["action_id"]; - $action_param = db_escape_string($action["action_param"]); + $action_param = $this->dbh->escape_string($action["action_param"]); - db_query($this->link, "INSERT INTO ttrss_filters2_actions (filter_id,action_id,action_param) + $this->dbh->query("INSERT INTO ttrss_filters2_actions (filter_id,action_id,action_param) VALUES ($filter_id, $action_id, '$action_param')"); } } - db_query($this->link, "COMMIT"); + $this->dbh->query("COMMIT"); } } } @@ -383,22 +383,22 @@ class Opml extends Handler_Protected { private function opml_import_category($doc, $root_node, $owner_uid, $parent_id) { $body = $doc->getElementsByTagName('body'); - $default_cat_id = (int) get_feed_category($this->link, 'Imported feeds', false); + $default_cat_id = (int) get_feed_category('Imported feeds', false); if ($root_node) { - $cat_title = db_escape_string($root_node->attributes->getNamedItem('text')->nodeValue); + $cat_title = $this->dbh->escape_string(mb_substr($root_node->attributes->getNamedItem('text')->nodeValue, 0, 250)); if (!$cat_title) - $cat_title = db_escape_string($root_node->attributes->getNamedItem('title')->nodeValue); + $cat_title = $this->dbh->escape_string(mb_substr($root_node->attributes->getNamedItem('title')->nodeValue, 0, 250)); if (!in_array($cat_title, array("tt-rss-filters", "tt-rss-labels", "tt-rss-prefs"))) { - $cat_id = get_feed_category($this->link, $cat_title, $parent_id); - db_query($this->link, "BEGIN"); + $cat_id = get_feed_category($cat_title, $parent_id); + $this->dbh->query("BEGIN"); if ($cat_id === false) { - add_feed_category($this->link, $cat_title, $parent_id); - $cat_id = get_feed_category($this->link, $cat_title, $parent_id); + add_feed_category($cat_title, $parent_id); + $cat_id = get_feed_category($cat_title, $parent_id); } - db_query($this->link, "COMMIT"); + $this->dbh->query("COMMIT"); } else { $cat_id = 0; } @@ -418,12 +418,12 @@ class Opml extends Handler_Protected { foreach ($outlines as $node) { if ($node->hasAttributes() && strtolower($node->tagName) == "outline") { $attrs = $node->attributes; - $node_cat_title = db_escape_string($attrs->getNamedItem('text')->nodeValue); + $node_cat_title = $this->dbh->escape_string($attrs->getNamedItem('text')->nodeValue); if (!$node_cat_title) - $node_cat_title = db_escape_string($attrs->getNamedItem('title')->nodeValue); + $node_cat_title = $this->dbh->escape_string($attrs->getNamedItem('title')->nodeValue); - $node_feed_url = db_escape_string($attrs->getNamedItem('xmlUrl')->nodeValue); + $node_feed_url = $this->dbh->escape_string($attrs->getNamedItem('xmlUrl')->nodeValue); if ($node_cat_title && !$node_feed_url) { $this->opml_import_category($doc, $node, $owner_uid, $cat_id); @@ -461,13 +461,38 @@ class Opml extends Handler_Protected { # if ($debug) $doc = DOMDocument::load("/tmp/test.opml"); - if (is_file($_FILES['opml_file']['tmp_name'])) { - $doc = DOMDocument::load($_FILES['opml_file']['tmp_name']); - } else if (!$doc) { + if ($_FILES['opml_file']['error'] != 0) { + print_error(T_sprintf("Upload failed with error code %d", + $_FILES['opml_file']['error'])); + return; + } + + $tmp_file = false; + + if (is_uploaded_file($_FILES['opml_file']['tmp_name'])) { + $tmp_file = tempnam(CACHE_DIR . '/upload', 'opml'); + + $result = move_uploaded_file($_FILES['opml_file']['tmp_name'], + $tmp_file); + + if (!$result) { + print_error(__("Unable to move uploaded file.")); + return; + } + } else { print_error(__('Error: please upload OPML file.')); return; } + if (is_file($tmp_file)) { + $doc = new DOMDocument(); + $doc->load($tmp_file); + unlink($tmp_file); + } else if (!$doc) { + print_error(__('Error: unable to find moved OPML file.')); + return; + } + if ($doc) { $this->opml_import_category($doc, false, $owner_uid, false); } else { @@ -479,11 +504,11 @@ class Opml extends Handler_Protected { print "$msg<br/>"; } - static function opml_publish_url($link){ + static function opml_publish_url(){ $url_path = get_self_url_prefix(); $url_path .= "/opml.php?op=publish&key=" . - get_feed_access_key($link, 'OPML:Publish', false, $_SESSION["uid"]); + get_feed_access_key('OPML:Publish', false, $_SESSION["uid"]); return $url_path; } diff --git a/classes/plugin.php b/classes/plugin.php index e655a2062..8fbacf363 100644 --- a/classes/plugin.php +++ b/classes/plugin.php @@ -1,10 +1,12 @@ <?php class Plugin { - private $link; + private $dbh; private $host; + const API_VERSION_COMPAT = 1; + function init($host) { - $this->link = $host->get_link(); + $this->dbh = $host->get_dbh(); $this->host = $host; } @@ -20,5 +22,9 @@ class Plugin { function get_prefs_js() { return ""; } + + function api_version() { + return Plugin::API_VERSION_COMPAT; + } } ?> diff --git a/classes/pluginhandler.php b/classes/pluginhandler.php index eb859ab32..690305165 100644 --- a/classes/pluginhandler.php +++ b/classes/pluginhandler.php @@ -5,9 +5,7 @@ class PluginHandler extends Handler_Protected { } function catchall($method) { - global $pluginhost; - - $plugin = $pluginhost->get_plugin($_REQUEST["plugin"]); + $plugin = PluginHost::getInstance()->get_plugin($_REQUEST["plugin"]); if ($plugin) { if (method_exists($plugin, $method)) { diff --git a/classes/pluginhost.php b/classes/pluginhost.php index 592629881..bc5dc96be 100644 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -1,13 +1,19 @@ <?php class PluginHost { - private $link; + private $dbh; private $hooks = array(); private $plugins = array(); private $handlers = array(); private $commands = array(); private $storage = array(); + private $feeds = array(); + private $api_methods = array(); private $owner_uid; private $debug; + private $last_registered; + private static $instance; + + const API_VERSION = 2; const HOOK_ARTICLE_BUTTON = 1; const HOOK_ARTICLE_FILTER = 2; @@ -21,17 +27,35 @@ class PluginHost { const HOOK_RENDER_ARTICLE = 10; const HOOK_RENDER_ARTICLE_CDM = 11; const HOOK_FEED_FETCHED = 12; + const HOOK_SANITIZE = 13; + const HOOK_RENDER_ARTICLE_API = 14; + const HOOK_TOOLBAR_BUTTON = 15; + const HOOK_ACTION_ITEM = 16; + const HOOK_HEADLINE_TOOLBAR_BUTTON = 17; + const HOOK_HOTKEY_INFO = 18; + const HOOK_ARTICLE_LEFT_BUTTON = 19; + const HOOK_PREFS_EDIT_FEED = 20; + const HOOK_PREFS_SAVE_FEED = 21; const KIND_ALL = 1; const KIND_SYSTEM = 2; const KIND_USER = 3; - function __construct($link) { - $this->link = $link; + function __construct() { + $this->dbh = Db::get(); + + $this->storage = array(); + } + + private function __clone() { + // + } - $this->storage = $_SESSION["plugin_storage"]; + public static function getInstance() { + if (self::$instance == null) + self::$instance = new self(); - if (!$this->storage) $this->storage = array(); + return self::$instance; } private function register_plugin($name, $plugin) { @@ -39,8 +63,13 @@ class PluginHost { $this->plugins[$name] = $plugin; } + // needed for compatibility with API 1 function get_link() { - return $this->link; + return false; + } + + function get_dbh() { + return $this->dbh; } function get_plugins() { @@ -94,6 +123,9 @@ class PluginHost { foreach ($plugins as $class) { $class = trim($class); $class_file = strtolower(basename($class)); + + if (!is_dir(dirname(__FILE__)."/../plugins/$class_file")) continue; + $file = dirname(__FILE__)."/../plugins/$class_file/init.php"; if (!isset($this->plugins[$class])) { @@ -102,6 +134,15 @@ class PluginHost { if (class_exists($class) && is_subclass_of($class, "Plugin")) { $plugin = new $class($this); + $plugin_api = $plugin->api_version(); + + if ($plugin_api < PluginHost::API_VERSION) { + user_error("Plugin $class is not compatible with current API version (need: " . PluginHost::API_VERSION . ", got: $plugin_api)", E_USER_WARNING); + continue; + } + + $this->last_registered = $class; + switch ($kind) { case $this::KIND_SYSTEM: if ($this->is_system($plugin)) { @@ -145,7 +186,7 @@ class PluginHost { } } - function del_handler($handler, $method) { + function del_handler($handler, $method, $sender) { $handler = str_replace("-", "_", strtolower($handler)); $method = strtolower($method); @@ -169,10 +210,12 @@ class PluginHost { return false; } - function add_command($command, $description, $sender) { - $command = "-" . str_replace("-", "_", strtolower($command)); + function add_command($command, $description, $sender, $suffix = "", $arghelp = "") { + $command = str_replace("-", "_", strtolower($command)); $this->commands[$command] = array("description" => $description, + "suffix" => $suffix, + "arghelp" => $arghelp, "class" => $sender); } @@ -200,7 +243,7 @@ class PluginHost { function run_commands($args) { foreach ($this->get_commands() as $command => $data) { - if (in_array($command, $args)) { + if (isset($args[$command])) { $command = str_replace("-", "", $command); $data["class"]->$command($args); } @@ -208,45 +251,41 @@ class PluginHost { } function load_data($force = false) { - if ($this->owner_uid && (!$_SESSION["plugin_storage"] || $force)) { - $plugin = db_escape_string($plugin); - - $result = db_query($this->link, "SELECT name, content FROM ttrss_plugin_storage + if ($this->owner_uid) { + $result = $this->dbh->query("SELECT name, content FROM ttrss_plugin_storage WHERE owner_uid = '".$this->owner_uid."'"); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $this->storage[$line["name"]] = unserialize($line["content"]); } - - $_SESSION["plugin_storage"] = $this->storage; } } private function save_data($plugin) { if ($this->owner_uid) { - $plugin = db_escape_string($plugin); + $plugin = $this->dbh->escape_string($plugin); - db_query($this->link, "BEGIN"); + $this->dbh->query("BEGIN"); - $result = db_query($this->link,"SELECT id FROM ttrss_plugin_storage WHERE + $result = $this->dbh->query("SELECT id FROM ttrss_plugin_storage WHERE owner_uid= '".$this->owner_uid."' AND name = '$plugin'"); if (!isset($this->storage[$plugin])) $this->storage[$plugin] = array(); - $content = db_escape_string(serialize($this->storage[$plugin])); + $content = $this->dbh->escape_string(serialize($this->storage[$plugin])); - if (db_num_rows($result) != 0) { - db_query($this->link, "UPDATE ttrss_plugin_storage SET content = '$content' + if ($this->dbh->num_rows($result) != 0) { + $this->dbh->query("UPDATE ttrss_plugin_storage SET content = '$content' WHERE owner_uid= '".$this->owner_uid."' AND name = '$plugin'"); } else { - db_query($this->link, "INSERT INTO ttrss_plugin_storage + $this->dbh->query("INSERT INTO ttrss_plugin_storage (name,owner_uid,content) VALUES ('$plugin','".$this->owner_uid."','$content')"); } - db_query($this->link, "COMMIT"); + $this->dbh->query("COMMIT"); } } @@ -258,8 +297,6 @@ class PluginHost { $this->storage[$idx][$name] = $value; - $_SESSION["plugin_storage"] = $this->storage; - if ($sync) $this->save_data(get_class($sender)); } @@ -285,10 +322,8 @@ class PluginHost { unset($this->storage[$idx]); - db_query($this->link, "DELETE FROM ttrss_plugin_storage WHERE name = '$idx' + $this->dbh->query("DELETE FROM ttrss_plugin_storage WHERE name = '$idx' AND owner_uid = " . $this->owner_uid); - - $_SESSION["plugin_storage"] = $this->storage; } } @@ -299,5 +334,52 @@ class PluginHost { function get_debug() { return $this->debug; } + + // Plugin feed functions are *EXPERIMENTAL*! + + // cat_id: only -1 is supported (Special) + function add_feed($cat_id, $title, $icon, $sender) { + if (!$this->feeds[$cat_id]) $this->feeds[$cat_id] = array(); + + $id = count($this->feeds[$cat_id]); + + array_push($this->feeds[$cat_id], + array('id' => $id, 'title' => $title, 'sender' => $sender, 'icon' => $icon)); + + return $id; + } + + function get_feeds($cat_id) { + return $this->feeds[$cat_id]; + } + + // convert feed_id (e.g. -129) to pfeed_id first + function get_feed_handler($pfeed_id) { + foreach ($this->feeds as $cat) { + foreach ($cat as $feed) { + if ($feed['id'] == $pfeed_id) { + return $feed['sender']; + } + } + } + } + + static function pfeed_to_feed_id($label) { + return PLUGIN_FEED_BASE_INDEX - 1 - abs($label); + } + + static function feed_to_pfeed_id($feed) { + return PLUGIN_FEED_BASE_INDEX - 1 + abs($feed); + } + + function add_api_method($name, $sender) { + if ($this->is_system($sender)) { + $this->api_methods[strtolower($name)] = $sender; + } + } + + function get_api_method($name) { + return $this->api_methods[$name]; + } } ?> diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index bfcc75f0d..ffe7410fe 100644 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -3,7 +3,8 @@ class Pref_Feeds extends Handler_Protected { function csrf_ignore($method) { $csrf_ignored = array("index", "getfeedtree", "add", "editcats", "editfeed", - "savefeedorder", "uploadicon", "feedswitherrors", "inactivefeeds"); + "savefeedorder", "uploadicon", "feedswitherrors", "inactivefeeds", + "batchsubscribe"); return array_search($method, $csrf_ignored) !== false; } @@ -14,11 +15,11 @@ class Pref_Feeds extends Handler_Protected { } function renamecat() { - $title = db_escape_string($_REQUEST['title']); - $id = db_escape_string($_REQUEST['id']); + $title = $this->dbh->escape_string($_REQUEST['title']); + $id = $this->dbh->escape_string($_REQUEST['id']); if ($title) { - db_query($this->link, "UPDATE ttrss_feed_categories SET + $this->dbh->query("UPDATE ttrss_feed_categories SET title = '$title' WHERE id = '$id' AND owner_uid = " . $_SESSION["uid"]); } return; @@ -33,15 +34,16 @@ class Pref_Feeds extends Handler_Protected { if ($search) $search_qpart = " AND LOWER(title) LIKE LOWER('%$search%')"; - $show_empty_cats = $_REQUEST['mode'] != 2 && !$search && - get_pref($this->link, '_PREFS_SHOW_EMPTY_CATS'); + // first one is set by API + $show_empty_cats = $_REQUEST['force_show_empty'] || + ($_REQUEST['mode'] != 2 && !$search); $items = array(); - $result = db_query($this->link, "SELECT id, title FROM ttrss_feed_categories + $result = $this->dbh->query("SELECT id, title FROM ttrss_feed_categories WHERE owner_uid = " . $_SESSION["uid"] . " AND parent_cat = '$cat_id' ORDER BY order_id, title"); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $cat = array(); $cat['id'] = 'CAT:' . $line['id']; @@ -55,20 +57,20 @@ class Pref_Feeds extends Handler_Protected { $cat['items'] = $this->get_category_items($line['id']); - $cat['param'] = T_sprintf('(%d feeds)', count($cat['items'])); + $cat['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($cat['items'])), count($cat['items'])); if (count($cat['items']) > 0 || $show_empty_cats) array_push($items, $cat); } - $feed_result = db_query($this->link, "SELECT id, title, last_error, + $feed_result = $this->dbh->query("SELECT id, title, last_error, ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated FROM ttrss_feeds WHERE cat_id = '$cat_id' AND owner_uid = ".$_SESSION["uid"]. "$search_qpart ORDER BY order_id, title"); - while ($feed_line = db_fetch_assoc($feed_result)) { + while ($feed_line = $this->dbh->fetch_assoc($feed_result)) { $feed = array(); $feed['id'] = 'FEED:' . $feed_line['id']; $feed['bare_id'] = (int)$feed_line['id']; @@ -77,7 +79,7 @@ class Pref_Feeds extends Handler_Protected { $feed['unread'] = 0; $feed['error'] = $feed_line['last_error']; $feed['icon'] = getFeedIcon($feed_line['id']); - $feed['param'] = make_local_datetime($this->link, + $feed['param'] = make_local_datetime( $feed_line['last_updated'], true); array_push($items, $feed); @@ -87,6 +89,10 @@ class Pref_Feeds extends Handler_Protected { } function getfeedtree() { + print json_encode($this->makefeedtree()); + } + + function makefeedtree() { if ($_REQUEST['mode'] != 2) $search = $_SESSION["prefs_feed_search"]; @@ -101,7 +107,7 @@ class Pref_Feeds extends Handler_Protected { $root['items'] = array(); $root['type'] = 'category'; - $enable_cats = get_pref($this->link, 'ENABLE_FEED_CATS'); + $enable_cats = get_pref('ENABLE_FEED_CATS'); if ($_REQUEST['mode'] == 2) { @@ -115,26 +121,50 @@ class Pref_Feeds extends Handler_Protected { array_push($cat['items'], $this->feedlist_init_feed($i)); } + /* Plugin feeds for -1 */ + + $feeds = PluginHost::getInstance()->get_feeds(-1); + + if ($feeds) { + foreach ($feeds as $feed) { + $feed_id = PluginHost::pfeed_to_feed_id($feed['id']); + + $item = array(); + $item['id'] = 'FEED:' . $feed_id; + $item['bare_id'] = (int)$feed_id; + $item['name'] = $feed['title']; + $item['checkbox'] = false; + $item['error'] = ''; + $item['icon'] = $feed['icon']; + + $item['param'] = ''; + $item['unread'] = 0; //$feed['sender']->get_unread($feed['id']); + $item['type'] = 'feed'; + + array_push($cat['items'], $item); + } + } + if ($enable_cats) { array_push($root['items'], $cat); } else { $root['items'] = array_merge($root['items'], $cat['items']); } - $result = db_query($this->link, "SELECT * FROM + $result = $this->dbh->query("SELECT * FROM ttrss_labels2 WHERE owner_uid = ".$_SESSION['uid']." ORDER by caption"); - if (db_num_rows($result) > 0) { + if ($this->dbh->num_rows($result) > 0) { - if (get_pref($this->link, 'ENABLE_FEED_CATS')) { + if (get_pref('ENABLE_FEED_CATS')) { $cat = $this->feedlist_init_cat(-2); } else { $cat['items'] = array(); } - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { - $label_id = -$line['id'] - 11; + $label_id = label_to_feed_id($line['id']); $feed = $this->feedlist_init_feed($label_id, false, 0); @@ -153,13 +183,13 @@ class Pref_Feeds extends Handler_Protected { } if ($enable_cats) { - $show_empty_cats = $_REQUEST['mode'] != 2 && !$search && - get_pref($this->link, '_PREFS_SHOW_EMPTY_CATS'); + $show_empty_cats = $_REQUEST['force_show_empty'] || + ($_REQUEST['mode'] != 2 && !$search); - $result = db_query($this->link, "SELECT id, title FROM ttrss_feed_categories + $result = $this->dbh->query("SELECT id, title FROM ttrss_feed_categories WHERE owner_uid = " . $_SESSION["uid"] . " AND parent_cat IS NULL ORDER BY order_id, title"); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $cat = array(); $cat['id'] = 'CAT:' . $line['id']; $cat['bare_id'] = (int)$line['id']; @@ -172,7 +202,7 @@ class Pref_Feeds extends Handler_Protected { $cat['items'] = $this->get_category_items($line['id']); - $cat['param'] = T_sprintf('(%d feeds)', count($cat['items'])); + $cat['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($cat['items'])), count($cat['items'])); if (count($cat['items']) > 0 || $show_empty_cats) array_push($root['items'], $cat); @@ -192,13 +222,13 @@ class Pref_Feeds extends Handler_Protected { $cat['unread'] = 0; $cat['child_unread'] = 0; - $feed_result = db_query($this->link, "SELECT id, title,last_error, + $feed_result = $this->dbh->query("SELECT id, title,last_error, ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated FROM ttrss_feeds WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"]. "$search_qpart ORDER BY order_id, title"); - while ($feed_line = db_fetch_assoc($feed_result)) { + while ($feed_line = $this->dbh->fetch_assoc($feed_result)) { $feed = array(); $feed['id'] = 'FEED:' . $feed_line['id']; $feed['bare_id'] = (int)$feed_line['id']; @@ -206,7 +236,7 @@ class Pref_Feeds extends Handler_Protected { $feed['checkbox'] = false; $feed['error'] = $feed_line['last_error']; $feed['icon'] = getFeedIcon($feed_line['id']); - $feed['param'] = make_local_datetime($this->link, + $feed['param'] = make_local_datetime( $feed_line['last_updated'], true); $feed['unread'] = 0; $feed['type'] = 'feed'; @@ -214,22 +244,22 @@ class Pref_Feeds extends Handler_Protected { array_push($cat['items'], $feed); } - $cat['param'] = T_sprintf('(%d feeds)', count($cat['items'])); + $cat['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($cat['items'])), count($cat['items'])); if (count($cat['items']) > 0 || $show_empty_cats) array_push($root['items'], $cat); $root['param'] += count($cat['items']); - $root['param'] = T_sprintf('(%d feeds)', $root['param']); + $root['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($cat['items'])), count($cat['items'])); } else { - $feed_result = db_query($this->link, "SELECT id, title, last_error, + $feed_result = $this->dbh->query("SELECT id, title, last_error, ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated FROM ttrss_feeds WHERE owner_uid = ".$_SESSION["uid"]. "$search_qpart ORDER BY order_id, title"); - while ($feed_line = db_fetch_assoc($feed_result)) { + while ($feed_line = $this->dbh->fetch_assoc($feed_result)) { $feed = array(); $feed['id'] = 'FEED:' . $feed_line['id']; $feed['bare_id'] = (int)$feed_line['id']; @@ -237,7 +267,7 @@ class Pref_Feeds extends Handler_Protected { $feed['checkbox'] = false; $feed['error'] = $feed_line['last_error']; $feed['icon'] = getFeedIcon($feed_line['id']); - $feed['param'] = make_local_datetime($this->link, + $feed['param'] = make_local_datetime( $feed_line['last_updated'], true); $feed['unread'] = 0; $feed['type'] = 'feed'; @@ -245,7 +275,7 @@ class Pref_Feeds extends Handler_Protected { array_push($root['items'], $feed); } - $root['param'] = T_sprintf('(%d feeds)', count($root['items'])); + $root['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($cat['items'])), count($cat['items'])); } $fl = array(); @@ -258,27 +288,21 @@ class Pref_Feeds extends Handler_Protected { $fl['items'] =& $root['items']; } - print json_encode($fl); - return; + return $fl; } function catsortreset() { - db_query($this->link, "UPDATE ttrss_feed_categories + $this->dbh->query("UPDATE ttrss_feed_categories SET order_id = 0 WHERE owner_uid = " . $_SESSION["uid"]); return; } function feedsortreset() { - db_query($this->link, "UPDATE ttrss_feeds + $this->dbh->query("UPDATE ttrss_feeds SET order_id = 0 WHERE owner_uid = " . $_SESSION["uid"]); return; } - function togglehiddenfeedcats() { - set_pref($this->link, '_PREFS_SHOW_EMPTY_CATS', - (get_pref($this->link, '_PREFS_SHOW_EMPTY_CATS') ? 'false' : 'true')); - } - private function process_category_order(&$data_map, $item_id, $parent_id = false, $nest_level = 0) { $debug = isset($_REQUEST["debug"]); @@ -293,12 +317,12 @@ class Pref_Feeds extends Handler_Protected { if ($item_id != 'root') { if ($parent_id && $parent_id != 'root') { $parent_bare_id = substr($parent_id, strpos($parent_id, ':')+1); - $parent_qpart = db_escape_string($parent_bare_id); + $parent_qpart = $this->dbh->escape_string($parent_bare_id); } else { $parent_qpart = 'NULL'; } - db_query($this->link, "UPDATE ttrss_feed_categories + $this->dbh->query("UPDATE ttrss_feed_categories SET parent_cat = $parent_qpart WHERE id = '$bare_item_id' AND owner_uid = " . $_SESSION["uid"]); } @@ -319,12 +343,12 @@ class Pref_Feeds extends Handler_Protected { if (strpos($id, "FEED") === 0) { $cat_id = ($item_id != "root") ? - db_escape_string($bare_item_id) : "NULL"; + $this->dbh->escape_string($bare_item_id) : "NULL"; $cat_qpart = ($cat_id != 0) ? "cat_id = '$cat_id'" : "cat_id = NULL"; - db_query($this->link, "UPDATE ttrss_feeds + $this->dbh->query("UPDATE ttrss_feeds SET order_id = $order_id, $cat_qpart WHERE id = '$bare_id' AND owner_uid = " . $_SESSION["uid"]); @@ -334,12 +358,12 @@ class Pref_Feeds extends Handler_Protected { $nest_level+1); if ($item_id != 'root') { - $parent_qpart = db_escape_string($bare_id); + $parent_qpart = $this->dbh->escape_string($bare_id); } else { $parent_qpart = 'NULL'; } - db_query($this->link, "UPDATE ttrss_feed_categories + $this->dbh->query("UPDATE ttrss_feed_categories SET order_id = '$order_id' WHERE id = '$bare_id' AND owner_uid = " . $_SESSION["uid"]); } @@ -391,7 +415,7 @@ class Pref_Feeds extends Handler_Protected { ++$cat_order_id; if ($bare_id > 0) { - db_query($this->link, "UPDATE ttrss_feed_categories + $this->dbh->query("UPDATE ttrss_feed_categories SET order_id = '$cat_order_id' WHERE id = '$bare_id' AND owner_uid = " . $_SESSION["uid"]); } @@ -408,7 +432,7 @@ class Pref_Feeds extends Handler_Protected { else $cat_query = "cat_id = NULL"; - db_query($this->link, "UPDATE ttrss_feeds + $this->dbh->query("UPDATE ttrss_feeds SET order_id = '$feed_order_id', $cat_query WHERE id = '$feed_id' AND @@ -424,13 +448,16 @@ class Pref_Feeds extends Handler_Protected { } function removeicon() { - $feed_id = db_escape_string($_REQUEST["feed_id"]); + $feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]); - $result = db_query($this->link, "SELECT id FROM ttrss_feeds + $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE id = '$feed_id' AND owner_uid = ". $_SESSION["uid"]); - if (db_num_rows($result) != 0) { - unlink(ICONS_DIR . "/$feed_id.ico"); + if ($this->dbh->num_rows($result) != 0) { + @unlink(ICONS_DIR . "/$feed_id.ico"); + + $this->dbh->query("UPDATE ttrss_feeds SET favicon_avg_color = NULL + where id = '$feed_id'"); } return; @@ -439,19 +466,39 @@ class Pref_Feeds extends Handler_Protected { function uploadicon() { header("Content-type: text/html"); - $icon_file = $_FILES['icon_file']['tmp_name']; - $feed_id = db_escape_string($_REQUEST["feed_id"]); + $tmp_file = false; + + if (is_uploaded_file($_FILES['icon_file']['tmp_name'])) { + $tmp_file = tempnam(CACHE_DIR . '/upload', 'icon'); + + $result = move_uploaded_file($_FILES['icon_file']['tmp_name'], + $tmp_file); + + if (!$result) { + return; + } + } else { + return; + } + + $icon_file = $tmp_file; + $feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]); if (is_file($icon_file) && $feed_id) { if (filesize($icon_file) < 20000) { - $result = db_query($this->link, "SELECT id FROM ttrss_feeds + $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE id = '$feed_id' AND owner_uid = ". $_SESSION["uid"]); - if (db_num_rows($result) != 0) { - unlink(ICONS_DIR . "/$feed_id.ico"); - move_uploaded_file($icon_file, ICONS_DIR . "/$feed_id.ico"); - $rc = 0; + if ($this->dbh->num_rows($result) != 0) { + @unlink(ICONS_DIR . "/$feed_id.ico"); + if (rename($icon_file, ICONS_DIR . "/$feed_id.ico")) { + $this->dbh->query("UPDATE ttrss_feeds SET + favicon_avg_color = '' + WHERE id = '$feed_id'"); + + $rc = 0; + } } else { $rc = 2; } @@ -462,6 +509,8 @@ class Pref_Feeds extends Handler_Protected { $rc = 2; } + @unlink($icon_file); + print "<script type=\"text/javascript\">"; print "parent.uploadIconHandler($rc);"; print "</script>"; @@ -472,13 +521,16 @@ class Pref_Feeds extends Handler_Protected { global $purge_intervals; global $update_intervals; - $feed_id = db_escape_string($_REQUEST["id"]); + $feed_id = $this->dbh->escape_string($_REQUEST["id"]); - $result = db_query($this->link, + $result = $this->dbh->query( "SELECT * FROM ttrss_feeds WHERE id = '$feed_id' AND owner_uid = " . $_SESSION["uid"]); - $title = htmlspecialchars(db_fetch_result($result, + $auth_pass_encrypted = sql_bool_to_bool($this->dbh->fetch_result($result, 0, + "auth_pass_encrypted")); + + $title = htmlspecialchars($this->dbh->fetch_result($result, 0, "title")); print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"id\" value=\"$feed_id\">"; @@ -496,8 +548,8 @@ class Pref_Feeds extends Handler_Protected { /* Feed URL */ - $feed_url = db_fetch_result($result, 0, "feed_url"); - $feed_url = htmlspecialchars(db_fetch_result($result, + $feed_url = $this->dbh->fetch_result($result, 0, "feed_url"); + $feed_url = htmlspecialchars($this->dbh->fetch_result($result, 0, "feed_url")); print "<hr/>"; @@ -508,7 +560,7 @@ class Pref_Feeds extends Handler_Protected { regExp='^(http|https)://.*' style=\"width : 20em\" name=\"feed_url\" value=\"$feed_url\">"; - $last_error = db_fetch_result($result, 0, "last_error"); + $last_error = $this->dbh->fetch_result($result, 0, "last_error"); if ($last_error) { print " <span title=\"".htmlspecialchars($last_error)."\" @@ -518,15 +570,15 @@ class Pref_Feeds extends Handler_Protected { /* Category */ - if (get_pref($this->link, 'ENABLE_FEED_CATS')) { + if (get_pref('ENABLE_FEED_CATS')) { - $cat_id = db_fetch_result($result, 0, "cat_id"); + $cat_id = $this->dbh->fetch_result($result, 0, "cat_id"); print "<hr/>"; print __('Place in category:') . " "; - print_feed_cat_select($this->link, "cat_id", $cat_id, + print_feed_cat_select("cat_id", $cat_id, 'dojoType="dijit.form.Select"'); } @@ -537,14 +589,14 @@ class Pref_Feeds extends Handler_Protected { /* Update Interval */ - $update_interval = db_fetch_result($result, 0, "update_interval"); + $update_interval = $this->dbh->fetch_result($result, 0, "update_interval"); print_select_hash("update_interval", $update_interval, $update_intervals, 'dojoType="dijit.form.Select"'); /* Purge intl */ - $purge_interval = db_fetch_result($result, 0, "purge_interval"); + $purge_interval = $this->dbh->fetch_result($result, 0, "purge_interval"); print "<hr/>"; print __('Article purging:') . " "; @@ -557,13 +609,20 @@ class Pref_Feeds extends Handler_Protected { print "<div class=\"dlgSec\">".__("Authentication")."</div>"; print "<div class=\"dlgSecCont\">"; - $auth_login = htmlspecialchars(db_fetch_result($result, 0, "auth_login")); + $auth_login = htmlspecialchars($this->dbh->fetch_result($result, 0, "auth_login")); print "<input dojoType=\"dijit.form.TextBox\" id=\"feedEditDlg_login\" placeHolder=\"".__("Login")."\" name=\"auth_login\" value=\"$auth_login\"><hr/>"; - $auth_pass = htmlspecialchars(db_fetch_result($result, 0, "auth_pass")); + $auth_pass = $this->dbh->fetch_result($result, 0, "auth_pass"); + + if ($auth_pass_encrypted) { + require_once "crypt.php"; + $auth_pass = decrypt_string($auth_pass); + } + + $auth_pass = htmlspecialchars($auth_pass); print "<input dojoType=\"dijit.form.TextBox\" type=\"password\" name=\"auth_pass\" placeHolder=\"".__("Password")."\" @@ -577,7 +636,7 @@ class Pref_Feeds extends Handler_Protected { print "<div class=\"dlgSec\">".__("Options")."</div>"; print "<div class=\"dlgSecCont\">"; - $private = sql_bool_to_bool(db_fetch_result($result, 0, "private")); + $private = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "private")); if ($private) { $checked = "checked=\"1\""; @@ -588,7 +647,7 @@ class Pref_Feeds extends Handler_Protected { print "<input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"private\" id=\"private\" $checked> <label for=\"private\">".__('Hide from Popular feeds')."</label>"; - $include_in_digest = sql_bool_to_bool(db_fetch_result($result, 0, "include_in_digest")); + $include_in_digest = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "include_in_digest")); if ($include_in_digest) { $checked = "checked=\"1\""; @@ -601,7 +660,7 @@ class Pref_Feeds extends Handler_Protected { $checked> <label for=\"include_in_digest\">".__('Include in e-mail digest')."</label>"; - $always_display_enclosures = sql_bool_to_bool(db_fetch_result($result, 0, "always_display_enclosures")); + $always_display_enclosures = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "always_display_enclosures")); if ($always_display_enclosures) { $checked = "checked"; @@ -613,8 +672,20 @@ class Pref_Feeds extends Handler_Protected { name=\"always_display_enclosures\" $checked> <label for=\"always_display_enclosures\">".__('Always display image attachments')."</label>"; + $hide_images = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "hide_images")); - $cache_images = sql_bool_to_bool(db_fetch_result($result, 0, "cache_images")); + if ($hide_images) { + $checked = "checked=\"1\""; + } else { + $checked = ""; + } + + print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"hide_images\" + name=\"hide_images\" + $checked> <label for=\"hide_images\">". + __('Do not embed images')."</label>"; + + $cache_images = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "cache_images")); if ($cache_images) { $checked = "checked=\"1\""; @@ -627,7 +698,7 @@ class Pref_Feeds extends Handler_Protected { $checked> <label for=\"cache_images\">". __('Cache images locally')."</label>"; - $mark_unread_on_update = sql_bool_to_bool(db_fetch_result($result, 0, "mark_unread_on_update")); + $mark_unread_on_update = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "mark_unread_on_update")); if ($mark_unread_on_update) { $checked = "checked"; @@ -664,6 +735,9 @@ class Pref_Feeds extends Handler_Protected { print "</div>"; + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_EDIT_FEED, + "hook_prefs_edit_feed", $feed_id); + $title = htmlspecialchars($title, ENT_QUOTES); print "<div class='dlgButtons'> @@ -672,7 +746,7 @@ class Pref_Feeds extends Handler_Protected { __('Unsubscribe')."</button>"; if (PUBSUBHUBBUB_ENABLED) { - $pubsub_state = db_fetch_result($result, 0, "pubsub_state"); + $pubsub_state = $this->dbh->fetch_result($result, 0, "pubsub_state"); $pubsub_btn_disabled = ($pubsub_state == 2) ? "" : "disabled=\"1\""; print "<button dojoType=\"dijit.form.Button\" id=\"pubsubReset_Btn\" $pubsub_btn_disabled @@ -696,9 +770,11 @@ class Pref_Feeds extends Handler_Protected { global $purge_intervals; global $update_intervals; - $feed_ids = db_escape_string($_REQUEST["ids"]); + $feed_ids = $this->dbh->escape_string($_REQUEST["ids"]); - print "<div class=\"dialogNotice\">" . __("Enable the options you wish to apply using checkboxes on the right:") . "</div>"; + print_notice("Enable the options you wish to apply using checkboxes on the right:"); + + print "<p>"; print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"ids\" value=\"$feed_ids\">"; print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-feeds\">"; @@ -711,7 +787,7 @@ class Pref_Feeds extends Handler_Protected { print "<input dojoType=\"dijit.form.ValidationTextBox\" disabled=\"1\" style=\"font-size : 16px; width : 20em;\" required=\"1\" - name=\"title\" value=\"$title\">"; + name=\"title\" value=\"\">"; $this->batch_edit_cbox("title"); @@ -722,19 +798,19 @@ class Pref_Feeds extends Handler_Protected { print __('URL:') . " "; print "<input dojoType=\"dijit.form.ValidationTextBox\" disabled=\"1\" required=\"1\" regExp='^(http|https)://.*' style=\"width : 20em\" - name=\"feed_url\" value=\"$feed_url\">"; + name=\"feed_url\" value=\"\">"; $this->batch_edit_cbox("feed_url"); /* Category */ - if (get_pref($this->link, 'ENABLE_FEED_CATS')) { + if (get_pref('ENABLE_FEED_CATS')) { print "<br/>"; print __('Place in category:') . " "; - print_feed_cat_select($this->link, "cat_id", $cat_id, + print_feed_cat_select("cat_id", false, 'disabled="1" dojoType="dijit.form.Select"'); $this->batch_edit_cbox("cat_id"); @@ -748,7 +824,7 @@ class Pref_Feeds extends Handler_Protected { /* Update Interval */ - print_select_hash("update_interval", $update_interval, $update_intervals, + print_select_hash("update_interval", "", $update_intervals, 'disabled="1" dojoType="dijit.form.Select"'); $this->batch_edit_cbox("update_interval"); @@ -761,7 +837,7 @@ class Pref_Feeds extends Handler_Protected { print __('Article purging:') . " "; - print_select_hash("purge_interval", $purge_interval, $purge_intervals, + print_select_hash("purge_interval", "", $purge_intervals, 'disabled="1" dojoType="dijit.form.Select"'); $this->batch_edit_cbox("purge_interval"); @@ -773,13 +849,13 @@ class Pref_Feeds extends Handler_Protected { print "<input dojoType=\"dijit.form.TextBox\" placeHolder=\"".__("Login")."\" disabled=\"1\" - name=\"auth_login\" value=\"$auth_login\">"; + name=\"auth_login\" value=\"\">"; $this->batch_edit_cbox("auth_login"); print "<br/><input dojoType=\"dijit.form.TextBox\" type=\"password\" name=\"auth_pass\" placeHolder=\"".__("Password")."\" disabled=\"1\" - value=\"$auth_pass\">"; + value=\"\">"; $this->batch_edit_cbox("auth_pass"); @@ -804,6 +880,14 @@ class Pref_Feeds extends Handler_Protected { print " "; $this->batch_edit_cbox("always_display_enclosures", "always_display_enclosures_l"); + print "<br/><input disabled=\"1\" type=\"checkbox\" id=\"hide_images\" + name=\"hide_images\" + dojoType=\"dijit.form.CheckBox\"> <label class='insensitive' id=\"hide_images_l\" + for=\"hide_images\">". + __('Do not embed images')."</label>"; + + print " "; $this->batch_edit_cbox("hide_images", "hide_images_l"); + print "<br/><input disabled=\"1\" type=\"checkbox\" id=\"cache_images\" name=\"cache_images\" dojoType=\"dijit.form.CheckBox\"> <label class='insensitive' id=\"cache_images_l\" @@ -842,28 +926,39 @@ class Pref_Feeds extends Handler_Protected { function editsaveops($batch) { - $feed_title = db_escape_string(trim($_POST["title"])); - $feed_link = db_escape_string(trim($_POST["feed_url"])); - $upd_intl = (int) db_escape_string($_POST["update_interval"]); - $purge_intl = (int) db_escape_string($_POST["purge_interval"]); - $feed_id = (int) db_escape_string($_POST["id"]); /* editSave */ - $feed_ids = db_escape_string($_POST["ids"]); /* batchEditSave */ - $cat_id = (int) db_escape_string($_POST["cat_id"]); - $auth_login = db_escape_string(trim($_POST["auth_login"])); - $auth_pass = db_escape_string(trim($_POST["auth_pass"])); - $private = checkbox_to_sql_bool(db_escape_string($_POST["private"])); + $feed_title = $this->dbh->escape_string(trim($_POST["title"])); + $feed_link = $this->dbh->escape_string(trim($_POST["feed_url"])); + $upd_intl = (int) $this->dbh->escape_string($_POST["update_interval"]); + $purge_intl = (int) $this->dbh->escape_string($_POST["purge_interval"]); + $feed_id = (int) $this->dbh->escape_string($_POST["id"]); /* editSave */ + $feed_ids = $this->dbh->escape_string($_POST["ids"]); /* batchEditSave */ + $cat_id = (int) $this->dbh->escape_string($_POST["cat_id"]); + $auth_login = $this->dbh->escape_string(trim($_POST["auth_login"])); + $auth_pass = trim($_POST["auth_pass"]); + $private = checkbox_to_sql_bool($this->dbh->escape_string($_POST["private"])); $include_in_digest = checkbox_to_sql_bool( - db_escape_string($_POST["include_in_digest"])); + $this->dbh->escape_string($_POST["include_in_digest"])); $cache_images = checkbox_to_sql_bool( - db_escape_string($_POST["cache_images"])); - + $this->dbh->escape_string($_POST["cache_images"])); + $hide_images = checkbox_to_sql_bool( + $this->dbh->escape_string($_POST["hide_images"])); $always_display_enclosures = checkbox_to_sql_bool( - db_escape_string($_POST["always_display_enclosures"])); + $this->dbh->escape_string($_POST["always_display_enclosures"])); $mark_unread_on_update = checkbox_to_sql_bool( - db_escape_string($_POST["mark_unread_on_update"])); + $this->dbh->escape_string($_POST["mark_unread_on_update"])); - if (get_pref($this->link, 'ENABLE_FEED_CATS')) { + if (strlen(FEED_CRYPT_KEY) > 0) { + require_once "crypt.php"; + $auth_pass = substr(encrypt_string($auth_pass), 0, 250); + $auth_pass_encrypted = 'true'; + } else { + $auth_pass_encrypted = 'false'; + } + + $auth_pass = $this->dbh->escape_string($auth_pass); + + if (get_pref('ENABLE_FEED_CATS')) { if ($cat_id && $cat_id != 0) { $category_qpart = "cat_id = '$cat_id',"; $category_qpart_nocomma = "cat_id = '$cat_id'"; @@ -878,20 +973,25 @@ class Pref_Feeds extends Handler_Protected { if (!$batch) { - $result = db_query($this->link, "UPDATE ttrss_feeds SET + $result = $this->dbh->query("UPDATE ttrss_feeds SET $category_qpart title = '$feed_title', feed_url = '$feed_link', update_interval = '$upd_intl', purge_interval = '$purge_intl', auth_login = '$auth_login', auth_pass = '$auth_pass', + auth_pass_encrypted = $auth_pass_encrypted, private = $private, cache_images = $cache_images, + hide_images = $hide_images, include_in_digest = $include_in_digest, always_display_enclosures = $always_display_enclosures, mark_unread_on_update = $mark_unread_on_update WHERE id = '$feed_id' AND owner_uid = " . $_SESSION["uid"]); + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_SAVE_FEED, + "hook_prefs_save_feed", $feed_id); + } else { $feed_data = array(); @@ -901,7 +1001,7 @@ class Pref_Feeds extends Handler_Protected { } } - db_query($this->link, "BEGIN"); + $this->dbh->query("BEGIN"); foreach (array_keys($feed_data) as $k) { @@ -929,7 +1029,8 @@ class Pref_Feeds extends Handler_Protected { break; case "auth_pass": - $qpart = "auth_pass = '$auth_pass'"; + $qpart = "auth_pass = '$auth_pass' AND + auth_pass_encrypted = $auth_pass_encrypted"; break; case "private": @@ -952,6 +1053,10 @@ class Pref_Feeds extends Handler_Protected { $qpart = "cache_images = $cache_images"; break; + case "hide_images": + $qpart = "hide_images = $hide_images"; + break; + case "cat_id": $qpart = $category_qpart_nocomma; break; @@ -959,23 +1064,23 @@ class Pref_Feeds extends Handler_Protected { } if ($qpart) { - db_query($this->link, + $this->dbh->query( "UPDATE ttrss_feeds SET $qpart WHERE id IN ($feed_ids) AND owner_uid = " . $_SESSION["uid"]); print "<br/>"; } } - db_query($this->link, "COMMIT"); + $this->dbh->query("COMMIT"); } return; } function resetPubSub() { - $ids = db_escape_string($_REQUEST["ids"]); + $ids = $this->dbh->escape_string($_REQUEST["ids"]); - db_query($this->link, "UPDATE ttrss_feeds SET pubsub_state = 0 WHERE id IN ($ids) + $this->dbh->query("UPDATE ttrss_feeds SET pubsub_state = 0 WHERE id IN ($ids) AND owner_uid = " . $_SESSION["uid"]); return; @@ -983,30 +1088,30 @@ class Pref_Feeds extends Handler_Protected { function remove() { - $ids = split(",", db_escape_string($_REQUEST["ids"])); + $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); foreach ($ids as $id) { - $this->remove_feed($this->link, $id, $_SESSION["uid"]); + Pref_Feeds::remove_feed($id, $_SESSION["uid"]); } return; } function clear() { - $id = db_escape_string($_REQUEST["id"]); - $this->clear_feed_articles($this->link, $id); + $id = $this->dbh->escape_string($_REQUEST["id"]); + $this->clear_feed_articles($id); } function rescore() { require_once "rssfuncs.php"; - $ids = split(",", db_escape_string($_REQUEST["ids"])); + $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); foreach ($ids as $id) { - $filters = load_filters($this->link, $id, $_SESSION["uid"], 6); + $filters = load_filters($id, $_SESSION["uid"], 6); - $result = db_query($this->link, "SELECT + $result = $this->dbh->query("SELECT title, content, link, ref_id, author,". SUBSTRING_FOR_DATE."(updated, 1, 19) AS updated FROM @@ -1017,9 +1122,9 @@ class Pref_Feeds extends Handler_Protected { $scores = array(); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { - $tags = get_article_tags($this->link, $line["ref_id"]); + $tags = get_article_tags($line["ref_id"]); $article_filters = get_article_filters($filters, $line['title'], $line['content'], $line['link'], strtotime($line['updated']), @@ -1034,15 +1139,15 @@ class Pref_Feeds extends Handler_Protected { foreach (array_keys($scores) as $s) { if ($s > 1000) { - db_query($this->link, "UPDATE ttrss_user_entries SET score = '$s', + $this->dbh->query("UPDATE ttrss_user_entries SET score = '$s', marked = true WHERE ref_id IN (" . join(',', $scores[$s]) . ")"); } else if ($s < -500) { - db_query($this->link, "UPDATE ttrss_user_entries SET score = '$s', + $this->dbh->query("UPDATE ttrss_user_entries SET score = '$s', unread = false WHERE ref_id IN (" . join(',', $scores[$s]) . ")"); } else { - db_query($this->link, "UPDATE ttrss_user_entries SET score = '$s' WHERE + $this->dbh->query("UPDATE ttrss_user_entries SET score = '$s' WHERE ref_id IN (" . join(',', $scores[$s]) . ")"); } } @@ -1054,16 +1159,16 @@ class Pref_Feeds extends Handler_Protected { function rescoreAll() { - $result = db_query($this->link, + $result = $this->dbh->query( "SELECT id FROM ttrss_feeds WHERE owner_uid = " . $_SESSION['uid']); - while ($feed_line = db_fetch_assoc($result)) { + while ($feed_line = $this->dbh->fetch_assoc($result)) { $id = $feed_line["id"]; - $filters = load_filters($this->link, $id, $_SESSION["uid"], 6); + $filters = load_filters($id, $_SESSION["uid"], 6); - $tmp_result = db_query($this->link, "SELECT + $tmp_result = $this->dbh->query("SELECT title, content, link, ref_id, author,". SUBSTRING_FOR_DATE."(updated, 1, 19) AS updated FROM @@ -1074,9 +1179,9 @@ class Pref_Feeds extends Handler_Protected { $scores = array(); - while ($line = db_fetch_assoc($tmp_result)) { + while ($line = $this->dbh->fetch_assoc($tmp_result)) { - $tags = get_article_tags($this->link, $line["ref_id"]); + $tags = get_article_tags($line["ref_id"]); $article_filters = get_article_filters($filters, $line['title'], $line['content'], $line['link'], strtotime($line['updated']), @@ -1091,11 +1196,11 @@ class Pref_Feeds extends Handler_Protected { foreach (array_keys($scores) as $s) { if ($s > 1000) { - db_query($this->link, "UPDATE ttrss_user_entries SET score = '$s', + $this->dbh->query("UPDATE ttrss_user_entries SET score = '$s', marked = true WHERE ref_id IN (" . join(',', $scores[$s]) . ")"); } else { - db_query($this->link, "UPDATE ttrss_user_entries SET score = '$s' WHERE + $this->dbh->query("UPDATE ttrss_user_entries SET score = '$s' WHERE ref_id IN (" . join(',', $scores[$s]) . ")"); } } @@ -1106,9 +1211,9 @@ class Pref_Feeds extends Handler_Protected { } function categorize() { - $ids = split(",", db_escape_string($_REQUEST["ids"])); + $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); - $cat_id = db_escape_string($_REQUEST["cat_id"]); + $cat_id = $this->dbh->escape_string($_REQUEST["cat_id"]); if ($cat_id == 0) { $cat_id_qpart = 'NULL'; @@ -1116,30 +1221,30 @@ class Pref_Feeds extends Handler_Protected { $cat_id_qpart = "'$cat_id'"; } - db_query($this->link, "BEGIN"); + $this->dbh->query("BEGIN"); foreach ($ids as $id) { - db_query($this->link, "UPDATE ttrss_feeds SET cat_id = $cat_id_qpart + $this->dbh->query("UPDATE ttrss_feeds SET cat_id = $cat_id_qpart WHERE id = '$id' AND owner_uid = " . $_SESSION["uid"]); } - db_query($this->link, "COMMIT"); + $this->dbh->query("COMMIT"); } function removeCat() { - $ids = split(",", db_escape_string($_REQUEST["ids"])); + $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); foreach ($ids as $id) { - $this->remove_feed_category($this->link, $id, $_SESSION["uid"]); + $this->remove_feed_category($id, $_SESSION["uid"]); } } function addCat() { - $feed_cat = db_escape_string(trim($_REQUEST["cat"])); + $feed_cat = $this->dbh->escape_string(trim($_REQUEST["cat"])); - add_feed_category($this->link, $feed_cat); + add_feed_category($feed_cat); } function index() { @@ -1147,10 +1252,10 @@ class Pref_Feeds extends Handler_Protected { print "<div dojoType=\"dijit.layout.AccordionContainer\" region=\"center\">"; print "<div id=\"pref-feeds-feeds\" dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Feeds')."\">"; - $result = db_query($this->link, "SELECT COUNT(id) AS num_errors + $result = $this->dbh->query("SELECT COUNT(id) AS num_errors FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]); - $num_errors = db_fetch_result($result, 0, "num_errors"); + $num_errors = $this->dbh->fetch_result($result, 0, "num_errors"); if ($num_errors > 0) { @@ -1165,13 +1270,13 @@ class Pref_Feeds extends Handler_Protected { $interval_qpart = "DATE_SUB(NOW(), INTERVAL 3 MONTH)"; } - $result = db_query($this->link, "SELECT COUNT(*) AS num_inactive FROM ttrss_feeds WHERE + $result = $this->dbh->query("SELECT COUNT(*) AS num_inactive FROM ttrss_feeds WHERE (SELECT MAX(updated) FROM ttrss_entries, ttrss_user_entries WHERE ttrss_entries.id = ref_id AND ttrss_user_entries.feed_id = ttrss_feeds.id) < $interval_qpart AND ttrss_feeds.owner_uid = ".$_SESSION["uid"]); - $num_inactive = db_fetch_result($result, 0, "num_inactive"); + $num_inactive = $this->dbh->fetch_result($result, 0, "num_inactive"); if ($num_inactive > 0) { $inactive_button = "<button dojoType=\"dijit.form.Button\" @@ -1179,7 +1284,7 @@ class Pref_Feeds extends Handler_Protected { __("Inactive feeds") . "</button>"; } - $feed_search = db_escape_string($_REQUEST["search"]); + $feed_search = $this->dbh->escape_string($_REQUEST["search"]); if (array_key_exists("search", $_REQUEST)) { $_SESSION["prefs_feed_search"] = $feed_search; @@ -1218,16 +1323,16 @@ class Pref_Feeds extends Handler_Protected { dojoType=\"dijit.MenuItem\">".__('Reset sort order')."</div>"; print "<div onclick=\"batchSubscribe()\" dojoType=\"dijit.MenuItem\">".__('Batch subscribe')."</div>"; + print "<div dojoType=\"dijit.MenuItem\" onclick=\"removeSelectedFeeds()\">" + .__('Unsubscribe')."</div> "; print "</div></div>"; - if (get_pref($this->link, 'ENABLE_FEED_CATS')) { + if (get_pref('ENABLE_FEED_CATS')) { print "<div dojoType=\"dijit.form.DropDownButton\">". "<span>" . __('Categories')."</span>"; print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">"; print "<div onclick=\"createCategory()\" dojoType=\"dijit.MenuItem\">".__('Add category')."</div>"; - print "<div onclick=\"toggleHiddenFeedCats()\" - dojoType=\"dijit.MenuItem\">".__('(Un)hide empty categories')."</div>"; print "<div onclick=\"resetCatOrder()\" dojoType=\"dijit.MenuItem\">".__('Reset sort order')."</div>"; print "<div onclick=\"removeSelectedCategories()\" @@ -1239,9 +1344,6 @@ class Pref_Feeds extends Handler_Protected { print $error_button; print $inactive_button; - print "<button dojoType=\"dijit.form.Button\" onclick=\"removeSelectedFeeds()\">" - .__('Unsubscribe')."</button dojoType=\"dijit.form.Button\"> "; - if (defined('_ENABLE_FEED_DEBUGGING')) { print "<select id=\"feedActionChooser\" onchange=\"feedActionChange()\"> @@ -1306,9 +1408,7 @@ class Pref_Feeds extends Handler_Protected { print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('OPML')."\">"; - print "<p>" . __("Using OPML you can export and import your feeds, filters, labels and Tiny Tiny RSS settings.") . " "; - - print __("Only main settings profile can be migrated using OPML.") . "</p>"; + print_notice(__("Using OPML you can export and import your feeds, filters, labels and Tiny Tiny RSS settings.") . __("Only main settings profile can be migrated using OPML.")); print "<iframe id=\"upload_iframe\" name=\"upload_iframe\" onload=\"opmlImportComplete(this)\" @@ -1339,11 +1439,10 @@ class Pref_Feeds extends Handler_Protected { print __("Published OPML does not include your Tiny Tiny RSS settings, feeds that require authentication or feeds hidden from Popular feeds.") . "</p>"; - print "<button dojoType=\"dijit.form.Button\" onclick=\"return displayDlg('pubOPMLUrl')\">". + print "<button dojoType=\"dijit.form.Button\" onclick=\"return displayDlg('".__("Public OPML URL")."','pubOPMLUrl')\">". __('Display published OPML URL')."</button> "; - global $pluginhost; - $pluginhost->run_hooks($pluginhost::HOOK_PREFS_TAB_SECTION, + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "hook_prefs_tab_section", "prefFeedsOPML"); print "</div>"; # pane @@ -1352,7 +1451,7 @@ class Pref_Feeds extends Handler_Protected { print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Firefox integration')."\">"; - print "<p>" . __('This Tiny Tiny RSS site can be used as a Firefox Feed Reader by clicking the link below.') . "</p>"; + print_notice(__('This Tiny Tiny RSS site can be used as a Firefox Feed Reader by clicking the link below.')); print "<p>"; @@ -1369,35 +1468,36 @@ class Pref_Feeds extends Handler_Protected { print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Published & shared articles / Generated feeds')."\">"; - print "<h3>" . __("Published articles and generated feeds") . "</h3>"; - - print "<p>".__('Published articles are exported as a public RSS feed and can be subscribed by anyone who knows the URL specified below.')."</p>"; + print_notice(__('Published articles are exported as a public RSS feed and can be subscribed by anyone who knows the URL specified below.')); $rss_url = '-2::' . htmlspecialchars(get_self_url_prefix() . "/public.php?op=rss&id=-2&view-mode=all_articles");; - print "<button dojoType=\"dijit.form.Button\" onclick=\"return displayDlg('generatedFeed', '$rss_url')\">". + print "<p>"; + + print "<button dojoType=\"dijit.form.Button\" onclick=\"return displayDlg('".__("View as RSS")."','generatedFeed', '$rss_url')\">". __('Display URL')."</button> "; print "<button dojoType=\"dijit.form.Button\" onclick=\"return clearFeedAccessKeys()\">". __('Clear all generated URLs')."</button> "; - print "<h3>" . __("Articles shared by URL") . "</h3>"; + print "</p>"; - print "<p>" . __("You can disable all articles shared by unique URLs here.") . "</p>"; + print_warning(__("You can disable all articles shared by unique URLs here.")); + + print "<p>"; print "<button dojoType=\"dijit.form.Button\" onclick=\"return clearArticleAccessKeys()\">". __('Unshare all articles')."</button> "; - global $pluginhost; - $pluginhost->run_hooks($pluginhost::HOOK_PREFS_TAB_SECTION, + print "</p>"; + + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "hook_prefs_tab_section", "prefFeedsPublishedGenerated"); print "</div>"; #pane - global $pluginhost; - - $pluginhost->run_hooks($pluginhost::HOOK_PREFS_TAB, + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB, "hook_prefs_tab", "prefFeeds"); print "</div>"; #container @@ -1408,14 +1508,14 @@ class Pref_Feeds extends Handler_Protected { $cat_id = (int) $cat_id; if ($cat_id > 0) { - $cat_unread = ccache_find($this->link, $cat_id, $_SESSION["uid"], true); + $cat_unread = ccache_find($cat_id, $_SESSION["uid"], true); } else if ($cat_id == 0 || $cat_id == -2) { - $cat_unread = getCategoryUnread($this->link, $cat_id); + $cat_unread = getCategoryUnread($cat_id); } $obj['id'] = 'CAT:' . $cat_id; $obj['items'] = array(); - $obj['name'] = getCategoryTitle($this->link, $cat_id); + $obj['name'] = getCategoryTitle($cat_id); $obj['type'] = 'category'; $obj['unread'] = (int) $cat_unread; $obj['bare_id'] = $cat_id; @@ -1428,10 +1528,10 @@ class Pref_Feeds extends Handler_Protected { $feed_id = (int) $feed_id; if (!$title) - $title = getFeedTitle($this->link, $feed_id, false); + $title = getFeedTitle($feed_id, false); if ($unread === false) - $unread = getFeedUnread($this->link, $feed_id, false); + $unread = getFeedUnread($feed_id, false); $obj['id'] = 'FEED:' . $feed_id; $obj['name'] = $title; @@ -1453,7 +1553,7 @@ class Pref_Feeds extends Handler_Protected { $interval_qpart = "DATE_SUB(NOW(), INTERVAL 3 MONTH)"; } - $result = db_query($this->link, "SELECT ttrss_feeds.title, ttrss_feeds.site_url, + $result = $this->dbh->query("SELECT ttrss_feeds.title, ttrss_feeds.site_url, ttrss_feeds.feed_url, ttrss_feeds.id, MAX(updated) AS last_article FROM ttrss_feeds, ttrss_entries, ttrss_user_entries WHERE (SELECT MAX(updated) FROM ttrss_entries, ttrss_user_entries WHERE @@ -1465,7 +1565,7 @@ class Pref_Feeds extends Handler_Protected { GROUP BY ttrss_feeds.title, ttrss_feeds.id, ttrss_feeds.site_url, ttrss_feeds.feed_url ORDER BY last_article"); - print "<div class=\"dialogNotice\">" . __("These feeds have not been updated with new content for 3 months (oldest first):") . "</div>"; + print "<p" .__("These feeds have not been updated with new content for 3 months (oldest first):") . "</p>"; print "<div dojoType=\"dijit.Toolbar\">"; print "<div dojoType=\"dijit.form.DropDownButton\">". @@ -1484,9 +1584,8 @@ class Pref_Feeds extends Handler_Protected { $lnum = 1; - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { - $class = ($lnum % 2) ? "even" : "odd"; $feed_id = $line["id"]; $this_row_id = "id=\"FUPDD-$feed_id\""; @@ -1507,7 +1606,7 @@ class Pref_Feeds extends Handler_Protected { htmlspecialchars($line["title"])."</a>"; print "</td><td class=\"insensitive\" align='right'>"; - print make_local_datetime($this->link, $line['last_article'], false); + print make_local_datetime($line['last_article'], false); print "</td>"; print "</tr>"; @@ -1531,9 +1630,7 @@ class Pref_Feeds extends Handler_Protected { } function feedsWithErrors() { - print "<div class=\"dialogNotice\">" . __("These feeds have not been updated because of errors:") . "</div>"; - - $result = db_query($this->link, "SELECT id,title,feed_url,last_error,site_url + $result = $this->dbh->query("SELECT id,title,feed_url,last_error,site_url FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]); print "<div dojoType=\"dijit.Toolbar\">"; @@ -1553,9 +1650,8 @@ class Pref_Feeds extends Handler_Protected { $lnum = 1; - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { - $class = ($lnum % 2) ? "even" : "odd"; $feed_id = $line["id"]; $this_row_id = "id=\"FERDD-$feed_id\""; @@ -1607,77 +1703,232 @@ class Pref_Feeds extends Handler_Protected { * @param integer $id The id of the feed to purge. * @return void */ - private function clear_feed_articles($link, $id) { + private function clear_feed_articles($id) { if ($id != 0) { - $result = db_query($link, "DELETE FROM ttrss_user_entries + $result = $this->dbh->query("DELETE FROM ttrss_user_entries WHERE feed_id = '$id' AND marked = false AND owner_uid = " . $_SESSION["uid"]); } else { - $result = db_query($link, "DELETE FROM ttrss_user_entries + $result = $this->dbh->query("DELETE FROM ttrss_user_entries WHERE feed_id IS NULL AND marked = false AND owner_uid = " . $_SESSION["uid"]); } - $result = db_query($link, "DELETE FROM ttrss_entries WHERE + $result = $this->dbh->query("DELETE FROM ttrss_entries WHERE (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0"); - ccache_update($link, $id, $_SESSION['uid']); + ccache_update($id, $_SESSION['uid']); } // function clear_feed_articles - private function remove_feed_category($link, $id, $owner_uid) { + private function remove_feed_category($id, $owner_uid) { - db_query($link, "DELETE FROM ttrss_feed_categories + $this->dbh->query("DELETE FROM ttrss_feed_categories WHERE id = '$id' AND owner_uid = $owner_uid"); - ccache_remove($link, $id, $owner_uid, true); + ccache_remove($id, $owner_uid, true); } - private function remove_feed($link, $id, $owner_uid) { + static function remove_feed($id, $owner_uid) { if ($id > 0) { /* save starred articles in Archived feed */ - db_query($link, "BEGIN"); + db_query("BEGIN"); /* prepare feed if necessary */ - $result = db_query($link, "SELECT id FROM ttrss_archived_feeds - WHERE id = '$id'"); + $result = db_query("SELECT feed_url FROM ttrss_feeds WHERE id = $id + AND owner_uid = $owner_uid"); + + $feed_url = db_escape_string(db_fetch_result($result, 0, "feed_url")); + + $result = db_query("SELECT id FROM ttrss_archived_feeds + WHERE feed_url = '$feed_url' AND owner_uid = $owner_uid"); if (db_num_rows($result) == 0) { - db_query($link, "INSERT INTO ttrss_archived_feeds + $result = db_query("SELECT MAX(id) AS id FROM ttrss_archived_feeds"); + $new_feed_id = (int)db_fetch_result($result, 0, "id") + 1; + + db_query("INSERT INTO ttrss_archived_feeds (id, owner_uid, title, feed_url, site_url) - SELECT id, owner_uid, title, feed_url, site_url from ttrss_feeds - WHERE id = '$id'"); + SELECT $new_feed_id, owner_uid, title, feed_url, site_url from ttrss_feeds + WHERE id = '$id'"); + + $archive_id = $new_feed_id; + } else { + $archive_id = db_fetch_result($result, 0, "id"); } - db_query($link, "UPDATE ttrss_user_entries SET feed_id = NULL, - orig_feed_id = '$id' WHERE feed_id = '$id' AND + db_query("UPDATE ttrss_user_entries SET feed_id = NULL, + orig_feed_id = '$archive_id' WHERE feed_id = '$id' AND marked = true AND owner_uid = $owner_uid"); /* Remove access key for the feed */ - db_query($link, "DELETE FROM ttrss_access_keys WHERE + db_query("DELETE FROM ttrss_access_keys WHERE feed_id = '$id' AND owner_uid = $owner_uid"); /* remove the feed */ - db_query($link, "DELETE FROM ttrss_feeds + db_query("DELETE FROM ttrss_feeds WHERE id = '$id' AND owner_uid = $owner_uid"); - db_query($link, "COMMIT"); + db_query("COMMIT"); if (file_exists(ICONS_DIR . "/$id.ico")) { unlink(ICONS_DIR . "/$id.ico"); } - ccache_remove($link, $id, $owner_uid); + ccache_remove($id, $owner_uid); } else { - label_remove($link, -11-$id, $owner_uid); - ccache_remove($link, -11-$id, $owner_uid); + label_remove(feed_to_label_id($id), $owner_uid); + //ccache_remove($id, $owner_uid); don't think labels are cached + } + } + + function batchSubscribe() { + print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-feeds\">"; + print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"batchaddfeeds\">"; + + print "<table width='100%'><tr><td> + ".__("Add one valid RSS feed per line (no feed detection is done)")." + </td><td align='right'>"; + if (get_pref('ENABLE_FEED_CATS')) { + print __('Place in category:') . " "; + print_feed_cat_select("cat", false, 'dojoType="dijit.form.Select"'); } + print "</td></tr><tr><td colspan='2'>"; + print "<textarea + style='font-size : 12px; width : 100%; height: 200px;' + placeHolder=\"".__("Feeds to subscribe, One per line")."\" + dojoType=\"dijit.form.SimpleTextarea\" required=\"1\" name=\"feeds\"></textarea>"; + + print "</td></tr><tr><td colspan='2'>"; + + print "<div id='feedDlg_loginContainer' style='display : none'> + " . + " <input dojoType=\"dijit.form.TextBox\" name='login'\" + placeHolder=\"".__("Login")."\" + style=\"width : 10em;\"> ". + " <input + placeHolder=\"".__("Password")."\" + dojoType=\"dijit.form.TextBox\" type='password' + style=\"width : 10em;\" name='pass'\">". + "</div>"; + + print "</td></tr><tr><td colspan='2'>"; + + print "<div style=\"clear : both\"> + <input type=\"checkbox\" name=\"need_auth\" dojoType=\"dijit.form.CheckBox\" id=\"feedDlg_loginCheck\" + onclick='checkboxToggleElement(this, \"feedDlg_loginContainer\")'> + <label for=\"feedDlg_loginCheck\">". + __('Feeds require authentication.')."</div>"; + + print "</form>"; + + print "</td></tr></table>"; + + print "<div class=\"dlgButtons\"> + <button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('batchSubDlg').execute()\">".__('Subscribe')."</button> + <button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('batchSubDlg').hide()\">".__('Cancel')."</button> + </div>"; } + function batchAddFeeds() { + $cat_id = $this->dbh->escape_string($_REQUEST['cat']); + $feeds = explode("\n", $_REQUEST['feeds']); + $login = $this->dbh->escape_string($_REQUEST['login']); + $pass = trim($_REQUEST['pass']); + + foreach ($feeds as $feed) { + $feed = $this->dbh->escape_string(trim($feed)); + + if (validate_feed_url($feed)) { + + $this->dbh->query("BEGIN"); + + if ($cat_id == "0" || !$cat_id) { + $cat_qpart = "NULL"; + } else { + $cat_qpart = "'$cat_id'"; + } + + $result = $this->dbh->query( + "SELECT id FROM ttrss_feeds + WHERE feed_url = '$feed' AND owner_uid = ".$_SESSION["uid"]); + + if (strlen(FEED_CRYPT_KEY) > 0) { + require_once "crypt.php"; + $pass = substr(encrypt_string($pass), 0, 250); + $auth_pass_encrypted = 'true'; + } else { + $auth_pass_encrypted = 'false'; + } + + $pass = $this->dbh->escape_string($pass); + + if ($this->dbh->num_rows($result) == 0) { + $result = $this->dbh->query( + "INSERT INTO ttrss_feeds + (owner_uid,feed_url,title,cat_id,auth_login,auth_pass,update_method,auth_pass_encrypted) + VALUES ('".$_SESSION["uid"]."', '$feed', + '[Unknown]', $cat_qpart, '$login', '$pass', 0, $auth_pass_encrypted)"); + } + + $this->dbh->query("COMMIT"); + } + } + } + + function regenOPMLKey() { + $this->update_feed_access_key('OPML:Publish', + false, $_SESSION["uid"]); + + $new_link = Opml::opml_publish_url(); + + print json_encode(array("link" => $new_link)); + } + + function regenFeedKey() { + $feed_id = $this->dbh->escape_string($_REQUEST['id']); + $is_cat = $this->dbh->escape_string($_REQUEST['is_cat']) == "true"; + + $new_key = $this->update_feed_access_key($feed_id, $is_cat); + + print json_encode(array("link" => $new_key)); + } + + + private function update_feed_access_key($feed_id, $is_cat, $owner_uid = false) { + if (!$owner_uid) $owner_uid = $_SESSION["uid"]; + + $sql_is_cat = bool_to_sql_bool($is_cat); + + $result = $this->dbh->query("SELECT access_key FROM ttrss_access_keys + WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat + AND owner_uid = " . $owner_uid); + + if ($this->dbh->num_rows($result) == 1) { + $key = $this->dbh->escape_string(sha1(uniqid(rand(), true))); + + $this->dbh->query("UPDATE ttrss_access_keys SET access_key = '$key' + WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat + AND owner_uid = " . $owner_uid); + + return $key; + + } else { + return get_feed_access_key($feed_id, $is_cat, $owner_uid); + } + } + + // Silent + function clearKeys() { + $this->dbh->query("DELETE FROM ttrss_access_keys WHERE + owner_uid = " . $_SESSION["uid"]); + } + + } ?> diff --git a/classes/pref/filters.php b/classes/pref/filters.php index 74a29c619..bcc7b5aec 100644 --- a/classes/pref/filters.php +++ b/classes/pref/filters.php @@ -3,23 +3,62 @@ class Pref_Filters extends Handler_Protected { function csrf_ignore($method) { $csrf_ignored = array("index", "getfiltertree", "edit", "newfilter", "newrule", - "newaction"); + "newaction", "savefilterorder"); return array_search($method, $csrf_ignored) !== false; } + function filtersortreset() { + $this->dbh->query("UPDATE ttrss_filters2 + SET order_id = 0 WHERE owner_uid = " . $_SESSION["uid"]); + return; + } + + function savefilterorder() { + $data = json_decode($_POST['payload'], true); + + #file_put_contents("/tmp/saveorder.json", $_POST['payload']); + #$data = json_decode(file_get_contents("/tmp/saveorder.json"), true); + + if (!is_array($data['items'])) + $data['items'] = json_decode($data['items'], true); + + $index = 0; + + if (is_array($data) && is_array($data['items'])) { + foreach ($data['items'][0]['items'] as $item) { + $filter_id = (int) str_replace("FILTER:", "", $item['_reference']); + + if ($filter_id > 0) { + + $this->dbh->query("UPDATE ttrss_filters2 SET + order_id = $index WHERE id = '$filter_id' AND + owner_uid = " .$_SESSION["uid"]); + + ++$index; + } + } + } + + return; + } + + function testFilter() { $filter = array(); $filter["enabled"] = true; $filter["match_any_rule"] = sql_bool_to_bool( - checkbox_to_sql_bool(db_escape_string($_REQUEST["match_any_rule"]))); + checkbox_to_sql_bool($this->dbh->escape_string($_REQUEST["match_any_rule"]))); + $filter["inverse"] = sql_bool_to_bool( + checkbox_to_sql_bool($this->dbh->escape_string($_REQUEST["inverse"]))); + $filter["rules"] = array(); - $result = db_query($this->link, "SELECT id,name FROM ttrss_filter_types"); + $result = $this->dbh->query("SELECT id,name FROM ttrss_filter_types"); $filter_types = array(); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $filter_types[$line["id"]] = $line["name"]; } @@ -44,10 +83,8 @@ class Pref_Filters extends Handler_Protected { } } - $feed_title = getFeedTitle($this->link, $feed); - - $qfh_ret = queryFeedHeadlines($this->link, -4, 30, "", false, false, false, - false, "date_entered DESC", 0, $_SESSION["uid"], $filter); + $qfh_ret = queryFeedHeadlines(-4, 30, "", false, false, false, + "date_entered DESC", 0, $_SESSION["uid"], $filter); $result = $qfh_ret[0]; @@ -59,10 +96,10 @@ class Pref_Filters extends Handler_Protected { print "<div class=\"filterTestHolder\">"; print "<table width=\"100%\" cellspacing=\"0\" id=\"prefErrorFeedList\">"; - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $entry_timestamp = strtotime($line["updated"]); - $entry_tags = get_article_tags($this->link, $line["id"], $_SESSION["uid"]); + $entry_tags = get_article_tags($line["id"], $_SESSION["uid"]); $content_preview = truncate_string( strip_tags($line["content_preview"]), 100, '...'); @@ -119,7 +156,7 @@ class Pref_Filters extends Handler_Protected { $filter_search = $_SESSION["prefs_filter_search"]; - $result = db_query($this->link, "SELECT *, + $result = $this->dbh->query("SELECT *, (SELECT action_param FROM ttrss_filters2_actions WHERE filter_id = ttrss_filters2.id ORDER BY id LIMIT 1) AS action_param, (SELECT action_id FROM ttrss_filters2_actions @@ -130,16 +167,16 @@ class Pref_Filters extends Handler_Protected { (SELECT reg_exp FROM ttrss_filters2_rules WHERE filter_id = ttrss_filters2.id ORDER BY id LIMIT 1) AS reg_exp FROM ttrss_filters2 WHERE - owner_uid = ".$_SESSION["uid"]." ORDER BY action_id,reg_exp"); + owner_uid = ".$_SESSION["uid"]." ORDER BY order_id, title"); $action_id = -1; $folder = array(); $folder['items'] = array(); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { - if ($action_id != $line["action_id"]) { + /* if ($action_id != $line["action_id"]) { if (count($folder['items']) > 0) { array_push($root['items'], $folder); } @@ -149,16 +186,16 @@ class Pref_Filters extends Handler_Protected { $folder['name'] = __($line["action_name"]); $folder['items'] = array(); $action_id = $line["action_id"]; - } + } */ $name = $this->getFilterName($line["id"]); $match_ok = false; if ($filter_search) { - $rules_result = db_query($this->link, + $rules_result = $this->dbh->query( "SELECT reg_exp FROM ttrss_filters2_rules WHERE filter_id = ".$line["id"]); - while ($rule_line = db_fetch_assoc($rules_result)) { + while ($rule_line = $this->dbh->fetch_assoc($rules_result)) { if (mb_strpos($rule_line['reg_exp'], $filter_search) !== false) { $match_ok = true; break; @@ -167,13 +204,13 @@ class Pref_Filters extends Handler_Protected { } if ($line['action_id'] == 7) { - $label_result = db_query($this->link, "SELECT fg_color, bg_color - FROM ttrss_labels2 WHERE caption = '".db_escape_string($line['action_param'])."' AND + $label_result = $this->dbh->query("SELECT fg_color, bg_color + FROM ttrss_labels2 WHERE caption = '".$this->dbh->escape_string($line['action_param'])."' AND owner_uid = " . $_SESSION["uid"]); - if (db_num_rows($label_result) > 0) { - $fg_color = db_fetch_result($label_result, 0, "fg_color"); - $bg_color = db_fetch_result($label_result, 0, "bg_color"); + if ($this->dbh->num_rows($label_result) > 0) { + $fg_color = $this->dbh->fetch_result($label_result, 0, "fg_color"); + $bg_color = $this->dbh->fetch_result($label_result, 0, "bg_color"); $name[1] = "<span class=\"labelColorIndicator\" id=\"label-editor-indicator\" style='color : $fg_color; background-color : $bg_color; margin-right : 4px'>α</span>" . $name[1]; } @@ -192,9 +229,11 @@ class Pref_Filters extends Handler_Protected { } } - if (count($folder['items']) > 0) { + /* if (count($folder['items']) > 0) { array_push($root['items'], $folder); - } + } */ + + $root['items'] = $folder['items']; $fl = array(); $fl['identifier'] = 'id'; @@ -207,13 +246,15 @@ class Pref_Filters extends Handler_Protected { function edit() { - $filter_id = db_escape_string($_REQUEST["id"]); + $filter_id = $this->dbh->escape_string($_REQUEST["id"]); - $result = db_query($this->link, + $result = $this->dbh->query( "SELECT * FROM ttrss_filters2 WHERE id = '$filter_id' AND owner_uid = " . $_SESSION["uid"]); - $enabled = sql_bool_to_bool(db_fetch_result($result, 0, "enabled")); - $match_any_rule = sql_bool_to_bool(db_fetch_result($result, 0, "match_any_rule")); + $enabled = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "enabled")); + $match_any_rule = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "match_any_rule")); + $inverse = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "inverse")); + $title = htmlspecialchars($this->dbh->fetch_result($result, 0, "title")); print "<form id=\"filter_edit_form\" onsubmit='return false'>"; @@ -222,6 +263,12 @@ class Pref_Filters extends Handler_Protected { print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"editSave\">"; print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"csrf_token\" value=\"".$_SESSION['csrf_token']."\">"; + print "<div class=\"dlgSec\">".__("Caption")."</div>"; + + print "<input required=\"true\" dojoType=\"dijit.form.ValidationTextBox\" style=\"width : 20em;\" name=\"title\" value=\"$title\">"; + + print "</div>"; + print "<div class=\"dlgSec\">".__("Match")."</div>"; print "<div dojoType=\"dijit.Toolbar\">"; @@ -245,10 +292,10 @@ class Pref_Filters extends Handler_Protected { print "<ul id='filterDlg_Matches'>"; - $rules_result = db_query($this->link, "SELECT * FROM ttrss_filters2_rules + $rules_result = $this->dbh->query("SELECT * FROM ttrss_filters2_rules WHERE filter_id = '$filter_id' ORDER BY reg_exp, id"); - while ($line = db_fetch_assoc($rules_result)) { + while ($line = $this->dbh->fetch_assoc($rules_result)) { if (sql_bool_to_bool($line["cat_filter"])) { $line["feed_id"] = "CAT:" . (int)$line["cat_id"]; } @@ -257,6 +304,7 @@ class Pref_Filters extends Handler_Protected { unset($line["cat_id"]); unset($line["filter_id"]); unset($line["id"]); + if (!sql_bool_to_bool($line["inverse"])) unset($line["inverse"]); $data = htmlspecialchars(json_encode($line)); @@ -292,10 +340,10 @@ class Pref_Filters extends Handler_Protected { print "<ul id='filterDlg_Actions'>"; - $actions_result = db_query($this->link, "SELECT * FROM ttrss_filters2_actions + $actions_result = $this->dbh->query("SELECT * FROM ttrss_filters2_actions WHERE filter_id = '$filter_id' ORDER BY id"); - while ($line = db_fetch_assoc($actions_result)) { + while ($line = $this->dbh->fetch_assoc($actions_result)) { $line["action_param_label"] = $line["action_param"]; unset($line["filter_id"]); @@ -330,6 +378,15 @@ class Pref_Filters extends Handler_Protected { print "<br/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"match_any_rule\" id=\"match_any_rule\" $checked> <label for=\"match_any_rule\">".__('Match any rule')."</label>"; + if ($inverse) { + $checked = "checked=\"1\""; + } else { + $checked = ""; + } + + print "<br/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"inverse\" id=\"inverse\" $checked> + <label for=\"inverse\">".__('Inverse matching')."</label>"; + print "<p/>"; print "<div class=\"dlgButtons\">"; @@ -358,21 +415,22 @@ class Pref_Filters extends Handler_Protected { if (strpos($feed_id, "CAT:") === 0) { $feed_id = (int) substr($feed_id, 4); - $feed = getCategoryTitle($this->link, $feed_id); + $feed = getCategoryTitle($feed_id); } else { $feed_id = (int) $feed_id; if ($rule["feed_id"]) - $feed = getFeedTitle($this->link, (int)$rule["feed_id"]); + $feed = getFeedTitle((int)$rule["feed_id"]); else $feed = __("All feeds"); } - $result = db_query($this->link, "SELECT description FROM ttrss_filter_types + $result = $this->dbh->query("SELECT description FROM ttrss_filter_types WHERE id = ".(int)$rule["filter_type"]); - $match_on = db_fetch_result($result, 0, "description"); + $filter_type = $this->dbh->fetch_result($result, 0, "description"); - return T_sprintf("%s on %s in %s", $rule["reg_exp"], $match_on, $feed); + return T_sprintf("%s on %s in %s %s", strip_tags($rule["reg_exp"]), + $filter_type, $feed, isset($rule["inverse"]) ? __("(inverse)") : ""); } function printRuleName() { @@ -380,10 +438,10 @@ class Pref_Filters extends Handler_Protected { } private function getActionName($action) { - $result = db_query($this->link, "SELECT description FROM + $result = $this->dbh->query("SELECT description FROM ttrss_filter_actions WHERE id = " .(int)$action["action_id"]); - $title = __(db_fetch_result($result, 0, "description")); + $title = __($this->dbh->fetch_result($result, 0, "description")); if ($action["action_id"] == 4 || $action["action_id"] == 6 || $action["action_id"] == 7) @@ -403,12 +461,16 @@ class Pref_Filters extends Handler_Protected { # print_r($_REQUEST); - $filter_id = db_escape_string($_REQUEST["id"]); - $enabled = checkbox_to_sql_bool(db_escape_string($_REQUEST["enabled"])); - $match_any_rule = checkbox_to_sql_bool(db_escape_string($_REQUEST["match_any_rule"])); + $filter_id = $this->dbh->escape_string($_REQUEST["id"]); + $enabled = checkbox_to_sql_bool($this->dbh->escape_string($_REQUEST["enabled"])); + $match_any_rule = checkbox_to_sql_bool($this->dbh->escape_string($_REQUEST["match_any_rule"])); + $inverse = checkbox_to_sql_bool($this->dbh->escape_string($_REQUEST["inverse"])); + $title = $this->dbh->escape_string($_REQUEST["title"]); - $result = db_query($this->link, "UPDATE ttrss_filters2 SET enabled = $enabled, - match_any_rule = $match_any_rule + $result = $this->dbh->query("UPDATE ttrss_filters2 SET enabled = $enabled, + match_any_rule = $match_any_rule, + inverse = $inverse, + title = '$title' WHERE id = '$filter_id' AND owner_uid = ". $_SESSION["uid"]); @@ -418,17 +480,17 @@ class Pref_Filters extends Handler_Protected { function remove() { - $ids = split(",", db_escape_string($_REQUEST["ids"])); + $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); foreach ($ids as $id) { - db_query($this->link, "DELETE FROM ttrss_filters2 WHERE id = '$id' AND owner_uid = ". $_SESSION["uid"]); + $this->dbh->query("DELETE FROM ttrss_filters2 WHERE id = '$id' AND owner_uid = ". $_SESSION["uid"]); } } private function saveRulesAndActions($filter_id) { - db_query($this->link, "DELETE FROM ttrss_filters2_rules WHERE filter_id = '$filter_id'"); - db_query($this->link, "DELETE FROM ttrss_filters2_actions WHERE filter_id = '$filter_id'"); + $this->dbh->query("DELETE FROM ttrss_filters2_rules WHERE filter_id = '$filter_id'"); + $this->dbh->query("DELETE FROM ttrss_filters2_actions WHERE filter_id = '$filter_id'"); if ($filter_id) { /* create rules */ @@ -457,9 +519,11 @@ class Pref_Filters extends Handler_Protected { foreach ($rules as $rule) { if ($rule) { - $reg_exp = strip_tags(db_escape_string(trim($rule["reg_exp"]))); - $filter_type = (int) db_escape_string(trim($rule["filter_type"])); - $feed_id = db_escape_string(trim($rule["feed_id"])); + $reg_exp = strip_tags($this->dbh->escape_string(trim($rule["reg_exp"]))); + $inverse = isset($rule["inverse"]) ? "true" : "false"; + + $filter_type = (int) $this->dbh->escape_string(trim($rule["filter_type"])); + $feed_id = $this->dbh->escape_string(trim($rule["feed_id"])); if (strpos($feed_id, "CAT:") === 0) { @@ -477,19 +541,19 @@ class Pref_Filters extends Handler_Protected { } $query = "INSERT INTO ttrss_filters2_rules - (filter_id, reg_exp,filter_type,feed_id,cat_id,cat_filter) VALUES - ('$filter_id', '$reg_exp', '$filter_type', $feed_id, $cat_id, $cat_filter)"; + (filter_id, reg_exp,filter_type,feed_id,cat_id,cat_filter,inverse) VALUES + ('$filter_id', '$reg_exp', '$filter_type', $feed_id, $cat_id, $cat_filter, $inverse)"; - db_query($this->link, $query); + $this->dbh->query($query); } } foreach ($actions as $action) { if ($action) { - $action_id = (int) db_escape_string($action["action_id"]); - $action_param = db_escape_string($action["action_param"]); - $action_param_label = db_escape_string($action["action_param_label"]); + $action_id = (int) $this->dbh->escape_string($action["action_id"]); + $action_param = $this->dbh->escape_string($action["action_param"]); + $action_param_label = $this->dbh->escape_string($action["action_param_label"]); if ($action_id == 7) { $action_param = $action_param_label; @@ -503,7 +567,7 @@ class Pref_Filters extends Handler_Protected { (filter_id, action_id, action_param) VALUES ('$filter_id', '$action_id', '$action_param')"; - db_query($this->link, $query); + $this->dbh->query($query); } } } @@ -520,34 +584,35 @@ class Pref_Filters extends Handler_Protected { $enabled = checkbox_to_sql_bool($_REQUEST["enabled"]); $match_any_rule = checkbox_to_sql_bool($_REQUEST["match_any_rule"]); + $title = $this->dbh->escape_string($_REQUEST["title"]); - db_query($this->link, "BEGIN"); + $this->dbh->query("BEGIN"); /* create base filter */ - $result = db_query($this->link, "INSERT INTO ttrss_filters2 - (owner_uid, match_any_rule, enabled) VALUES - (".$_SESSION["uid"].",$match_any_rule,$enabled)"); + $result = $this->dbh->query("INSERT INTO ttrss_filters2 + (owner_uid, match_any_rule, enabled, title) VALUES + (".$_SESSION["uid"].",$match_any_rule,$enabled, '$title')"); - $result = db_query($this->link, "SELECT MAX(id) AS id FROM ttrss_filters2 + $result = $this->dbh->query("SELECT MAX(id) AS id FROM ttrss_filters2 WHERE owner_uid = ".$_SESSION["uid"]); - $filter_id = db_fetch_result($result, 0, "id"); + $filter_id = $this->dbh->fetch_result($result, 0, "id"); $this->saveRulesAndActions($filter_id); - db_query($this->link, "COMMIT"); + $this->dbh->query("COMMIT"); } function index() { - $sort = db_escape_string($_REQUEST["sort"]); + $sort = $this->dbh->escape_string($_REQUEST["sort"]); if (!$sort || $sort == "undefined") { $sort = "reg_exp"; } - $filter_search = db_escape_string($_REQUEST["search"]); + $filter_search = $this->dbh->escape_string($_REQUEST["search"]); if (array_key_exists("search", $_REQUEST)) { $_SESSION["prefs_filter_search"] = $filter_search; @@ -559,7 +624,7 @@ class Pref_Filters extends Handler_Protected { print "<div id=\"pref-filter-header\" dojoType=\"dijit.layout.ContentPane\" region=\"top\">"; print "<div id=\"pref-filter-toolbar\" dojoType=\"dijit.Toolbar\">"; - $filter_search = db_escape_string($_REQUEST["search"]); + $filter_search = $this->dbh->escape_string($_REQUEST["search"]); if (array_key_exists("search", $_REQUEST)) { $_SESSION["prefs_filter_search"] = $filter_search; @@ -592,6 +657,10 @@ class Pref_Filters extends Handler_Protected { print "<button dojoType=\"dijit.form.Button\" onclick=\"return editSelectedFilter()\">". __('Edit')."</button> "; + print "<button dojoType=\"dijit.form.Button\" onclick=\"return resetFilterOrder()\">". + __('Reset sort order')."</button> "; + + print "<button dojoType=\"dijit.form.Button\" onclick=\"return removeSelectedFilters()\">". __('Remove')."</button> "; @@ -608,14 +677,16 @@ class Pref_Filters extends Handler_Protected { <img src='images/indicator_tiny.gif'>". __("Loading, please wait...")."</div>"; - print "<div dojoType=\"dojo.data.ItemFileWriteStore\" jsId=\"filterStore\" + print "<div dojoType=\"fox.PrefFilterStore\" jsId=\"filterStore\" url=\"backend.php?op=pref-filters&method=getfiltertree\"> </div> <div dojoType=\"lib.CheckBoxStoreModel\" jsId=\"filterModel\" store=\"filterStore\" - query=\"{id:'root'}\" rootId=\"root\" rootLabel=\"Feeds\" + query=\"{id:'root'}\" rootId=\"root\" rootLabel=\"Filters\" childrenAttrs=\"items\" checkboxStrict=\"false\" checkboxAll=\"false\"> </div> <div dojoType=\"fox.PrefFilterTree\" id=\"filterTree\" + dndController=\"dijit.tree.dndSource\" + betweenThreshold=\"5\" model=\"filterModel\" openOnClick=\"true\"> <script type=\"dojo/method\" event=\"onLoad\" args=\"item\"> Element.hide(\"filterlistLoading\"); @@ -633,8 +704,7 @@ class Pref_Filters extends Handler_Protected { print "</div>"; #pane - global $pluginhost; - $pluginhost->run_hooks($pluginhost::HOOK_PREFS_TAB, + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB, "hook_prefs_tab", "prefFilters"); print "</div>"; #container @@ -649,6 +719,10 @@ class Pref_Filters extends Handler_Protected { print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"add\">"; print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"csrf_token\" value=\"".$_SESSION['csrf_token']."\">"; + print "<div class=\"dlgSec\">".__("Caption")."</div>"; + + print "<input required=\"true\" dojoType=\"dijit.form.ValidationTextBox\" style=\"width : 20em;\" name=\"title\" value=\"\">"; + print "<div class=\"dlgSec\">".__("Match")."</div>"; print "<div dojoType=\"dijit.Toolbar\">"; @@ -710,10 +784,8 @@ class Pref_Filters extends Handler_Protected { print "<br/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"match_any_rule\" id=\"match_any_rule\"> <label for=\"match_any_rule\">".__('Match any rule')."</label>"; - print "<p/>"; - -/* print "<input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"inverse\" id=\"inverse\"> - <label for=\"inverse\">".__('Inverse match')."</label><hr/>"; */ + print "<br/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"inverse\" id=\"inverse\"> + <label for=\"inverse\">".__('Inverse matching')."</label>"; // print "</div>"; @@ -739,10 +811,12 @@ class Pref_Filters extends Handler_Protected { $reg_exp = htmlspecialchars($rule["reg_exp"]); $filter_type = $rule["filter_type"]; $feed_id = $rule["feed_id"]; + $inverse_checked = isset($rule["inverse"]) ? "checked" : ""; } else { $reg_exp = ""; $filter_type = 1; $feed_id = 0; + $inverse_checked = ""; } if (strpos($feed_id, "CAT:") === 0) { @@ -755,12 +829,12 @@ class Pref_Filters extends Handler_Protected { print "<form name='filter_new_rule_form' id='filter_new_rule_form'>"; - $result = db_query($this->link, "SELECT id,description + $result = $this->dbh->query("SELECT id,description FROM ttrss_filter_types WHERE id != 5 ORDER BY description"); $filter_types = array(); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $filter_types[$line["id"]] = __($line["description"]); } @@ -773,6 +847,11 @@ class Pref_Filters extends Handler_Protected { style=\"font-size : 16px; width : 20em;\" name=\"reg_exp\" value=\"$reg_exp\"/>"; + print "<hr/>"; + print "<input id=\"filterDlg_inverse\" dojoType=\"dijit.form.CheckBox\" + name=\"inverse\" $inverse_checked/>"; + print "<label for=\"filterDlg_inverse\">".__("Inverse regular expression matching")."</label>"; + print "<hr/>" . __("on field") . " "; print_select_hash("filter_type", $filter_type, $filter_types, 'dojoType="dijit.form.Select"'); @@ -782,7 +861,7 @@ class Pref_Filters extends Handler_Protected { print __("in") . " "; print "<span id='filterDlg_feeds'>"; - print_feed_select($this->link, "feed_id", + print_feed_select("feed_id", $cat_filter ? "CAT:$feed_id" : $feed_id, 'dojoType="dijit.form.FilteringSelect"'); print "</span>"; @@ -806,7 +885,7 @@ class Pref_Filters extends Handler_Protected { $action = json_decode($_REQUEST["action"], true); if ($action) { - $action_param = db_escape_string($action["action_param"]); + $action_param = $this->dbh->escape_string($action["action_param"]); $action_id = (int)$action["action_id"]; } else { $action_param = ""; @@ -822,10 +901,10 @@ class Pref_Filters extends Handler_Protected { print "<select name=\"action_id\" dojoType=\"dijit.form.Select\" onchange=\"filterDlgCheckAction(this)\">"; - $result = db_query($this->link, "SELECT id,description FROM ttrss_filter_actions + $result = $this->dbh->query("SELECT id,description FROM ttrss_filter_actions ORDER BY name"); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $is_selected = ($line["id"] == $action_id) ? "selected='1'" : ""; printf("<option $is_selected value='%d'>%s</option>", $line["id"], __($line["description"])); } @@ -846,7 +925,7 @@ class Pref_Filters extends Handler_Protected { id=\"filterDlg_actionParam\" style=\"$param_hidden\" name=\"action_param\" value=\"$action_param\">"; - print_label_select($this->link, "action_param_label", $action_param, + print_label_select("action_param_label", $action_param, "id=\"filterDlg_actionParamLabel\" style=\"$label_param_hidden\" dojoType=\"dijit.form.Select\""); @@ -870,66 +949,57 @@ class Pref_Filters extends Handler_Protected { } private function getFilterName($id) { - $result = db_query($this->link, - "SELECT * FROM ttrss_filters2_rules WHERE filter_id = '$id' ORDER BY id - LIMIT 3"); - $titles = array(); - $count = 0; + $result = $this->dbh->query( + "SELECT title,COUNT(DISTINCT r.id) AS num_rules,COUNT(DISTINCT a.id) AS num_actions + FROM ttrss_filters2 AS f LEFT JOIN ttrss_filters2_rules AS r + ON (r.filter_id = f.id) + LEFT JOIN ttrss_filters2_actions AS a + ON (a.filter_id = f.id) WHERE f.id = '$id' GROUP BY f.title"); - while ($line = db_fetch_assoc($result)) { + $title = $this->dbh->fetch_result($result, 0, "title"); + $num_rules = $this->dbh->fetch_result($result, 0, "num_rules"); + $num_actions = $this->dbh->fetch_result($result, 0, "num_actions"); - if (sql_bool_to_bool($line["cat_filter"])) { - unset($line["cat_filter"]); - $line["feed_id"] = "CAT:" . (int)$line["cat_id"]; - unset($line["cat_id"]); - } + if (!$title) $title = __("[No caption]"); - if ($count < 2) { - array_push($titles, $this->getRuleName($line)); - } else { - array_push($titles, "..."); - break; - } - ++$count; - } + $title = sprintf(_ngettext("%s (%d rule)", "%s (%d rules)", $num_rules), $title, $num_rules); - $result = db_query($this->link, - "SELECT * FROM ttrss_filters2_actions WHERE filter_id = '$id' ORDER BY id LIMIT 3"); + $result = $this->dbh->query( + "SELECT * FROM ttrss_filters2_actions WHERE filter_id = '$id' ORDER BY id LIMIT 1"); - $actions = array(); - $count = 0; + $actions = ""; - while ($line = db_fetch_assoc($result)) { - if ($count < 2) { - array_push($actions, $this->getActionName($line)); - } else { - array_push($actions, "..."); - break; - } - ++$count; + if ($this->dbh->num_rows($result) > 0) { + $line = $this->dbh->fetch_assoc($result); + $actions = $this->getActionName($line); + + $num_actions -= 1; } - return array(join(", ", $titles), join(", ", $actions)); + if ($num_actions > 0) + $actions = sprintf(_ngettext("%s (+%d action)", "%s (+%d actions)", $num_actions), $actions, $num_actions); + + return array($title, $actions); } function join() { - $ids = explode(",", db_escape_string($_REQUEST["ids"])); + $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); if (count($ids) > 1) { $base_id = array_shift($ids); $ids_str = join(",", $ids); - db_query($this->link, "BEGIN"); - db_query($this->link, "UPDATE ttrss_filters2_rules + $this->dbh->query("BEGIN"); + $this->dbh->query("UPDATE ttrss_filters2_rules SET filter_id = '$base_id' WHERE filter_id IN ($ids_str)"); - db_query($this->link, "UPDATE ttrss_filters2_actions + $this->dbh->query("UPDATE ttrss_filters2_actions SET filter_id = '$base_id' WHERE filter_id IN ($ids_str)"); - db_query($this->link, "DELETE FROM ttrss_filters2 WHERE id IN ($ids_str)"); - db_query($this->link, "UPDATE ttrss_filters2 SET match_any_rule = true WHERE id = '$base_id'"); + $this->dbh->query("DELETE FROM ttrss_filters2 WHERE id IN ($ids_str)"); + $this->dbh->query("UPDATE ttrss_filters2 SET match_any_rule = true WHERE id = '$base_id'"); - db_query($this->link, "COMMIT"); + $this->dbh->query("COMMIT"); $this->optimizeFilter($base_id); @@ -937,14 +1007,14 @@ class Pref_Filters extends Handler_Protected { } private function optimizeFilter($id) { - db_query($this->link, "BEGIN"); - $result = db_query($this->link, "SELECT * FROM ttrss_filters2_actions + $this->dbh->query("BEGIN"); + $result = $this->dbh->query("SELECT * FROM ttrss_filters2_actions WHERE filter_id = '$id'"); $tmp = array(); $dupe_ids = array(); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $id = $line["id"]; unset($line["id"]); @@ -957,17 +1027,17 @@ class Pref_Filters extends Handler_Protected { if (count($dupe_ids) > 0) { $ids_str = join(",", $dupe_ids); - db_query($this->link, "DELETE FROM ttrss_filters2_actions + $this->dbh->query("DELETE FROM ttrss_filters2_actions WHERE id IN ($ids_str)"); } - $result = db_query($this->link, "SELECT * FROM ttrss_filters2_rules + $result = $this->dbh->query("SELECT * FROM ttrss_filters2_rules WHERE filter_id = '$id'"); $tmp = array(); $dupe_ids = array(); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $id = $line["id"]; unset($line["id"]); @@ -980,11 +1050,11 @@ class Pref_Filters extends Handler_Protected { if (count($dupe_ids) > 0) { $ids_str = join(",", $dupe_ids); - db_query($this->link, "DELETE FROM ttrss_filters2_rules + $this->dbh->query("DELETE FROM ttrss_filters2_rules WHERE id IN ($ids_str)"); } - db_query($this->link, "COMMIT"); + $this->dbh->query("COMMIT"); } } ?> diff --git a/classes/pref/labels.php b/classes/pref/labels.php index e63a0cfc2..2ad152c2d 100644 --- a/classes/pref/labels.php +++ b/classes/pref/labels.php @@ -8,12 +8,12 @@ class Pref_Labels extends Handler_Protected { } function edit() { - $label_id = db_escape_string($_REQUEST['id']); + $label_id = $this->dbh->escape_string($_REQUEST['id']); - $result = db_query($this->link, "SELECT * FROM ttrss_labels2 WHERE + $result = $this->dbh->query("SELECT * FROM ttrss_labels2 WHERE id = '$label_id' AND owner_uid = " . $_SESSION["uid"]); - $line = db_fetch_assoc($result); + $line = $this->dbh->fetch_assoc($result); print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"id\" value=\"$label_id\">"; print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-labels\">"; @@ -90,12 +90,12 @@ class Pref_Labels extends Handler_Protected { $root['name'] = __('Labels'); $root['items'] = array(); - $result = db_query($this->link, "SELECT * + $result = $this->dbh->query("SELECT * FROM ttrss_labels2 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY caption"); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $label = array(); $label['id'] = 'LABEL:' . $line['id']; $label['bare_id'] = $line['id']; @@ -118,29 +118,29 @@ class Pref_Labels extends Handler_Protected { } function colorset() { - $kind = db_escape_string($_REQUEST["kind"]); - $ids = split(',', db_escape_string($_REQUEST["ids"])); - $color = db_escape_string($_REQUEST["color"]); - $fg = db_escape_string($_REQUEST["fg"]); - $bg = db_escape_string($_REQUEST["bg"]); + $kind = $this->dbh->escape_string($_REQUEST["kind"]); + $ids = explode(',', $this->dbh->escape_string($_REQUEST["ids"])); + $color = $this->dbh->escape_string($_REQUEST["color"]); + $fg = $this->dbh->escape_string($_REQUEST["fg"]); + $bg = $this->dbh->escape_string($_REQUEST["bg"]); foreach ($ids as $id) { if ($kind == "fg" || $kind == "bg") { - db_query($this->link, "UPDATE ttrss_labels2 SET + $this->dbh->query("UPDATE ttrss_labels2 SET ${kind}_color = '$color' WHERE id = '$id' AND owner_uid = " . $_SESSION["uid"]); } else { - db_query($this->link, "UPDATE ttrss_labels2 SET + $this->dbh->query("UPDATE ttrss_labels2 SET fg_color = '$fg', bg_color = '$bg' WHERE id = '$id' AND owner_uid = " . $_SESSION["uid"]); } - $caption = db_escape_string(label_find_caption($this->link, $id, $_SESSION["uid"])); + $caption = $this->dbh->escape_string(label_find_caption($id, $_SESSION["uid"])); /* Remove cached data */ - db_query($this->link, "UPDATE ttrss_user_entries SET label_cache = '' + $this->dbh->query("UPDATE ttrss_user_entries SET label_cache = '' WHERE label_cache LIKE '%$caption%' AND owner_uid = " . $_SESSION["uid"]); } @@ -149,18 +149,18 @@ class Pref_Labels extends Handler_Protected { } function colorreset() { - $ids = split(',', db_escape_string($_REQUEST["ids"])); + $ids = explode(',', $this->dbh->escape_string($_REQUEST["ids"])); foreach ($ids as $id) { - db_query($this->link, "UPDATE ttrss_labels2 SET + $this->dbh->query("UPDATE ttrss_labels2 SET fg_color = '', bg_color = '' WHERE id = '$id' AND owner_uid = " . $_SESSION["uid"]); - $caption = db_escape_string(label_find_caption($this->link, $id, $_SESSION["uid"])); + $caption = $this->dbh->escape_string(label_find_caption($id, $_SESSION["uid"])); /* Remove cached data */ - db_query($this->link, "UPDATE ttrss_user_entries SET label_cache = '' + $this->dbh->query("UPDATE ttrss_user_entries SET label_cache = '' WHERE label_cache LIKE '%$caption%' AND owner_uid = " . $_SESSION["uid"]); } @@ -168,31 +168,31 @@ class Pref_Labels extends Handler_Protected { function save() { - $id = db_escape_string($_REQUEST["id"]); - $caption = db_escape_string(trim($_REQUEST["caption"])); + $id = $this->dbh->escape_string($_REQUEST["id"]); + $caption = $this->dbh->escape_string(trim($_REQUEST["caption"])); - db_query($this->link, "BEGIN"); + $this->dbh->query("BEGIN"); - $result = db_query($this->link, "SELECT caption FROM ttrss_labels2 + $result = $this->dbh->query("SELECT caption FROM ttrss_labels2 WHERE id = '$id' AND owner_uid = ". $_SESSION["uid"]); - if (db_num_rows($result) != 0) { - $old_caption = db_fetch_result($result, 0, "caption"); + if ($this->dbh->num_rows($result) != 0) { + $old_caption = $this->dbh->fetch_result($result, 0, "caption"); - $result = db_query($this->link, "SELECT id FROM ttrss_labels2 + $result = $this->dbh->query("SELECT id FROM ttrss_labels2 WHERE caption = '$caption' AND owner_uid = ". $_SESSION["uid"]); - if (db_num_rows($result) == 0) { + if ($this->dbh->num_rows($result) == 0) { if ($caption) { - $result = db_query($this->link, "UPDATE ttrss_labels2 SET + $result = $this->dbh->query("UPDATE ttrss_labels2 SET caption = '$caption' WHERE id = '$id' AND owner_uid = " . $_SESSION["uid"]); /* Update filters that reference label being renamed */ - $old_caption = db_escape_string($old_caption); + $old_caption = $this->dbh->escape_string($old_caption); - db_query($this->link, "UPDATE ttrss_filters2_actions SET + $this->dbh->query("UPDATE ttrss_filters2_actions SET action_param = '$caption' WHERE action_param = '$old_caption' AND action_id = 7 AND filter_id IN (SELECT id FROM ttrss_filters2 WHERE owner_uid = ".$_SESSION["uid"].")"); @@ -206,28 +206,28 @@ class Pref_Labels extends Handler_Protected { } } - db_query($this->link, "COMMIT"); + $this->dbh->query("COMMIT"); return; } function remove() { - $ids = split(",", db_escape_string($_REQUEST["ids"])); + $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); foreach ($ids as $id) { - label_remove($this->link, $id, $_SESSION["uid"]); + label_remove($id, $_SESSION["uid"]); } } function add() { - $caption = db_escape_string($_REQUEST["caption"]); - $output = db_escape_string($_REQUEST["output"]); + $caption = $this->dbh->escape_string($_REQUEST["caption"]); + $output = $this->dbh->escape_string($_REQUEST["output"]); if ($caption) { - if (label_create($this->link, $caption)) { + if (label_create($caption)) { if (!$output) { print T_sprintf("Created label <b>%s</b>", htmlspecialchars($caption)); } @@ -238,7 +238,7 @@ class Pref_Labels extends Handler_Protected { print "<rpc-reply><payload>"; - print_label_select($this->link, "select_label", + print_label_select("select_label", $caption, ""); print "</payload></rpc-reply>"; @@ -250,13 +250,13 @@ class Pref_Labels extends Handler_Protected { function index() { - $sort = db_escape_string($_REQUEST["sort"]); + $sort = $this->dbh->escape_string($_REQUEST["sort"]); if (!$sort || $sort == "undefined") { $sort = "caption"; } - $label_search = db_escape_string($_REQUEST["search"]); + $label_search = $this->dbh->escape_string($_REQUEST["search"]); if (array_key_exists("search", $_REQUEST)) { $_SESSION["prefs_label_search"] = $label_search; @@ -319,8 +319,7 @@ class Pref_Labels extends Handler_Protected { print "</div>"; #pane - global $pluginhost; - $pluginhost->run_hooks($pluginhost::HOOK_PREFS_TAB, + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB, "hook_prefs_tab", "prefLabels"); print "</div>"; #container diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index 810b1e164..425d4b0ac 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -1,12 +1,64 @@ <?php + class Pref_Prefs extends Handler_Protected { + private $pref_help = array(); + private $pref_sections = array(); + function csrf_ignore($method) { - $csrf_ignored = array("index", "updateself"); + $csrf_ignored = array("index", "updateself", "customizecss", "editprefprofiles"); return array_search($method, $csrf_ignored) !== false; } + function __construct($args) { + parent::__construct($args); + + $this->pref_sections = array( + 1 => __('General'), + 2 => __('Interface'), + 3 => __('Advanced'), + 4 => __('Digest') + ); + + $this->pref_help = array( + "ALLOW_DUPLICATE_POSTS" => array(__("Allow duplicate articles"), ""), + "AUTO_ASSIGN_LABELS" => array(__("Assign articles to labels automatically"), ""), + "BLACKLISTED_TAGS" => array(__("Blacklisted tags"), __("When auto-detecting tags in articles these tags will not be applied (comma-separated list).")), + "CDM_AUTO_CATCHUP" => array(__("Automatically mark articles as read"), __("This option enables marking articles as read automatically while you scroll article list.")), + "CDM_EXPANDED" => array(__("Automatically expand articles in combined mode"), ""), + "COMBINED_DISPLAY_MODE" => array(__("Combined feed display"), __("Display expanded list of feed articles, instead of separate displays for headlines and article content")), + "CONFIRM_FEED_CATCHUP" => array(__("Confirm marking feed as read"), ""), + "DEFAULT_ARTICLE_LIMIT" => array(__("Amount of articles to display at once"), ""), + "DEFAULT_UPDATE_INTERVAL" => array(__("Default feed update interval"), __("Shortest interval at which a feed will be checked for updates regardless of update method")), + "DIGEST_CATCHUP" => array(__("Mark articles in e-mail digest as read"), ""), + "DIGEST_ENABLE" => array(__("Enable e-mail digest"), __("This option enables sending daily digest of new (and unread) headlines on your configured e-mail address")), + "DIGEST_PREFERRED_TIME" => array(__("Try to send digests around specified time"), __("Uses UTC timezone")), + "ENABLE_API_ACCESS" => array(__("Enable API access"), __("Allows external clients to access this account through the API")), + "ENABLE_FEED_CATS" => array(__("Enable feed categories"), ""), + "FEEDS_SORT_BY_UNREAD" => array(__("Sort feeds by unread articles count"), ""), + "FRESH_ARTICLE_MAX_AGE" => array(__("Maximum age of fresh articles (in hours)"), ""), + "HIDE_READ_FEEDS" => array(__("Hide feeds with no unread articles"), ""), + "HIDE_READ_SHOWS_SPECIAL" => array(__("Show special feeds when hiding read feeds"), ""), + "LONG_DATE_FORMAT" => array(__("Long date format"), ""), + "ON_CATCHUP_SHOW_NEXT_FEED" => array(__("On catchup show next feed"), __("Automatically open next feed with unread articles after marking one as read")), + "PURGE_OLD_DAYS" => array(__("Purge articles after this number of days (0 - disables)"), ""), + "PURGE_UNREAD_ARTICLES" => array(__("Purge unread articles"), ""), + "REVERSE_HEADLINES" => array(__("Reverse headline order (oldest first)"), ""), + "SHORT_DATE_FORMAT" => array(__("Short date format"), ""), + "SHOW_CONTENT_PREVIEW" => array(__("Show content preview in headlines list"), ""), + "SORT_HEADLINES_BY_FEED_DATE" => array(__("Sort headlines by feed date"), __("Use feed-specified date to sort headlines instead of local import date.")), + "SSL_CERT_SERIAL" => array(__("Login with an SSL certificate"), __("Click to register your SSL client certificate with tt-rss")), + "STRIP_IMAGES" => array(__("Do not embed images in articles"), ""), + "STRIP_UNSAFE_TAGS" => array(__("Strip unsafe tags from articles"), __("Strip all but most common HTML tags when reading articles.")), + "USER_STYLESHEET" => array(__("Customize stylesheet"), __("Customize CSS stylesheet to your liking")), + "USER_TIMEZONE" => array(__("Time zone"), ""), + "VFEED_GROUP_BY_FEED" => array(__("Group headlines in virtual feeds"), __("Special feeds, labels, and categories are grouped by originating feeds")), + "USER_LANGUAGE" => array(__("Language")), + "USER_CSS_THEME" => array(__("Theme"), __("Select one of the available CSS themes")) + ); + } + function changepassword() { $old_pw = $_POST["old_password"]; @@ -28,8 +80,7 @@ class Pref_Prefs extends Handler_Protected { return; } - global $pluginhost; - $authenticator = $pluginhost->get_plugin($_SESSION["auth_module"]); + $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); if (method_exists($authenticator, "change_password")) { print $authenticator->change_password($_SESSION["uid"], $old_pw, $new_pw); @@ -39,39 +90,53 @@ class Pref_Prefs extends Handler_Protected { } function saveconfig() { + $boolean_prefs = explode(",", $_POST["boolean_prefs"]); - $_SESSION["prefs_cache"] = false; + foreach ($boolean_prefs as $pref) { + if (!isset($_POST[$pref])) $_POST[$pref] = 'false'; + } + + $need_reload = false; foreach (array_keys($_POST) as $pref_name) { - $pref_name = db_escape_string($pref_name); - $value = db_escape_string($_POST[$pref_name]); + $pref_name = $this->dbh->escape_string($pref_name); + $value = $this->dbh->escape_string($_POST[$pref_name]); if ($pref_name == 'DIGEST_PREFERRED_TIME') { - if (get_pref($this->link, 'DIGEST_PREFERRED_TIME') != $value) { + if (get_pref('DIGEST_PREFERRED_TIME') != $value) { - db_query($this->link, "UPDATE ttrss_users SET + $this->dbh->query("UPDATE ttrss_users SET last_digest_sent = NULL WHERE id = " . $_SESSION['uid']); } } - set_pref($this->link, $pref_name, $value); + if ($pref_name == "USER_LANGUAGE") { + if ($_SESSION["language"] != $value) { + $need_reload = true; + } + } + set_pref($pref_name, $value); } - print __("The configuration was saved."); + if ($need_reload) { + print "PREFS_NEED_RELOAD"; + } else { + print __("The configuration was saved."); + } } function getHelp() { - $pref_name = db_escape_string($_REQUEST["pn"]); + $pref_name = $this->dbh->escape_string($_REQUEST["pn"]); - $result = db_query($this->link, "SELECT help_text FROM ttrss_prefs + $result = $this->dbh->query("SELECT help_text FROM ttrss_prefs WHERE pref_name = '$pref_name'"); - if (db_num_rows($result) > 0) { - $help_text = db_fetch_result($result, 0, "help_text"); + if ($this->dbh->num_rows($result) > 0) { + $help_text = $this->dbh->fetch_result($result, 0, "help_text"); print $help_text; } else { printf(__("Unknown option: %s"), $pref_name); @@ -80,12 +145,12 @@ class Pref_Prefs extends Handler_Protected { function changeemail() { - $email = db_escape_string($_POST["email"]); - $full_name = db_escape_string($_POST["full_name"]); + $email = $this->dbh->escape_string($_POST["email"]); + $full_name = $this->dbh->escape_string($_POST["full_name"]); $active_uid = $_SESSION["uid"]; - db_query($this->link, "UPDATE ttrss_users SET email = '$email', + $this->dbh->query("UPDATE ttrss_users SET email = '$email', full_name = '$full_name' WHERE id = '$active_uid'"); print __("Your personal data has been saved."); @@ -103,20 +168,22 @@ class Pref_Prefs extends Handler_Protected { $profile_qpart = "profile IS NULL"; } - db_query($this->link, "DELETE FROM ttrss_user_prefs + $this->dbh->query("DELETE FROM ttrss_user_prefs WHERE $profile_qpart AND owner_uid = ".$_SESSION["uid"]); - initialize_user_prefs($this->link, $_SESSION["uid"], $_SESSION["profile"]); + initialize_user_prefs($_SESSION["uid"], $_SESSION["profile"]); - print "PREFS_THEME_CHANGED"; + echo __("Your preferences are now set to default values."); } function index() { global $access_level_names; - $prefs_blacklist = array("HIDE_READ_FEEDS", "FEEDS_SORT_BY_UNREAD", - "STRIP_UNSAFE_TAGS"); + $prefs_blacklist = array("STRIP_UNSAFE_TAGS", "REVERSE_HEADLINES", + "SORT_HEADLINES_BY_FEED_DATE", "DEFAULT_ARTICLE_LIMIT"); + + /* "FEEDS_SORT_BY_UNREAD", "HIDE_READ_FEEDS", "REVERSE_HEADLINES" */ $profile_blacklist = array("ALLOW_DUPLICATE_POSTS", "PURGE_OLD_DAYS", "PURGE_UNREAD_ARTICLES", "DIGEST_ENABLE", "DIGEST_CATCHUP", @@ -150,13 +217,13 @@ class Pref_Prefs extends Handler_Protected { print "<h2>" . __("Personal data") . "</h2>"; - $result = db_query($this->link, "SELECT email,full_name,otp_enabled, + $result = $this->dbh->query("SELECT email,full_name,otp_enabled, access_level FROM ttrss_users WHERE id = ".$_SESSION["uid"]); - $email = htmlspecialchars(db_fetch_result($result, 0, "email")); - $full_name = htmlspecialchars(db_fetch_result($result, 0, "full_name")); - $otp_enabled = sql_bool_to_bool(db_fetch_result($result, 0, "otp_enabled")); + $email = htmlspecialchars($this->dbh->fetch_result($result, 0, "email")); + $full_name = htmlspecialchars($this->dbh->fetch_result($result, 0, "full_name")); + $otp_enabled = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "otp_enabled")); print "<tr><td width=\"40%\">".__('Full name')."</td>"; print "<td class=\"prefValue\"><input dojoType=\"dijit.form.ValidationTextBox\" name=\"full_name\" required=\"1\" @@ -167,7 +234,7 @@ class Pref_Prefs extends Handler_Protected { if (!SINGLE_USER_MODE && !$_SESSION["hide_hello"]) { - $access_level = db_fetch_result($result, 0, "access_level"); + $access_level = $this->dbh->fetch_result($result, 0, "access_level"); print "<tr><td width=\"40%\">".__('Access level')."</td>"; print "<td>" . $access_level_names[$access_level] . "</td></tr>"; } @@ -183,10 +250,7 @@ class Pref_Prefs extends Handler_Protected { print "</form>"; if ($_SESSION["auth_module"]) { - global $pluginhost; - - $authenticator = $pluginhost->get_plugin($_SESSION["auth_module"]); - + $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); } else { $authenticator = false; } @@ -195,11 +259,11 @@ class Pref_Prefs extends Handler_Protected { print "<h2>" . __("Password") . "</h2>"; - $result = db_query($this->link, "SELECT id FROM ttrss_users + $result = $this->dbh->query("SELECT id FROM ttrss_users WHERE id = ".$_SESSION["uid"]." AND pwd_hash = 'SHA1:5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8'"); - if (db_num_rows($result) != 0) { + if ($this->dbh->num_rows($result) != 0) { print format_warning(__("Your password is at default value, please change it."), "default_pass_warning"); } @@ -227,7 +291,7 @@ class Pref_Prefs extends Handler_Protected { </script>"; if ($otp_enabled) { - print_notice("Changing your current password will disable OTP."); + print_notice(__("Changing your current password will disable OTP.")); } print "<table width=\"100%\" class=\"prefPrefsList\">"; @@ -260,7 +324,7 @@ class Pref_Prefs extends Handler_Protected { if ($otp_enabled) { - print_notice("One time passwords are currently enabled. Enter your current password below to disable."); + print_notice(__("One time passwords are currently enabled. Enter your current password below to disable.")); print "<form dojoType=\"dijit.form.Form\">"; @@ -300,9 +364,9 @@ class Pref_Prefs extends Handler_Protected { print "</form>"; - } else { + } else if (function_exists("imagecreatefromstring")) { - print "<p>".__("You will need a compatible Authenticator to use this. Changing your password would automatically disable OTP.") . "</p>"; + print_warning(__("You will need a compatible Authenticator to use this. Changing your password would automatically disable OTP.")); print "<p>".__("Scan the following code by the Authenticator application:")."</p>"; @@ -324,8 +388,8 @@ class Pref_Prefs extends Handler_Protected { parameters: dojo.objectToQuery(this.getValues()), onComplete: function(transport) { notify(''); - if (transport.responseText.indexOf('ERROR: ') == 0) { - notify_error(transport.responseText.replace('ERROR: ', '')); + if (transport.responseText.indexOf('ERROR:') == 0) { + notify_error(transport.responseText.replace('ERROR:', '')); } else { window.location.reload(); } @@ -341,11 +405,13 @@ class Pref_Prefs extends Handler_Protected { print "<td class=\"prefValue\"><input dojoType=\"dijit.form.ValidationTextBox\" type=\"password\" required=\"1\" name=\"password\"></td></tr>"; - print "<tr><td colspan=\"2\">"; + print "<tr><td width=\"40%\">".__("Enter the generated one time password")."</td>"; - print "<input dojoType=\"dijit.form.CheckBox\" required=\"1\" - type=\"checkbox\" id=\"enable_otp\" name=\"enable_otp\"/> "; - print "<label for=\"enable_otp\">".__("I have scanned the code and would like to enable OTP")."</label>"; + print "<td class=\"prefValue\"><input dojoType=\"dijit.form.ValidationTextBox\" autocomplete=\"off\" + required=\"1\" + name=\"otp\"></td></tr>"; + + print "<tr><td colspan=\"2\">"; print "</td></tr><tr><td colspan=\"2\">"; @@ -357,13 +423,16 @@ class Pref_Prefs extends Handler_Protected { print "</form>"; + } else { + + print_notice(__("PHP GD functions are required for OTP support.")); + } } } - global $pluginhost; - $pluginhost->run_hooks($pluginhost::HOOK_PREFS_TAB_SECTION, + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "hook_prefs_tab_section", "prefPrefsAuth"); print "</div>"; #pane @@ -372,8 +441,8 @@ class Pref_Prefs extends Handler_Protected { print "<form dojoType=\"dijit.form.Form\" id=\"changeSettingsForm\">"; - print "<script type=\"dojo/method\" event=\"onSubmit\" args=\"evt\"> - evt.preventDefault(); + print "<script type=\"dojo/method\" event=\"onSubmit\" args=\"evt, quit\"> + if (evt) evt.preventDefault(); if (this.validate()) { console.log(dojo.objectToQuery(this.getValues())); @@ -381,10 +450,14 @@ class Pref_Prefs extends Handler_Protected { parameters: dojo.objectToQuery(this.getValues()), onComplete: function(transport) { var msg = transport.responseText; - if (msg.match('PREFS_THEME_CHANGED')) { - window.location.reload(); + if (quit) { + gotoMain(); } else { - notify_info(msg); + if (msg == 'PREFS_NEED_RELOAD') { + window.location.reload(); + } else { + notify_info(msg); + } } } }); } @@ -395,52 +468,65 @@ class Pref_Prefs extends Handler_Protected { print '<div dojoType="dijit.layout.ContentPane" region="center" style="overflow-y : auto">'; if ($_SESSION["profile"]) { - print_notice("Some preferences are only available in default profile."); + print_notice(__("Some preferences are only available in default profile.")); } if ($_SESSION["profile"]) { - initialize_user_prefs($this->link, $_SESSION["uid"], $_SESSION["profile"]); + initialize_user_prefs($_SESSION["uid"], $_SESSION["profile"]); $profile_qpart = "profile = '" . $_SESSION["profile"] . "'"; } else { - initialize_user_prefs($this->link, $_SESSION["uid"]); + initialize_user_prefs($_SESSION["uid"]); $profile_qpart = "profile IS NULL"; } - if ($_SESSION["prefs_show_advanced"]) + /* if ($_SESSION["prefs_show_advanced"]) $access_query = "true"; else - $access_query = "(access_level = 0 AND section_id != 3)"; + $access_query = "(access_level = 0 AND section_id != 3)"; */ + + $access_query = 'true'; - $result = db_query($this->link, "SELECT DISTINCT - ttrss_user_prefs.pref_name,short_desc,help_text,value,type_name, + $result = $this->dbh->query("SELECT DISTINCT + ttrss_user_prefs.pref_name,value,type_name, ttrss_prefs_sections.order_id, - section_name,def_value,section_id + def_value,section_id FROM ttrss_prefs,ttrss_prefs_types,ttrss_prefs_sections,ttrss_user_prefs WHERE type_id = ttrss_prefs_types.id AND $profile_qpart AND section_id = ttrss_prefs_sections.id AND ttrss_user_prefs.pref_name = ttrss_prefs.pref_name AND $access_query AND - short_desc != '' AND owner_uid = ".$_SESSION["uid"]." - ORDER BY ttrss_prefs_sections.order_id,short_desc"); + ORDER BY ttrss_prefs_sections.order_id,pref_name"); $lnum = 0; $active_section = ""; - while ($line = db_fetch_assoc($result)) { + $listed_boolean_prefs = array(); + + while ($line = $this->dbh->fetch_assoc($result)) { if (in_array($line["pref_name"], $prefs_blacklist)) { continue; } + $type_name = $line["type_name"]; + $pref_name = $line["pref_name"]; + $section_name = $this->getSectionName($line["section_id"]); + $value = $line["value"]; + + $short_desc = $this->getShortDesc($pref_name); + $help_text = $this->getHelpText($pref_name); + + if (!$short_desc) continue; + if ($_SESSION["profile"] && in_array($line["pref_name"], $profile_blacklist)) { continue; } - if ($active_section != $line["section_name"]) { + if ($active_section != $line["section_id"]) { if ($active_section != "") { print "</table>"; @@ -448,22 +534,19 @@ class Pref_Prefs extends Handler_Protected { print "<table width=\"100%\" class=\"prefPrefsList\">"; - $active_section = $line["section_name"]; + $active_section = $line["section_id"]; - print "<tr><td colspan=\"3\"><h3>".__($active_section)."</h3></td></tr>"; + print "<tr><td colspan=\"3\"><h3>".$section_name."</h3></td></tr>"; $lnum = 0; } print "<tr>"; - $type_name = $line["type_name"]; - $pref_name = $line["pref_name"]; - $value = $line["value"]; - $def_value = $line["def_value"]; - $help_text = $line["help_text"]; - - print "<td width=\"40%\" class=\"prefName\" id=\"$pref_name\">" . __($line["short_desc"]); + print "<td width=\"40%\" class=\"prefName\" id=\"$pref_name\">"; + print "<label for='CB_$pref_name'>"; + print $short_desc; + print "</label>"; if ($help_text) print "<div class=\"prefHelp\">".__($help_text)."</div>"; @@ -471,7 +554,11 @@ class Pref_Prefs extends Handler_Protected { print "<td class=\"prefValue\">"; - if ($pref_name == "USER_TIMEZONE") { + if ($pref_name == "USER_LANGUAGE") { + print_select_hash($pref_name, $value, get_translations(), + "style='width : 220px; margin : 0px' dojoType='dijit.form.Select'"); + + } else if ($pref_name == "USER_TIMEZONE") { $timezones = explode("\n", file_get_contents("lib/timezones.txt")); @@ -481,13 +568,14 @@ class Pref_Prefs extends Handler_Protected { print "<button dojoType=\"dijit.form.Button\" onclick=\"customizeCSS()\">" . __('Customize') . "</button>"; - } else if ($pref_name == "DEFAULT_ARTICLE_LIMIT") { + } else if ($pref_name == "USER_CSS_THEME") { - $limits = array(15, 30, 45, 60); + $themes = array_map("basename", glob("themes/*.css")); - print_select($pref_name, $value, $limits, + print_select($pref_name, $value, $themes, 'dojoType="dijit.form.Select"'); + } else if ($pref_name == "DEFAULT_UPDATE_INTERVAL") { global $update_intervals_nodefault; @@ -497,23 +585,21 @@ class Pref_Prefs extends Handler_Protected { } else if ($type_name == "bool") { - if ($value == "true") { - $value = __("Yes"); - } else { - $value = __("No"); - } + array_push($listed_boolean_prefs, $pref_name); + + $checked = ($value == "true") ? "checked=\"checked\"" : ""; if ($pref_name == "PURGE_UNREAD_ARTICLES" && FORCE_ARTICLE_PURGE != 0) { $disabled = "disabled=\"1\""; - $value = __("Yes"); + $checked = "checked=\"checked\""; } else { $disabled = ""; } - print_radio($pref_name, $value, __("Yes"), array(__("Yes"), __("No")), - $disabled); + print "<input type='checkbox' name='$pref_name' $checked $disabled + dojoType='dijit.form.CheckBox' id='CB_$pref_name' value='1'>"; - } else if (array_search($pref_name, array('FRESH_ARTICLE_MAX_AGE', 'DEFAULT_ARTICLE_LIMIT', + } else if (array_search($pref_name, array('FRESH_ARTICLE_MAX_AGE', 'PURGE_OLD_DAYS', 'LONG_DATE_FORMAT', 'SHORT_DATE_FORMAT')) !== false) { $regexp = ($type_name == 'integer') ? 'regexp="^\d*$"' : ''; @@ -568,8 +654,11 @@ class Pref_Prefs extends Handler_Protected { print "</table>"; - global $pluginhost; - $pluginhost->run_hooks($pluginhost::HOOK_PREFS_TAB_SECTION, + $listed_boolean_prefs = htmlspecialchars(join(",", $listed_boolean_prefs)); + + print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"boolean_prefs\" value=\"$listed_boolean_prefs\">"; + + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "hook_prefs_tab_section", "prefPrefsPrefsInside"); print '</div>'; # inside pane @@ -578,8 +667,14 @@ class Pref_Prefs extends Handler_Protected { print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-prefs\">"; print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"saveconfig\">"; - print "<button dojoType=\"dijit.form.Button\" type=\"submit\">". - __('Save configuration')."</button> "; + print "<div dojoType=\"dijit.form.ComboButton\" type=\"submit\"> + <span>".__('Save configuration')."</span> + <div dojoType=\"dijit.DropDownMenu\"> + <div dojoType=\"dijit.MenuItem\" + onclick=\"dijit.byId('changeSettingsForm').onSubmit(null, true)\">". + __("Save and exit preferences")."</div> + </div> + </div>"; print "<button dojoType=\"dijit.form.Button\" onclick=\"return editProfiles()\">". __('Manage profiles')."</button> "; @@ -589,7 +684,7 @@ class Pref_Prefs extends Handler_Protected { print " "; - $checked = $_SESSION["prefs_show_advanced"] ? "checked='1'" : ""; + /* $checked = $_SESSION["prefs_show_advanced"] ? "checked='1'" : ""; print "<input onclick='toggleAdvancedPrefs()' id='prefs_show_advanced' @@ -597,10 +692,9 @@ class Pref_Prefs extends Handler_Protected { $checked type=\"checkbox\"></input> <label for='prefs_show_advanced'>" . - __("Show additional preferences") . "</label>"; + __("Show additional preferences") . "</label>"; */ - global $pluginhost; - $pluginhost->run_hooks($pluginhost::HOOK_PREFS_TAB_SECTION, + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "hook_prefs_tab_section", "prefPrefsPrefsOutside"); print "</form>"; @@ -611,9 +705,9 @@ class Pref_Prefs extends Handler_Protected { print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Plugins')."\">"; - print "<h2>".__("Plugins")."</h2>"; + print "<p>" . __("You will need to reload Tiny Tiny RSS for plugin changes to take effect.") . "</p>"; - print_notice("You will need to reload Tiny Tiny RSS for plugin changes to take effect."); + print_notice(__("Download more plugins at tt-rss.org <a class=\"visibleLink\" target=\"_blank\" href=\"http://tt-rss.org/forum/viewforum.php?f=22\">forums</a> or <a target=\"_blank\" class=\"visibleLink\" href=\"http://tt-rss.org/wiki/Plugins\">wiki</a>.")); print "<form dojoType=\"dijit.form.Form\" id=\"changePluginsForm\">"; @@ -649,9 +743,9 @@ class Pref_Prefs extends Handler_Protected { <td width='10%'>".__('Author')."</td></tr>"; $system_enabled = array_map("trim", explode(",", PLUGINS)); - $user_enabled = array_map("trim", explode(",", get_pref($this->link, "_ENABLED_PLUGINS"))); + $user_enabled = array_map("trim", explode(",", get_pref("_ENABLED_PLUGINS"))); - $tmppluginhost = new PluginHost($this->link); + $tmppluginhost = new PluginHost(); $tmppluginhost->load_all($tmppluginhost::KIND_ALL, $_SESSION["uid"]); $tmppluginhost->load_data(true); @@ -672,7 +766,12 @@ class Pref_Prefs extends Handler_Protected { type=\"checkbox\"></td>"; print "<td>$name</td>"; - print "<td>" . htmlspecialchars($about[1]) . "</td>"; + print "<td>" . htmlspecialchars($about[1]); + if (@$about[4]) { + print " — <a target=\"_blank\" class=\"visibleLink\" + href=\"".htmlspecialchars($about[4])."\">".__("more info")."</a>"; + } + print "</td>"; print "<td>" . htmlspecialchars(sprintf("%.2f", $about[0])) . "</td>"; print "<td>" . htmlspecialchars($about[2]) . "</td>"; @@ -724,7 +823,13 @@ class Pref_Prefs extends Handler_Protected { type=\"checkbox\"></td>"; print "<td><label for='FPCHK-$name'>$name</label></td>"; - print "<td><label for='FPCHK-$name'>" . htmlspecialchars($about[1]) . "</label></td>"; + print "<td><label for='FPCHK-$name'>" . htmlspecialchars($about[1]) . "</label>"; + if (@$about[4]) { + print " — <a target=\"_blank\" class=\"visibleLink\" + href=\"".htmlspecialchars($about[4])."\">".__("more info")."</a>"; + } + print "</td>"; + print "<td>" . htmlspecialchars(sprintf("%.2f", $about[0])) . "</td>"; print "<td>" . htmlspecialchars($about[2]) . "</td>"; @@ -751,8 +856,7 @@ class Pref_Prefs extends Handler_Protected { print "</div>"; #pane - global $pluginhost; - $pluginhost->run_hooks($pluginhost::HOOK_PREFS_TAB, + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB, "hook_prefs_tab", "prefPrefs"); print "</div>"; #container @@ -768,52 +872,67 @@ class Pref_Prefs extends Handler_Protected { require_once "lib/otphp/lib/totp.php"; require_once "lib/phpqrcode/phpqrcode.php"; - $result = db_query($this->link, "SELECT login,salt,otp_enabled + $result = $this->dbh->query("SELECT login,salt,otp_enabled FROM ttrss_users WHERE id = ".$_SESSION["uid"]); $base32 = new Base32(); - $login = db_fetch_result($result, 0, "login"); - $otp_enabled = sql_bool_to_bool(db_fetch_result($result, 0, "otp_enabled")); + $login = $this->dbh->fetch_result($result, 0, "login"); + $otp_enabled = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "otp_enabled")); if (!$otp_enabled) { - $secret = $base32->encode(sha1(db_fetch_result($result, 0, "salt"))); + $secret = $base32->encode(sha1($this->dbh->fetch_result($result, 0, "salt"))); $topt = new \OTPHP\TOTP($secret); print QRcode::png($topt->provisioning_uri($login)); } } function otpenable() { - $password = db_escape_string($_REQUEST["password"]); - $enable_otp = $_REQUEST["enable_otp"] == "on"; + require_once "lib/otphp/vendor/base32.php"; + require_once "lib/otphp/lib/otp.php"; + require_once "lib/otphp/lib/totp.php"; + + $password = $_REQUEST["password"]; + $otp = $_REQUEST["otp"]; - global $pluginhost; - $authenticator = $pluginhost->get_plugin($_SESSION["auth_module"]); + $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); if ($authenticator->check_password($_SESSION["uid"], $password)) { - if ($enable_otp) { - db_query($this->link, "UPDATE ttrss_users SET otp_enabled = true WHERE + $result = $this->dbh->query("SELECT salt + FROM ttrss_users + WHERE id = ".$_SESSION["uid"]); + + $base32 = new Base32(); + + $secret = $base32->encode(sha1($this->dbh->fetch_result($result, 0, "salt"))); + $topt = new \OTPHP\TOTP($secret); + + $otp_check = $topt->now(); + + if ($otp == $otp_check) { + $this->dbh->query("UPDATE ttrss_users SET otp_enabled = true WHERE id = " . $_SESSION["uid"]); print "OK"; + } else { + print "ERROR:".__("Incorrect one time password"); } } else { - print "ERROR: ".__("Incorrect password"); + print "ERROR:".__("Incorrect password"); } } function otpdisable() { - $password = db_escape_string($_REQUEST["password"]); + $password = $this->dbh->escape_string($_REQUEST["password"]); - global $pluginhost; - $authenticator = $pluginhost->get_plugin($_SESSION["auth_module"]); + $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); if ($authenticator->check_password($_SESSION["uid"], $password)) { - db_query($this->link, "UPDATE ttrss_users SET otp_enabled = false WHERE + $this->dbh->query("UPDATE ttrss_users SET otp_enabled = false WHERE id = " . $_SESSION["uid"]); print "OK"; @@ -829,14 +948,176 @@ class Pref_Prefs extends Handler_Protected { else $plugins = ""; - set_pref($this->link, "_ENABLED_PLUGINS", $plugins); + set_pref("_ENABLED_PLUGINS", $plugins); } function clearplugindata() { - $name = db_escape_string($_REQUEST["name"]); + $name = $this->dbh->escape_string($_REQUEST["name"]); + + PluginHost::getInstance()->clear_data(PluginHost::getInstance()->get_plugin($name)); + } + + function customizeCSS() { + $value = get_pref("USER_STYLESHEET"); + + $value = str_replace("<br/>", "\n", $value); + + print_notice(T_sprintf("You can override colors, fonts and layout of your currently selected theme with custom CSS declarations here. <a target=\"_blank\" class=\"visibleLink\" href=\"%s\">This file</a> can be used as a baseline.", "tt-rss.css")); + + print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"rpc\">"; + print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"setpref\">"; + print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"key\" value=\"USER_STYLESHEET\">"; + + print "<table width='100%'><tr><td>"; + print "<textarea dojoType=\"dijit.form.SimpleTextarea\" + style='font-size : 12px; width : 100%; height: 200px;' + placeHolder='body#ttrssMain { font-size : 14px; };' + name='value'>$value</textarea>"; + print "</td></tr></table>"; + + print "<div class='dlgButtons'>"; + print "<button dojoType=\"dijit.form.Button\" + onclick=\"dijit.byId('cssEditDlg').execute()\">".__('Save')."</button> "; + print "<button dojoType=\"dijit.form.Button\" + onclick=\"dijit.byId('cssEditDlg').hide()\">".__('Cancel')."</button>"; + print "</div>"; + + } + + function editPrefProfiles() { + print "<div dojoType=\"dijit.Toolbar\">"; + + print "<div dojoType=\"dijit.form.DropDownButton\">". + "<span>" . __('Select')."</span>"; + print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">"; + print "<div onclick=\"selectTableRows('prefFeedProfileList', 'all')\" + dojoType=\"dijit.MenuItem\">".__('All')."</div>"; + print "<div onclick=\"selectTableRows('prefFeedProfileList', 'none')\" + dojoType=\"dijit.MenuItem\">".__('None')."</div>"; + print "</div></div>"; + + print "<div style=\"float : right\">"; + + print "<input name=\"newprofile\" dojoType=\"dijit.form.ValidationTextBox\" + required=\"1\"> + <button dojoType=\"dijit.form.Button\" + onclick=\"dijit.byId('profileEditDlg').addProfile()\">". + __('Create profile')."</button></div>"; + + print "</div>"; + + $result = $this->dbh->query("SELECT title,id FROM ttrss_settings_profiles + WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title"); + + print "<div class=\"prefProfileHolder\">"; + + print "<form id=\"profile_edit_form\" onsubmit=\"return false\">"; + + print "<table width=\"100%\" class=\"prefFeedProfileList\" + cellspacing=\"0\" id=\"prefFeedProfileList\">"; + + print "<tr class=\"placeholder\" id=\"FCATR-0\">"; #odd + + print "<td width='5%' align='center'><input + id='FCATC-0' + onclick='toggleSelectRow2(this);' + dojoType=\"dijit.form.CheckBox\" + type=\"checkbox\"></td>"; + + if (!$_SESSION["profile"]) { + $is_active = __("(active)"); + } else { + $is_active = ""; + } + + print "<td><span>" . + __("Default profile") . " $is_active</span></td>"; + + print "</tr>"; + + $lnum = 1; + + while ($line = $this->dbh->fetch_assoc($result)) { + + $profile_id = $line["id"]; + $this_row_id = "id=\"FCATR-$profile_id\""; + + print "<tr class=\"placeholder\" $this_row_id>"; + + $edit_title = htmlspecialchars($line["title"]); + + print "<td width='5%' align='center'><input + onclick='toggleSelectRow2(this);' + id='FCATC-$profile_id' + dojoType=\"dijit.form.CheckBox\" + type=\"checkbox\"></td>"; + + if ($_SESSION["profile"] == $line["id"]) { + $is_active = __("(active)"); + } else { + $is_active = ""; + } + + print "<td><span dojoType=\"dijit.InlineEditBox\" + width=\"300px\" autoSave=\"false\" + profile-id=\"$profile_id\">" . $edit_title . + "<script type=\"dojo/method\" event=\"onChange\" args=\"item\"> + var elem = this; + dojo.xhrPost({ + url: 'backend.php', + content: {op: 'rpc', method: 'saveprofile', + value: this.value, + id: this.srcNodeRef.getAttribute('profile-id')}, + load: function(response) { + elem.attr('value', response); + } + }); + </script> + </span> $is_active</td>"; + + print "</tr>"; + + ++$lnum; + } + + print "</table>"; + print "</form>"; + print "</div>"; + + print "<div class='dlgButtons'> + <div style='float : left'> + <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('profileEditDlg').removeSelected()\">". + __('Remove selected profiles')."</button> + <button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('profileEditDlg').activateProfile()\">". + __('Activate profile')."</button> + </div>"; + + print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('profileEditDlg').hide()\">". + __('Close this window')."</button>"; + print "</div>"; + + } + + private function getShortDesc($pref_name) { + if (isset($this->pref_help[$pref_name])) { + return $this->pref_help[$pref_name][0]; + } + return ""; + } + + private function getHelpText($pref_name) { + if (isset($this->pref_help[$pref_name])) { + return $this->pref_help[$pref_name][1]; + } + return ""; + } + + private function getSectionName($id) { + if (isset($this->pref_sections[$id])) { + return $this->pref_sections[$id]; + } - global $pluginhost; - $pluginhost->clear_data($pluginhost->get_plugin($name)); + return ""; } } ?> diff --git a/classes/pref/system.php b/classes/pref/system.php new file mode 100644 index 000000000..7accb1f91 --- /dev/null +++ b/classes/pref/system.php @@ -0,0 +1,90 @@ +<?php + +class Pref_System extends Handler_Protected { + + function before($method) { + if (parent::before($method)) { + if ($_SESSION["access_level"] < 10) { + print __("Your access level is insufficient to open this tab."); + return false; + } + return true; + } + return false; + } + + function csrf_ignore($method) { + $csrf_ignored = array("index"); + + return array_search($method, $csrf_ignored) !== false; + } + + function clearLog() { + $this->dbh->query("DELETE FROM ttrss_error_log"); + } + + function index() { + + print "<div dojoType=\"dijit.layout.AccordionContainer\" region=\"center\">"; + print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Error Log')."\">"; + + if (LOG_DESTINATION == "sql") { + + $result = $this->dbh->query("SELECT errno, errstr, filename, lineno, + created_at, login FROM ttrss_error_log + LEFT JOIN ttrss_users ON (owner_uid = ttrss_users.id) + ORDER BY ttrss_error_log.id DESC + LIMIT 100"); + + print "<button dojoType=\"dijit.form.Button\" + onclick=\"updateSystemList()\">".__('Refresh')."</button> "; + + print " <button dojoType=\"dijit.form.Button\" + onclick=\"clearSqlLog()\">".__('Clear log')."</button> "; + + print "<p><table width=\"100%\" cellspacing=\"10\" class=\"prefErrorLog\">"; + + print "<tr class=\"title\"> + <td width='5%'>".__("Error")."</td> + <td>".__("Filename")."</td> + <td>".__("Message")."</td> + <td width='5%'>".__("User")."</td> + <td width='5%'>".__("Date")."</td> + </tr>"; + + while ($line = $this->dbh->fetch_assoc($result)) { + print "<tr class=\"errrow\">"; + + foreach ($line as $k => $v) { + $line[$k] = htmlspecialchars($v); + } + + print "<td class='errno'>" . Logger::$errornames[$line["errno"]] . " (" . $line["errno"] . ")</td>"; + print "<td class='filename'>" . $line["filename"] . ":" . $line["lineno"] . "</td>"; + print "<td class='errstr'>" . $line["errstr"] . "</td>"; + print "<td class='login'>" . $line["login"] . "</td>"; + + print "<td class='timestamp'>" . + make_local_datetime( + $line["created_at"], false) . "</td>"; + + print "</tr>"; + } + + print "</table>"; + } else { + + print_notice("Please set LOG_DESTINATION to 'sql' in config.php to enable database logging."); + + } + + print "</div>"; + + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB, + "hook_prefs_tab", "prefSystem"); + + print "</div>"; #container + } + +} +?> diff --git a/classes/pref/users.php b/classes/pref/users.php index 0d7ca3c6e..60059dc8d 100644 --- a/classes/pref/users.php +++ b/classes/pref/users.php @@ -19,16 +19,9 @@ class Pref_Users extends Handler_Protected { function userdetails() { - header("Content-Type: text/xml"); - print "<dlg>"; - $uid = sprintf("%d", $_REQUEST["id"]); - print "<title>".__('User details')."</title>"; - - print "<content><![CDATA["; - - $result = db_query($this->link, "SELECT login, + $result = $this->dbh->query("SELECT login, ".SUBSTRING_FOR_DATE."(last_login,1,16) AS last_login, access_level, (SELECT COUNT(int_id) FROM ttrss_user_entries @@ -37,33 +30,33 @@ class Pref_Users extends Handler_Protected { FROM ttrss_users WHERE id = '$uid'"); - if (db_num_rows($result) == 0) { + if ($this->dbh->num_rows($result) == 0) { print "<h1>".__('User not found')."</h1>"; return; } // print "<h1>User Details</h1>"; - $login = db_fetch_result($result, 0, "login"); + $login = $this->dbh->fetch_result($result, 0, "login"); print "<table width='100%'>"; - $last_login = make_local_datetime($this->link, - db_fetch_result($result, 0, "last_login"), true); + $last_login = make_local_datetime( + $this->dbh->fetch_result($result, 0, "last_login"), true); - $created = make_local_datetime($this->link, - db_fetch_result($result, 0, "created"), true); + $created = make_local_datetime( + $this->dbh->fetch_result($result, 0, "created"), true); - $access_level = db_fetch_result($result, 0, "access_level"); - $stored_articles = db_fetch_result($result, 0, "stored_articles"); + $access_level = $this->dbh->fetch_result($result, 0, "access_level"); + $stored_articles = $this->dbh->fetch_result($result, 0, "stored_articles"); print "<tr><td>".__('Registered')."</td><td>$created</td></tr>"; print "<tr><td>".__('Last logged in')."</td><td>$last_login</td></tr>"; - $result = db_query($this->link, "SELECT COUNT(id) as num_feeds FROM ttrss_feeds + $result = $this->dbh->query("SELECT COUNT(id) as num_feeds FROM ttrss_feeds WHERE owner_uid = '$uid'"); - $num_feeds = db_fetch_result($result, 0, "num_feeds"); + $num_feeds = $this->dbh->fetch_result($result, 0, "num_feeds"); print "<tr><td>".__('Subscribed feeds count')."</td><td>$num_feeds</td></tr>"; @@ -71,14 +64,12 @@ class Pref_Users extends Handler_Protected { print "<h1>".__('Subscribed feeds')."</h1>"; - $result = db_query($this->link, "SELECT id,title,site_url FROM ttrss_feeds + $result = $this->dbh->query("SELECT id,title,site_url FROM ttrss_feeds WHERE owner_uid = '$uid' ORDER BY title"); print "<ul class=\"userFeedList\">"; - $row_class = "odd"; - - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $icon_file = ICONS_URL."/".$line["id"].".ico"; @@ -88,13 +79,11 @@ class Pref_Users extends Handler_Protected { $feed_icon = "<img class=\"tinyFeedIcon\" src=\"images/blank_icon.gif\">"; } - print "<li class=\"$row_class\">$feed_icon <a href=\"".$line["site_url"]."\">".$line["title"]."</a></li>"; - - $row_class = $row_class == "even" ? "odd" : "even"; + print "<li>$feed_icon <a href=\"".$line["site_url"]."\">".$line["title"]."</a></li>"; } - if (db_num_rows($result) < $num_feeds) { + if ($this->dbh->num_rows($result) < $num_feeds) { // FIXME - add link to show ALL subscribed feeds here somewhere print "<li><img class=\"tinyFeedIcon\" src=\"images/blank_icon.gif\"> ...</li>"; @@ -106,33 +95,24 @@ class Pref_Users extends Handler_Protected { <button onclick=\"closeInfoBox()\">".__("Close this window"). "</button></div>"; - print "]]></content></dlg>"; - return; } function edit() { global $access_level_names; - header("Content-Type: text/xml"); - - $id = db_escape_string($_REQUEST["id"]); - - print "<dlg id=\"$method\">"; - print "<title>".__('User Editor')."</title>"; - print "<content><![CDATA["; - + $id = $this->dbh->escape_string($_REQUEST["id"]); print "<form id=\"user_edit_form\" onsubmit='return false'>"; print "<input type=\"hidden\" name=\"id\" value=\"$id\">"; print "<input type=\"hidden\" name=\"op\" value=\"pref-users\">"; print "<input type=\"hidden\" name=\"method\" value=\"editSave\">"; - $result = db_query($this->link, "SELECT * FROM ttrss_users WHERE id = '$id'"); + $result = $this->dbh->query("SELECT * FROM ttrss_users WHERE id = '$id'"); - $login = db_fetch_result($result, 0, "login"); - $access_level = db_fetch_result($result, 0, "access_level"); - $email = db_fetch_result($result, 0, "email"); + $login = $this->dbh->fetch_result($result, 0, "login"); + $access_level = $this->dbh->fetch_result($result, 0, "access_level"); + $email = $this->dbh->fetch_result($result, 0, "email"); $sel_disabled = ($id == $_SESSION["uid"]) ? "disabled" : ""; @@ -193,17 +173,15 @@ class Pref_Users extends Handler_Protected { <button onclick=\"return userEditCancel()\">". __('Cancel')."</button></div>"; - print "]]></content></dlg>"; - return; } function editSave() { - $login = db_escape_string(trim($_REQUEST["login"])); - $uid = db_escape_string($_REQUEST["id"]); + $login = $this->dbh->escape_string(trim($_REQUEST["login"])); + $uid = $this->dbh->escape_string($_REQUEST["id"]); $access_level = (int) $_REQUEST["access_level"]; - $email = db_escape_string(trim($_REQUEST["email"])); - $password = db_escape_string(trim($_REQUEST["password"])); + $email = $this->dbh->escape_string(trim($_REQUEST["email"])); + $password = $_REQUEST["password"]; if ($password) { $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); @@ -213,52 +191,52 @@ class Pref_Users extends Handler_Protected { $pass_query_part = ""; } - db_query($this->link, "UPDATE ttrss_users SET $pass_query_part login = '$login', - access_level = '$access_level', email = '$email', otp_enabled = 'false' + $this->dbh->query("UPDATE ttrss_users SET $pass_query_part login = '$login', + access_level = '$access_level', email = '$email', otp_enabled = false WHERE id = '$uid'"); } function remove() { - $ids = split(",", db_escape_string($_REQUEST["ids"])); + $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); foreach ($ids as $id) { if ($id != $_SESSION["uid"] && $id != 1) { - db_query($this->link, "DELETE FROM ttrss_tags WHERE owner_uid = '$id'"); - db_query($this->link, "DELETE FROM ttrss_feeds WHERE owner_uid = '$id'"); - db_query($this->link, "DELETE FROM ttrss_users WHERE id = '$id'"); + $this->dbh->query("DELETE FROM ttrss_tags WHERE owner_uid = '$id'"); + $this->dbh->query("DELETE FROM ttrss_feeds WHERE owner_uid = '$id'"); + $this->dbh->query("DELETE FROM ttrss_users WHERE id = '$id'"); } } } function add() { - $login = db_escape_string(trim($_REQUEST["login"])); + $login = $this->dbh->escape_string(trim($_REQUEST["login"])); $tmp_user_pwd = make_password(8); $salt = substr(bin2hex(get_random_bytes(125)), 0, 250); $pwd_hash = encrypt_password($tmp_user_pwd, $salt, true); - $result = db_query($this->link, "SELECT id FROM ttrss_users WHERE + $result = $this->dbh->query("SELECT id FROM ttrss_users WHERE login = '$login'"); - if (db_num_rows($result) == 0) { + if ($this->dbh->num_rows($result) == 0) { - db_query($this->link, "INSERT INTO ttrss_users + $this->dbh->query("INSERT INTO ttrss_users (login,pwd_hash,access_level,last_login,created, salt) VALUES ('$login', '$pwd_hash', 0, null, NOW(), '$salt')"); - $result = db_query($this->link, "SELECT id FROM ttrss_users WHERE + $result = $this->dbh->query("SELECT id FROM ttrss_users WHERE login = '$login' AND pwd_hash = '$pwd_hash'"); - if (db_num_rows($result) == 1) { + if ($this->dbh->num_rows($result) == 1) { - $new_uid = db_fetch_result($result, 0, "id"); + $new_uid = $this->dbh->fetch_result($result, 0, "id"); print format_notice(T_sprintf("Added user <b>%s</b> with password <b>%s</b>", $login, $tmp_user_pwd)); - initialize_user($this->link, $new_uid); + initialize_user($new_uid); } else { @@ -270,11 +248,9 @@ class Pref_Users extends Handler_Protected { } } - function resetPass() { - - $uid = db_escape_string($_REQUEST["id"]); + static function resetUserPassword($uid, $show_password) { - $result = db_query($this->link, "SELECT login,email + $result = db_query("SELECT login,email FROM ttrss_users WHERE id = '$uid'"); $login = db_fetch_result($result, 0, "login"); @@ -286,18 +262,18 @@ class Pref_Users extends Handler_Protected { $pwd_hash = encrypt_password($tmp_user_pwd, $new_salt, true); - db_query($this->link, "UPDATE ttrss_users SET pwd_hash = '$pwd_hash', salt = '$new_salt' + db_query("UPDATE ttrss_users SET pwd_hash = '$pwd_hash', salt = '$new_salt' WHERE id = '$uid'"); - print T_sprintf("Changed password of user <b>%s</b> - to <b>%s</b>", $login, $tmp_user_pwd); + if ($show_password) { + print T_sprintf("Changed password of user <b>%s</b> to <b>%s</b>", $login, $tmp_user_pwd); + } else { + print_notice(T_sprintf("Sending new password of user <b>%s</b> to <b>%s</b>", $login, $email)); + } - require_once 'lib/phpmailer/class.phpmailer.php'; + require_once 'classes/ttrssmailer.php'; if ($email) { - print " "; - print T_sprintf("Notifying <b>%s</b>.", $email); - require_once "lib/MiniTemplator.class.php"; $tpl = new MiniTemplator; @@ -313,35 +289,19 @@ class Pref_Users extends Handler_Protected { $tpl->generateOutputToString($message); - $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($email, $login); + $mail = new ttrssMailer(); - if (SMTP_HOST) { - $mail->Host = SMTP_HOST; - $mail->Mailer = "smtp"; - $mail->SMTPAuth = SMTP_LOGIN != ''; - $mail->Username = SMTP_LOGIN; - $mail->Password = SMTP_PASSWORD; - } - - $mail->IsHTML(false); - $mail->Subject = __("[tt-rss] Password change notification"); - $mail->Body = $message; - - $rc = $mail->Send(); + $rc = $mail->quickMail($email, $login, + __("[tt-rss] Password change notification"), + $message, false); if (!$rc) print_error($mail->ErrorInfo); } + } - print "</div>"; + function resetPass() { + $uid = $this->dbh->escape_string($_REQUEST["id"]); + Pref_Users::resetUserPassword($uid, true); } function index() { @@ -353,7 +313,7 @@ class Pref_Users extends Handler_Protected { print "<div id=\"pref-user-toolbar\" dojoType=\"dijit.Toolbar\">"; - $user_search = db_escape_string($_REQUEST["search"]); + $user_search = $this->dbh->escape_string($_REQUEST["search"]); if (array_key_exists("search", $_REQUEST)) { $_SESSION["prefs_user_search"] = $user_search; @@ -368,7 +328,7 @@ class Pref_Users extends Handler_Protected { __('Search')."</button> </div>"; - $sort = db_escape_string($_REQUEST["sort"]); + $sort = $this->dbh->escape_string($_REQUEST["sort"]); if (!$sort || $sort == "undefined") { $sort = "login"; @@ -403,7 +363,7 @@ class Pref_Users extends Handler_Protected { if ($user_search) { - $user_search = split(" ", $user_search); + $user_search = explode(" ", $user_search); $tokens = array(); foreach ($user_search as $token) { @@ -417,7 +377,7 @@ class Pref_Users extends Handler_Protected { $user_search_query = ""; } - $result = db_query($this->link, "SELECT + $result = $this->dbh->query("SELECT id,login,access_level,email, ".SUBSTRING_FOR_DATE."(last_login,1,16) as last_login, ".SUBSTRING_FOR_DATE."(created,1,16) as created @@ -428,7 +388,7 @@ class Pref_Users extends Handler_Protected { id > 0 ORDER BY $sort"); - if (db_num_rows($result) > 0) { + if ($this->dbh->num_rows($result) > 0) { print "<p><table width=\"100%\" cellspacing=\"0\" class=\"prefUserList\" id=\"prefUserList\">"; @@ -442,9 +402,7 @@ class Pref_Users extends Handler_Protected { $lnum = 0; - while ($line = db_fetch_assoc($result)) { - - $class = ($lnum % 2) ? "even" : "odd"; + while ($line = $this->dbh->fetch_assoc($result)) { $uid = $line["id"]; @@ -452,8 +410,8 @@ class Pref_Users extends Handler_Protected { $line["login"] = htmlspecialchars($line["login"]); - $line["created"] = make_local_datetime($this->link, $line["created"], false); - $line["last_login"] = make_local_datetime($this->link, $line["last_login"], false); + $line["created"] = make_local_datetime($line["created"], false); + $line["last_login"] = make_local_datetime($line["last_login"], false); print "<td align='center'><input onclick='toggleSelectRow2(this);' dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" @@ -489,8 +447,7 @@ class Pref_Users extends Handler_Protected { print "</div>"; #pane - global $pluginhost; - $pluginhost->run_hooks($pluginhost::HOOK_PREFS_TAB, + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB, "hook_prefs_tab", "prefUsers"); print "</div>"; #container diff --git a/classes/rpc.php b/classes/rpc.php index 46c8b0d85..46583feb5 100644 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -8,18 +8,17 @@ class RPC extends Handler_Protected { } function setprofile() { - $id = db_escape_string($_REQUEST["id"]); + $id = $this->dbh->escape_string($_REQUEST["id"]); $_SESSION["profile"] = $id; - $_SESSION["prefs_cache"] = array(); } function remprofiles() { - $ids = explode(",", db_escape_string(trim($_REQUEST["ids"]))); + $ids = explode(",", $this->dbh->escape_string(trim($_REQUEST["ids"]))); foreach ($ids as $id) { if ($_SESSION["profile"] != $id) { - db_query($this->link, "DELETE FROM ttrss_settings_profiles WHERE id = '$id' AND + $this->dbh->query("DELETE FROM ttrss_settings_profiles WHERE id = '$id' AND owner_uid = " . $_SESSION["uid"]); } } @@ -27,38 +26,38 @@ class RPC extends Handler_Protected { // Silent function addprofile() { - $title = db_escape_string(trim($_REQUEST["title"])); + $title = $this->dbh->escape_string(trim($_REQUEST["title"])); if ($title) { - db_query($this->link, "BEGIN"); + $this->dbh->query("BEGIN"); - $result = db_query($this->link, "SELECT id FROM ttrss_settings_profiles + $result = $this->dbh->query("SELECT id FROM ttrss_settings_profiles WHERE title = '$title' AND owner_uid = " . $_SESSION["uid"]); - if (db_num_rows($result) == 0) { + if ($this->dbh->num_rows($result) == 0) { - db_query($this->link, "INSERT INTO ttrss_settings_profiles (title, owner_uid) + $this->dbh->query("INSERT INTO ttrss_settings_profiles (title, owner_uid) VALUES ('$title', ".$_SESSION["uid"] .")"); - $result = db_query($this->link, "SELECT id FROM ttrss_settings_profiles WHERE + $result = $this->dbh->query("SELECT id FROM ttrss_settings_profiles WHERE title = '$title'"); - if (db_num_rows($result) != 0) { - $profile_id = db_fetch_result($result, 0, "id"); + if ($this->dbh->num_rows($result) != 0) { + $profile_id = $this->dbh->fetch_result($result, 0, "id"); if ($profile_id) { - initialize_user_prefs($this->link, $_SESSION["uid"], $profile_id); + initialize_user_prefs($_SESSION["uid"], $profile_id); } } } - db_query($this->link, "COMMIT"); + $this->dbh->query("COMMIT"); } } // Silent function saveprofile() { - $id = db_escape_string($_REQUEST["id"]); - $title = db_escape_string(trim($_REQUEST["value"])); + $id = $this->dbh->escape_string($_REQUEST["id"]); + $title = $this->dbh->escape_string(trim($_REQUEST["value"])); if ($id == 0) { print __("Default profile"); @@ -66,56 +65,55 @@ class RPC extends Handler_Protected { } if ($title) { - db_query($this->link, "BEGIN"); + $this->dbh->query("BEGIN"); - $result = db_query($this->link, "SELECT id FROM ttrss_settings_profiles + $result = $this->dbh->query("SELECT id FROM ttrss_settings_profiles WHERE title = '$title' AND owner_uid =" . $_SESSION["uid"]); - if (db_num_rows($result) == 0) { - db_query($this->link, "UPDATE ttrss_settings_profiles + if ($this->dbh->num_rows($result) == 0) { + $this->dbh->query("UPDATE ttrss_settings_profiles SET title = '$title' WHERE id = '$id' AND owner_uid = " . $_SESSION["uid"]); print $title; } else { - $result = db_query($this->link, "SELECT title FROM ttrss_settings_profiles + $result = $this->dbh->query("SELECT title FROM ttrss_settings_profiles WHERE id = '$id' AND owner_uid =" . $_SESSION["uid"]); - print db_fetch_result($result, 0, "title"); + print $this->dbh->fetch_result($result, 0, "title"); } - db_query($this->link, "COMMIT"); + $this->dbh->query("COMMIT"); } } // Silent function remarchive() { - $ids = explode(",", db_escape_string($_REQUEST["ids"])); + $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); foreach ($ids as $id) { - $result = db_query($this->link, "DELETE FROM ttrss_archived_feeds WHERE + $result = $this->dbh->query("DELETE FROM ttrss_archived_feeds WHERE (SELECT COUNT(*) FROM ttrss_user_entries WHERE orig_feed_id = '$id') = 0 AND id = '$id' AND owner_uid = ".$_SESSION["uid"]); - $rc = db_affected_rows($this->link, $result); + $rc = $this->dbh->affected_rows($result); } } function addfeed() { - $feed = db_escape_string($_REQUEST['feed']); - $cat = db_escape_string($_REQUEST['cat']); - $login = db_escape_string($_REQUEST['login']); - $pass = db_escape_string($_REQUEST['pass']); - $need_auth = db_escape_string($_REQUEST['need_auth']) != ""; + $feed = $this->dbh->escape_string($_REQUEST['feed']); + $cat = $this->dbh->escape_string($_REQUEST['cat']); + $login = $this->dbh->escape_string($_REQUEST['login']); + $pass = trim($_REQUEST['pass']); // escaped later - $rc = subscribe_to_feed($this->link, $feed, $cat, $login, $pass, $need_auth); + $rc = subscribe_to_feed($feed, $cat, $login, $pass); print json_encode(array("result" => $rc)); } function togglepref() { - $key = db_escape_string($_REQUEST["key"]); - set_pref($this->link, $key, !get_pref($this->link, $key)); - $value = get_pref($this->link, $key); + $key = $this->dbh->escape_string($_REQUEST["key"]); + set_pref($key, !get_pref($key)); + $value = get_pref($key); print json_encode(array("param" =>$key, "value" => $value)); } @@ -125,14 +123,14 @@ class RPC extends Handler_Protected { $key = $_REQUEST['key']; $value = str_replace("\n", "<br/>", $_REQUEST['value']); - set_pref($this->link, $key, $value, $_SESSION['uid'], $key != 'USER_STYLESHEET'); + set_pref($key, $value, $_SESSION['uid'], $key != 'USER_STYLESHEET'); print json_encode(array("param" =>$key, "value" => $value)); } function mark() { $mark = $_REQUEST["mark"]; - $id = db_escape_string($_REQUEST["id"]); + $id = $this->dbh->escape_string($_REQUEST["id"]); if ($mark == "1") { $mark = "true"; @@ -140,77 +138,127 @@ class RPC extends Handler_Protected { $mark = "false"; } - $result = db_query($this->link, "UPDATE ttrss_user_entries SET marked = $mark + $result = $this->dbh->query("UPDATE ttrss_user_entries SET marked = $mark, + last_marked = NOW() WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); print json_encode(array("message" => "UPDATE_COUNTERS")); } function delete() { - $ids = db_escape_string($_REQUEST["ids"]); + $ids = $this->dbh->escape_string($_REQUEST["ids"]); - $result = db_query($this->link, "DELETE FROM ttrss_user_entries + $result = $this->dbh->query("DELETE FROM ttrss_user_entries WHERE ref_id IN ($ids) AND owner_uid = " . $_SESSION["uid"]); + purge_orphans(); + print json_encode(array("message" => "UPDATE_COUNTERS")); } function unarchive() { - $ids = db_escape_string($_REQUEST["ids"]); + $ids = explode(",", $_REQUEST["ids"]); + + foreach ($ids as $id) { + $id = $this->dbh->escape_string(trim($id)); + $this->dbh->query("BEGIN"); + + $result = $this->dbh->query("SELECT feed_url,site_url,title FROM ttrss_archived_feeds + WHERE id = (SELECT orig_feed_id FROM ttrss_user_entries WHERE ref_id = $id + AND owner_uid = ".$_SESSION["uid"].")"); + + if ($this->dbh->num_rows($result) != 0) { + $feed_url = $this->dbh->escape_string(db_fetch_result($result, 0, "feed_url")); + $site_url = $this->dbh->escape_string(db_fetch_result($result, 0, "site_url")); + $title = $this->dbh->escape_string(db_fetch_result($result, 0, "title")); - $result = db_query($this->link, "UPDATE ttrss_user_entries - SET feed_id = orig_feed_id, orig_feed_id = NULL - WHERE ref_id IN ($ids) AND owner_uid = " . $_SESSION["uid"]); + $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE feed_url = '$feed_url' + AND owner_uid = " .$_SESSION["uid"]); + + if ($this->dbh->num_rows($result) == 0) { + + if (!$title) $title = '[Unknown]'; + + $result = $this->dbh->query( + "INSERT INTO ttrss_feeds + (owner_uid,feed_url,site_url,title,cat_id,auth_login,auth_pass,update_method) + VALUES (".$_SESSION["uid"].", + '$feed_url', + '$site_url', + '$title', + NULL, '', '', 0)"); + + $result = $this->dbh->query( + "SELECT id FROM ttrss_feeds WHERE feed_url = '$feed_url' + AND owner_uid = ".$_SESSION["uid"]); + + if ($this->dbh->num_rows($result) != 0) { + $feed_id = $this->dbh->fetch_result($result, 0, "id"); + } + + } else { + $feed_id = $this->dbh->fetch_result($result, 0, "id"); + } + + if ($feed_id) { + $result = $this->dbh->query("UPDATE ttrss_user_entries + SET feed_id = '$feed_id', orig_feed_id = NULL + WHERE ref_id = $id AND owner_uid = " . $_SESSION["uid"]); + } + } + + $this->dbh->query("COMMIT"); + } print json_encode(array("message" => "UPDATE_COUNTERS")); } function archive() { - $ids = explode(",", db_escape_string($_REQUEST["ids"])); + $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); foreach ($ids as $id) { - $this->archive_article($this->link, $id, $_SESSION["uid"]); + $this->archive_article($id, $_SESSION["uid"]); } print json_encode(array("message" => "UPDATE_COUNTERS")); } - private function archive_article($link, $id, $owner_uid) { - db_query($link, "BEGIN"); + private function archive_article($id, $owner_uid) { + $this->dbh->query("BEGIN"); - $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries + $result = $this->dbh->query("SELECT feed_id FROM ttrss_user_entries WHERE ref_id = '$id' AND owner_uid = $owner_uid"); - if (db_num_rows($result) != 0) { + if ($this->dbh->num_rows($result) != 0) { /* prepare the archived table */ - $feed_id = (int) db_fetch_result($result, 0, "feed_id"); + $feed_id = (int) $this->dbh->fetch_result($result, 0, "feed_id"); if ($feed_id) { - $result = db_query($link, "SELECT id FROM ttrss_archived_feeds + $result = $this->dbh->query("SELECT id FROM ttrss_archived_feeds WHERE id = '$feed_id'"); - if (db_num_rows($result) == 0) { - db_query($link, "INSERT INTO ttrss_archived_feeds + if ($this->dbh->num_rows($result) == 0) { + $this->dbh->query("INSERT INTO ttrss_archived_feeds (id, owner_uid, title, feed_url, site_url) SELECT id, owner_uid, title, feed_url, site_url from ttrss_feeds WHERE id = '$feed_id'"); } - db_query($link, "UPDATE ttrss_user_entries + $this->dbh->query("UPDATE ttrss_user_entries SET orig_feed_id = feed_id, feed_id = NULL WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); } } - db_query($link, "COMMIT"); + $this->dbh->query("COMMIT"); } function publ() { $pub = $_REQUEST["pub"]; - $id = db_escape_string($_REQUEST["id"]); - $note = trim(strip_tags(db_escape_string($_REQUEST["note"]))); + $id = $this->dbh->escape_string($_REQUEST["id"]); + $note = trim(strip_tags($this->dbh->escape_string($_REQUEST["note"]))); if ($pub == "1") { $pub = "true"; @@ -218,8 +266,8 @@ class RPC extends Handler_Protected { $pub = "false"; } - $result = db_query($this->link, "UPDATE ttrss_user_entries SET - published = $pub, last_read = NOW() + $result = $this->dbh->query("UPDATE ttrss_user_entries SET + published = $pub, last_published = NOW() WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); $pubsub_result = false; @@ -227,7 +275,7 @@ class RPC extends Handler_Protected { if (PUBSUBHUBBUB_HUB) { $rss_link = get_self_url_prefix() . "/public.php?op=rss&id=-2&key=" . - get_feed_access_key($this->link, -2, false); + get_feed_access_key(-2, false); $p = new Publisher(PUBSUBHUBBUB_HUB); @@ -243,251 +291,103 @@ class RPC extends Handler_Protected { $reply = array(); - if ($seq) $reply['seq'] = $seq; + if (!empty($_REQUEST['seq'])) $reply['seq'] = (int) $_REQUEST['seq']; - if ($last_article_id != getLastArticleId($this->link)) { - $reply['counters'] = getAllCounters($this->link); + if ($last_article_id != getLastArticleId()) { + $reply['counters'] = getAllCounters(); } - $reply['runtime-info'] = make_runtime_info($this->link); + $reply['runtime-info'] = make_runtime_info(); print json_encode($reply); } /* GET["cmode"] = 0 - mark as read, 1 - as unread, 2 - toggle */ function catchupSelected() { - $ids = explode(",", db_escape_string($_REQUEST["ids"])); + $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); $cmode = sprintf("%d", $_REQUEST["cmode"]); - catchupArticlesById($this->link, $ids, $cmode); + catchupArticlesById($ids, $cmode); - print json_encode(array("message" => "UPDATE_COUNTERS")); + print json_encode(array("message" => "UPDATE_COUNTERS", "ids" => $ids)); } function markSelected() { - $ids = explode(",", db_escape_string($_REQUEST["ids"])); + $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); $cmode = sprintf("%d", $_REQUEST["cmode"]); - $this->markArticlesById($this->link, $ids, $cmode); + $this->markArticlesById($ids, $cmode); print json_encode(array("message" => "UPDATE_COUNTERS")); } function publishSelected() { - $ids = explode(",", db_escape_string($_REQUEST["ids"])); + $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); $cmode = sprintf("%d", $_REQUEST["cmode"]); - $this->publishArticlesById($this->link, $ids, $cmode); + $this->publishArticlesById($ids, $cmode); print json_encode(array("message" => "UPDATE_COUNTERS")); } function sanityCheck() { $_SESSION["hasAudio"] = $_REQUEST["hasAudio"] === "true"; + $_SESSION["hasSandbox"] = $_REQUEST["hasSandbox"] === "true"; + $_SESSION["hasMp3"] = $_REQUEST["hasMp3"] === "true"; + $_SESSION["clientTzOffset"] = $_REQUEST["clientTzOffset"]; $reply = array(); - $reply['error'] = sanity_check($this->link); + $reply['error'] = sanity_check(); if ($reply['error']['code'] == 0) { - $reply['init-params'] = make_init_params($this->link); - $reply['runtime-info'] = make_runtime_info($this->link); + $reply['init-params'] = make_init_params(); + $reply['runtime-info'] = make_runtime_info(); } print json_encode($reply); } - function setArticleTags() { - - $id = db_escape_string($_REQUEST["id"]); - - $tags_str = db_escape_string($_REQUEST["tags_str"]); - $tags = array_unique(trim_array(explode(",", $tags_str))); - - db_query($this->link, "BEGIN"); - - $result = db_query($this->link, "SELECT int_id FROM ttrss_user_entries WHERE - ref_id = '$id' AND owner_uid = '".$_SESSION["uid"]."' LIMIT 1"); - - if (db_num_rows($result) == 1) { - - $tags_to_cache = array(); - - $int_id = db_fetch_result($result, 0, "int_id"); - - db_query($this->link, "DELETE FROM ttrss_tags WHERE - post_int_id = $int_id AND owner_uid = '".$_SESSION["uid"]."'"); - - foreach ($tags as $tag) { - $tag = sanitize_tag($tag); - - if (!tag_is_valid($tag)) { - continue; - } - - if (preg_match("/^[0-9]*$/", $tag)) { - continue; - } - - // print "<!-- $id : $int_id : $tag -->"; - - if ($tag != '') { - db_query($this->link, "INSERT INTO ttrss_tags - (post_int_id, owner_uid, tag_name) VALUES ('$int_id', '".$_SESSION["uid"]."', '$tag')"); - } - - array_push($tags_to_cache, $tag); - } - - /* update tag cache */ - - sort($tags_to_cache); - $tags_str = join(",", $tags_to_cache); - - db_query($this->link, "UPDATE ttrss_user_entries - SET tag_cache = '$tags_str' WHERE ref_id = '$id' - AND owner_uid = " . $_SESSION["uid"]); - } - - db_query($this->link, "COMMIT"); - - $tags = get_article_tags($this->link, $id); - $tags_str = format_tags_string($tags, $id); - $tags_str_full = join(", ", $tags); - - if (!$tags_str_full) $tags_str_full = __("no tags"); - - print json_encode(array("tags_str" => array("id" => $id, - "content" => $tags_str, "content_full" => $tags_str_full))); - } - - function regenOPMLKey() { - $this->update_feed_access_key($this->link, 'OPML:Publish', - false, $_SESSION["uid"]); - - $new_link = Opml::opml_publish_url($this->link); - - print json_encode(array("link" => $new_link)); - } - function completeLabels() { - $search = db_escape_string($_REQUEST["search"]); + $search = $this->dbh->escape_string($_REQUEST["search"]); - $result = db_query($this->link, "SELECT DISTINCT caption FROM + $result = $this->dbh->query("SELECT DISTINCT caption FROM ttrss_labels2 WHERE owner_uid = '".$_SESSION["uid"]."' AND LOWER(caption) LIKE LOWER('$search%') ORDER BY caption LIMIT 5"); print "<ul>"; - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { print "<li>" . $line["caption"] . "</li>"; } print "</ul>"; } - - function completeTags() { - $search = db_escape_string($_REQUEST["search"]); - - $result = db_query($this->link, "SELECT DISTINCT tag_name FROM ttrss_tags - WHERE owner_uid = '".$_SESSION["uid"]."' AND - tag_name LIKE '$search%' ORDER BY tag_name - LIMIT 10"); - - print "<ul>"; - while ($line = db_fetch_assoc($result)) { - print "<li>" . $line["tag_name"] . "</li>"; - } - print "</ul>"; - } - function purge() { - $ids = explode(",", db_escape_string($_REQUEST["ids"])); + $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"])); $days = sprintf("%d", $_REQUEST["days"]); foreach ($ids as $id) { - $result = db_query($this->link, "SELECT id FROM ttrss_feeds WHERE + $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE id = '$id' AND owner_uid = ".$_SESSION["uid"]); - if (db_num_rows($result) == 1) { - purge_feed($this->link, $id, $days); - } - } - } - - function getArticles() { - $ids = explode(",", db_escape_string($_REQUEST["ids"])); - $articles = array(); - - foreach ($ids as $id) { - if ($id) { - array_push($articles, format_article($this->link, $id, 0, false)); - } - } - - print json_encode($articles); - } - - function checkDate() { - $date = db_escape_string($_REQUEST["date"]); - $date_parsed = strtotime($date); - - print json_encode(array("result" => (bool)$date_parsed, - "date" => date("c", $date_parsed))); - } - - function assigntolabel() { - return $this->labelops(true); - } - - function removefromlabel() { - return $this->labelops(false); - } - - function labelops($assign) { - $reply = array(); - - $ids = explode(",", db_escape_string($_REQUEST["ids"])); - $label_id = db_escape_string($_REQUEST["lid"]); - - $label = db_escape_string(label_find_caption($this->link, $label_id, - $_SESSION["uid"])); - - $reply["info-for-headlines"] = array(); - - if ($label) { - - foreach ($ids as $id) { - - if ($assign) - label_add_article($this->link, $id, $label, $_SESSION["uid"]); - else - label_remove_article($this->link, $id, $label, $_SESSION["uid"]); - - $labels = get_article_labels($this->link, $id, $_SESSION["uid"]); - - array_push($reply["info-for-headlines"], - array("id" => $id, "labels" => format_article_labels($labels, $id))); - + if ($this->dbh->num_rows($result) == 1) { + purge_feed($id, $days); } } - - $reply["message"] = "UPDATE_COUNTERS"; - - print json_encode($reply); } function updateFeedBrowser() { - $search = db_escape_string($_REQUEST["search"]); - $limit = db_escape_string($_REQUEST["limit"]); - $mode = (int) db_escape_string($_REQUEST["mode"]); + $search = $this->dbh->escape_string($_REQUEST["search"]); + $limit = $this->dbh->escape_string($_REQUEST["limit"]); + $mode = (int) $this->dbh->escape_string($_REQUEST["mode"]); require_once "feedbrowser.php"; print json_encode(array("content" => - make_feed_browser($this->link, $search, $limit, $mode), + make_feed_browser($search, $limit, $mode), "mode" => $mode)); } @@ -502,14 +402,14 @@ class RPC extends Handler_Protected { if ($mode == 1) { foreach ($payload as $feed) { - $title = db_escape_string($feed[0]); - $feed_url = db_escape_string($feed[1]); + $title = $this->dbh->escape_string($feed[0]); + $feed_url = $this->dbh->escape_string($feed[1]); - $result = db_query($this->link, "SELECT id FROM ttrss_feeds WHERE + $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE feed_url = '$feed_url' AND owner_uid = " . $_SESSION["uid"]); - if (db_num_rows($result) == 0) { - $result = db_query($this->link, "INSERT INTO ttrss_feeds + if ($this->dbh->num_rows($result) == 0) { + $result = $this->dbh->query("INSERT INTO ttrss_feeds (owner_uid,feed_url,title,cat_id,site_url) VALUES ('".$_SESSION["uid"]."', '$feed_url', '$title', NULL, '')"); @@ -518,19 +418,19 @@ class RPC extends Handler_Protected { } else if ($mode == 2) { // feed archive foreach ($payload as $id) { - $result = db_query($this->link, "SELECT * FROM ttrss_archived_feeds + $result = $this->dbh->query("SELECT * FROM ttrss_archived_feeds WHERE id = '$id' AND owner_uid = " . $_SESSION["uid"]); - if (db_num_rows($result) != 0) { - $site_url = db_escape_string(db_fetch_result($result, 0, "site_url")); - $feed_url = db_escape_string(db_fetch_result($result, 0, "feed_url")); - $title = db_escape_string(db_fetch_result($result, 0, "title")); + if ($this->dbh->num_rows($result) != 0) { + $site_url = $this->dbh->escape_string(db_fetch_result($result, 0, "site_url")); + $feed_url = $this->dbh->escape_string(db_fetch_result($result, 0, "feed_url")); + $title = $this->dbh->escape_string(db_fetch_result($result, 0, "title")); - $result = db_query($this->link, "SELECT id FROM ttrss_feeds WHERE + $result = $this->dbh->query("SELECT id FROM ttrss_feeds WHERE feed_url = '$feed_url' AND owner_uid = " . $_SESSION["uid"]); - if (db_num_rows($result) == 0) { - $result = db_query($this->link, "INSERT INTO ttrss_feeds + if ($this->dbh->num_rows($result) == 0) { + $result = $this->dbh->query("INSERT INTO ttrss_feeds (owner_uid,feed_url,title,cat_id,site_url) VALUES ('$id','".$_SESSION["uid"]."', '$feed_url', '$title', NULL, '$site_url')"); @@ -541,134 +441,45 @@ class RPC extends Handler_Protected { } function catchupFeed() { - $feed_id = db_escape_string($_REQUEST['feed_id']); - $is_cat = db_escape_string($_REQUEST['is_cat']) == "true"; - $max_id = (int) db_escape_string($_REQUEST['max_id']); + $feed_id = $this->dbh->escape_string($_REQUEST['feed_id']); + $is_cat = $this->dbh->escape_string($_REQUEST['is_cat']) == "true"; + $mode = $this->dbh->escape_string($_REQUEST['mode']); - catchup_feed($this->link, $feed_id, $is_cat, false, $max_id); + catchup_feed($feed_id, $is_cat, false, false, $mode); print json_encode(array("message" => "UPDATE_COUNTERS")); } function quickAddCat() { - $cat = db_escape_string($_REQUEST["cat"]); + $cat = $this->dbh->escape_string($_REQUEST["cat"]); - add_feed_category($this->link, $cat); + add_feed_category($cat); - $result = db_query($this->link, "SELECT id FROM ttrss_feed_categories WHERE + $result = $this->dbh->query("SELECT id FROM ttrss_feed_categories WHERE title = '$cat' AND owner_uid = " . $_SESSION["uid"]); - if (db_num_rows($result) == 1) { - $id = db_fetch_result($result, 0, "id"); + if ($this->dbh->num_rows($result) == 1) { + $id = $this->dbh->fetch_result($result, 0, "id"); } else { $id = 0; } - print_feed_cat_select($this->link, "cat_id", $id); - } - - function regenFeedKey() { - $feed_id = db_escape_string($_REQUEST['id']); - $is_cat = db_escape_string($_REQUEST['is_cat']) == "true"; - - $new_key = $this->update_feed_access_key($this->link, $feed_id, $is_cat); - - print json_encode(array("link" => $new_key)); - } - - // Silent - function clearKeys() { - db_query($this->link, "DELETE FROM ttrss_access_keys WHERE - owner_uid = " . $_SESSION["uid"]); + print_feed_cat_select("cat_id", $id, ''); } // Silent function clearArticleKeys() { - db_query($this->link, "UPDATE ttrss_user_entries SET uuid = '' WHERE + $this->dbh->query("UPDATE ttrss_user_entries SET uuid = '' WHERE owner_uid = " . $_SESSION["uid"]); return; } - function verifyRegexp() { - $reg_exp = $_REQUEST["reg_exp"]; - - $status = @preg_match("/$reg_exp/i", "TEST") !== false; - - print json_encode(array("status" => $status)); - } - - /* function buttonPlugin() { - $pclass = "button_" . basename($_REQUEST['plugin']); - $method = $_REQUEST['plugin_method']; - - if (class_exists($pclass)) { - $plugin = new $pclass($this->link); - if (method_exists($plugin, $method)) { - return $plugin->$method(); - } - } - } */ - - function genHash() { - $hash = sha1(uniqid(rand(), true)); - - print json_encode(array("hash" => $hash)); - } - - function batchAddFeeds() { - $cat_id = db_escape_string($_REQUEST['cat']); - $feeds = explode("\n", db_escape_string($_REQUEST['feeds'])); - $login = db_escape_string($_REQUEST['login']); - $pass = db_escape_string($_REQUEST['pass']); - $need_auth = db_escape_string($_REQUEST['need_auth']) != ""; - - foreach ($feeds as $feed) { - $feed = trim($feed); - - if (validate_feed_url($feed)) { - - db_query($this->link, "BEGIN"); - - if ($cat_id == "0" || !$cat_id) { - $cat_qpart = "NULL"; - } else { - $cat_qpart = "'$cat_id'"; - } - - $result = db_query($this->link, - "SELECT id FROM ttrss_feeds - WHERE feed_url = '$feed' AND owner_uid = ".$_SESSION["uid"]); - - if (db_num_rows($result) == 0) { - $result = db_query($this->link, - "INSERT INTO ttrss_feeds - (owner_uid,feed_url,title,cat_id,auth_login,auth_pass,update_method) - VALUES ('".$_SESSION["uid"]."', '$feed', - '[Unknown]', $cat_qpart, '$login', '$pass', 0)"); - } - - db_query($this->link, "COMMIT"); - } - } - } - - function setScore() { - $ids = db_escape_string($_REQUEST['id']); - $score = (int)db_escape_string($_REQUEST['score']); - - db_query($this->link, "UPDATE ttrss_user_entries SET - score = '$score' WHERE ref_id IN ($ids) AND owner_uid = " . $_SESSION["uid"]); - - print json_encode(array("id" => $id, - "score_pic" => theme_image($link, get_score_pic($score)))); - } - function setpanelmode() { $wide = (int) $_REQUEST["wide"]; setcookie("ttrss_widescreen", $wide, - time() + SESSION_COOKIE_LIFETIME); + time() + COOKIE_LIFETIME_LONG); print json_encode(array("wide" => $wide)); } @@ -705,7 +516,7 @@ class RPC extends Handler_Protected { $random_qpart = sql_random_function(); // We search for feed needing update. - $result = db_query($this->link, "SELECT ttrss_feeds.feed_url,ttrss_feeds.id + $result = $this->dbh->query("SELECT ttrss_feeds.feed_url,ttrss_feeds.id FROM ttrss_feeds, ttrss_users, ttrss_user_prefs WHERE @@ -724,17 +535,21 @@ class RPC extends Handler_Protected { $tstart = time(); - while ($line = db_fetch_assoc($result)) { + while ($line = $this->dbh->fetch_assoc($result)) { $feed_id = $line["id"]; if (time() - $tstart < ini_get("max_execution_time") * 0.7) { - update_rss_feed($this->link, $feed_id, true); + update_rss_feed($feed_id, true); ++$num_updated; } else { break; } } + // Purge orphans and cleanup tags + purge_orphans(); + cleanup_tags(14, 50000); + if ($num_updated > 0) { print json_encode(array("message" => "UPDATE_COUNTERS", "num_updated" => $num_updated)); @@ -744,30 +559,7 @@ class RPC extends Handler_Protected { } - function update_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) { - if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - - $sql_is_cat = bool_to_sql_bool($is_cat); - - $result = db_query($link, "SELECT access_key FROM ttrss_access_keys - WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat - AND owner_uid = " . $owner_uid); - - if (db_num_rows($result) == 1) { - $key = db_escape_string(sha1(uniqid(rand(), true))); - - db_query($link, "UPDATE ttrss_access_keys SET access_key = '$key' - WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat - AND owner_uid = " . $owner_uid); - - return $key; - - } else { - return get_feed_access_key($link, $feed_id, $is_cat, $owner_uid); - } - } - - private function markArticlesById($link, $ids, $cmode) { + private function markArticlesById($ids, $cmode) { $tmp_ids = array(); @@ -778,21 +570,21 @@ class RPC extends Handler_Protected { $ids_qpart = join(" OR ", $tmp_ids); if ($cmode == 0) { - db_query($link, "UPDATE ttrss_user_entries SET - marked = false,last_read = NOW() + $this->dbh->query("UPDATE ttrss_user_entries SET + marked = false, last_marked = NOW() WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); } else if ($cmode == 1) { - db_query($link, "UPDATE ttrss_user_entries SET - marked = true + $this->dbh->query("UPDATE ttrss_user_entries SET + marked = true, last_marked = NOW() WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); } else { - db_query($link, "UPDATE ttrss_user_entries SET - marked = NOT marked,last_read = NOW() + $this->dbh->query("UPDATE ttrss_user_entries SET + marked = NOT marked,last_marked = NOW() WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); } } - private function publishArticlesById($link, $ids, $cmode) { + private function publishArticlesById($ids, $cmode) { $tmp_ids = array(); @@ -803,23 +595,23 @@ class RPC extends Handler_Protected { $ids_qpart = join(" OR ", $tmp_ids); if ($cmode == 0) { - db_query($link, "UPDATE ttrss_user_entries SET - published = false,last_read = NOW() + $this->dbh->query("UPDATE ttrss_user_entries SET + published = false,last_published = NOW() WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); } else if ($cmode == 1) { - db_query($link, "UPDATE ttrss_user_entries SET - published = true,last_read = NOW() + $this->dbh->query("UPDATE ttrss_user_entries SET + published = true,last_published = NOW() WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); } else { - db_query($link, "UPDATE ttrss_user_entries SET - published = NOT published,last_read = NOW() + $this->dbh->query("UPDATE ttrss_user_entries SET + published = NOT published,last_published = NOW() WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); } if (PUBSUBHUBBUB_HUB) { $rss_link = get_self_url_prefix() . "/public.php?op=rss&id=-2&key=" . - get_feed_access_key($link, -2, false); + get_feed_access_key(-2, false); $p = new Publisher(PUBSUBHUBBUB_HUB); @@ -827,5 +619,32 @@ class RPC extends Handler_Protected { } } + function getlinktitlebyid() { + $id = $this->dbh->escape_string($_REQUEST['id']); + + $result = $this->dbh->query("SELECT link, title FROM ttrss_entries, ttrss_user_entries + WHERE ref_id = '$id' AND ref_id = id AND owner_uid = ". $_SESSION["uid"]); + + if ($this->dbh->num_rows($result) != 0) { + $link = $this->dbh->fetch_result($result, 0, "link"); + $title = $this->dbh->fetch_result($result, 0, "title"); + + echo json_encode(array("link" => $link, "title" => $title)); + } else { + echo json_encode(array("error" => "ARTICLE_NOT_FOUND")); + } + } + + function log() { + $logmsg = $this->dbh->escape_string($_REQUEST['logmsg']); + + if ($logmsg) { + Logger::get()->log_error(E_USER_WARNING, + $logmsg, '[client-js]', 0, false); + } + + echo json_encode(array("message" => "HOST_ERROR_LOGGED")); + + } } ?> diff --git a/classes/sanitizedummy.php b/classes/sanitizedummy.php deleted file mode 100644 index 7182fb7f5..000000000 --- a/classes/sanitizedummy.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php -class SanitizeDummy extends SimplePie_Sanitize { - function sanitize($data, $type, $base = '') { - return $data; - } -} -?> diff --git a/classes/ttrssmailer.php b/classes/ttrssmailer.php new file mode 100644 index 000000000..1e8d07723 --- /dev/null +++ b/classes/ttrssmailer.php @@ -0,0 +1,62 @@ +<?php
+/* @class ttrssMailer
+* @brief A TTRSS extension to the PHPMailer class
+* Configures default values through the __construct() function
+* @author Derek Murawsky
+* @version .1 (alpha)
+*
+*/
+require_once 'lib/phpmailer/class.phpmailer.php';
+require_once "config.php";
+
+class ttrssMailer extends PHPMailer {
+
+ //define all items that we want to override with defaults in PHPMailer
+ public $From = SMTP_FROM_ADDRESS;
+ public $FromName = SMTP_FROM_NAME;
+ public $CharSet = "UTF-8";
+ public $PluginDir = "lib/phpmailer/";
+ public $ContentType = "text/html"; //default email type is HTML
+
+ function __construct() {
+ $this->SetLanguage("en", "lib/phpmailer/language/");
+
+ if (SMTP_SERVER) {
+ $pair = explode(":", SMTP_SERVER, 2);
+ $this->Mailer = "smtp";
+
+ $this->Host = $pair[0];
+ $this->Port = $pair[1];
+
+ if (!$this->Port) $this->Port = 25;
+ } else {
+ $this->Host = '';
+ $this->Port = '';
+ }
+
+
+ //if SMTP_LOGIN is specified, set credentials and enable auth
+ if(SMTP_LOGIN){
+ $this->SMTPAuth = true;
+ $this->Username = SMTP_LOGIN;
+ $this->Password = SMTP_PASSWORD;
+ }
+ }
+ /* @brief a simple mail function to send email using the defaults
+ * This will send an HTML email using the configured defaults
+ * @param $toAddress A string with the recipients email address
+ * @param $toName A string with the recipients name
+ * @param $subject A string with the emails subject
+ * @param $body A string containing the body of the email
+ */
+ public function quickMail ($toAddress, $toName, $subject, $body, $altbody=""){
+ $this->addAddress($toAddress, $toName);
+ $this->Subject = $subject;
+ $this->Body = $body;
+ $this->IsHTML($altbody != '');
+ $rc=$this->send();
+ return $rc;
+ }
+}
+
+?>
|