dbh->escape_string($_REQUEST['id']); $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 ($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"); return; } else { print_error(__("Article not found.")); } } function view() { $id = $this->dbh->escape_string($_REQUEST["id"]); $cids = explode(",", $this->dbh->escape_string($_REQUEST["cids"])); $mode = $this->dbh->escape_string($_REQUEST["mode"]); // in prefetch mode we only output requested cids, main article // just gets marked as read (it already exists in client cache) $articles = array(); if ($mode == "") { array_push($articles, $this->format_article($id, false)); } else if ($mode == "zoom") { array_push($articles, $this->format_article($id, true, true)); } else if ($mode == "raw") { if (isset($_REQUEST['html'])) { header("Content-Type: text/html"); print ''; } $article = $this->format_article($id, false, isset($_REQUEST["zoom"])); print $article['content']; return; } $this->catchupArticleById($id, 0); if (!$_SESSION["bw_limit"]) { foreach ($cids as $cid) { if ($cid) { array_push($articles, $this->format_article($cid, false, false)); } } } print json_encode($articles); } private function catchupArticleById($id, $cmode) { if ($cmode == 0) { $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) { $this->dbh->query("UPDATE ttrss_user_entries SET unread = true WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); } else { $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 = $this->getArticleFeed($id); ccache_update($feed_id, $_SESSION["uid"]); } static function create_published_article($title, $url, $content, $labels_str, $owner_uid) { $guid = 'SHA1:' . sha1("ttshared:" . $url . $owner_uid); // include owner_uid to prevent global GUID clash if (!$content) { $pluginhost = new PluginHost(); $pluginhost->load_all(PluginHost::KIND_ALL, $owner_uid); $pluginhost->load_data(); $af_readability = $pluginhost->get_plugin("Af_Readability"); if ($af_readability) { $enable_share_anything = $pluginhost->get($af_readability, "enable_share_anything"); if ($enable_share_anything) { $extracted_content = $af_readability->extract_content($url); if ($extracted_content) $content = db_escape_string($extracted_content); } } } $content_hash = sha1($content); if ($labels_str != "") { $labels = explode(",", $labels_str); } else { $labels = array(); } $rc = false; if (!$title) $title = $url; if (!$title && !$url) return false; if (filter_var($url, FILTER_VALIDATE_URL) === FALSE) return false; db_query("BEGIN"); // only check for our user data here, others might have shared this with different content etc $result = db_query("SELECT id FROM ttrss_entries, ttrss_user_entries WHERE guid = '$guid' 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("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("UPDATE ttrss_entries SET content = '$content', content_hash = '$content_hash' WHERE id = '$ref_id'"); 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("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, NOW())"); } if (count($labels) != 0) { foreach ($labels as $label) { label_add_article($ref_id, trim($label), $owner_uid); } } $rc = true; } else { $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("SELECT id FROM ttrss_entries WHERE guid = '$guid'"); if (db_num_rows($result) != 0) { $ref_id = db_fetch_result($result, 0, "id"); 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, NOW())"); if (count($labels) != 0) { foreach ($labels as $label) { label_add_article($ref_id, trim($label), $owner_uid); } } $rc = true; } } db_query("COMMIT"); return $rc; } function editArticleTags() { print __("Tags for this article (separated by commas):")."
"; $param = $this->dbh->escape_string($_REQUEST['param']); $tags = Article::get_article_tags($this->dbh->escape_string($param)); $tags_str = join(", ", $tags); print_hidden("id", "$param"); print_hidden("op", "article"); print_hidden("method", "setArticleTags"); print "
"; print "
"; print "
"; print "
"; print " "; print ""; print "
"; } 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" => (int)$score, "score_pic" => get_score_pic($score))); } function getScore() { $id = $this->dbh->escape_string($_REQUEST['id']); $result = $this->dbh->query("SELECT score FROM ttrss_user_entries WHERE ref_id = $id AND owner_uid = " . $_SESSION["uid"]); $score = $this->dbh->fetch_result($result, 0, "score"); print json_encode(array("id" => $id, "score" => (int)$score, "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 ""; 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 = Article::get_article_tags($id); $tags_str = $this->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 ""; } 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" => $this->format_article_labels($labels))); } } $reply["message"] = "UPDATE_COUNTERS"; print json_encode($reply); } function getArticleFeed($id) { $result = db_query("SELECT feed_id FROM ttrss_user_entries WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); if (db_num_rows($result) != 0) { return db_fetch_result($result, 0, "feed_id"); } else { return 0; } } static function format_article_enclosures($id, $always_display_enclosures, $article_content, $hide_images = false) { $result = Article::get_article_enclosures($id); $rv = ''; foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_FORMAT_ENCLOSURES) as $plugin) { $retval = $plugin->hook_format_enclosures($rv, $result, $id, $always_display_enclosures, $article_content, $hide_images); if (is_array($retval)) { $rv = $retval[0]; $result = $retval[1]; } else { $rv = $retval; } } unset($retval); // Unset to prevent breaking render if there are no HOOK_RENDER_ENCLOSURE hooks below. if ($rv === '' && !empty($result)) { $entries_html = array(); $entries = array(); $entries_inline = array(); foreach ($result as $line) { foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ENCLOSURE_ENTRY) as $plugin) { $line = $plugin->hook_enclosure_entry($line); } $url = $line["content_url"]; $ctype = $line["content_type"]; $title = $line["title"]; $width = $line["width"]; $height = $line["height"]; if (!$ctype) $ctype = __("unknown type"); //$filename = substr($url, strrpos($url, "/")+1); $filename = basename($url); $player = format_inline_player($url, $ctype); if ($player) array_push($entries_inline, $player); # $entry .= " " . # $filename . " (" . $ctype . ")" . ""; $entry = "
$filename ($ctype)
"; array_push($entries_html, $entry); $entry = array(); $entry["type"] = $ctype; $entry["filename"] = $filename; $entry["url"] = $url; $entry["title"] = $title; $entry["width"] = $width; $entry["height"] = $height; array_push($entries, $entry); } if ($_SESSION['uid'] && !get_pref("STRIP_IMAGES") && !$_SESSION["bw_limit"]) { if ($always_display_enclosures || !preg_match("/get_hooks(PluginHost::HOOK_RENDER_ENCLOSURE) as $plugin) $retval = $plugin->hook_render_enclosure($entry, $hide_images); if ($retval) { $rv .= $retval; } else { if (preg_match("/image/", $entry["type"])) { if (!$hide_images) { $encsize = ''; if ($entry['height'] > 0) $encsize .= ' height="' . intval($entry['height']) . '"'; if ($entry['width'] > 0) $encsize .= ' width="' . intval($entry['width']) . '"'; $rv .= "

\"".htmlspecialchars($entry["filename"])."\"

"; } else { $rv .= "

" .htmlspecialchars($entry["url"]) . "

"; } if ($entry['title']) { $rv.= "
${entry['title']}
"; } } } } } } if (count($entries_inline) > 0) { $rv .= "
"; foreach ($entries_inline as $entry) { $rv .= $entry; }; $rv .= "
"; } $rv .= "
". "" . __('Attachments').""; $rv .= "
"; foreach ($entries as $entry) { if ($entry["title"]) $title = " — " . truncate_string($entry["title"], 30); else $title = ""; if ($entry["filename"]) $filename = truncate_middle(htmlspecialchars($entry["filename"]), 60); else $filename = ""; $rv .= "
".$filename . $title."
"; }; $rv .= "
"; $rv .= "
"; } return $rv; } static function format_article($id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; $rv = array(); $rv['id'] = $id; /* we can figure out feed_id from article id anyway, why do we * pass feed_id here? let's ignore the argument :(*/ $result = db_query("SELECT feed_id FROM ttrss_user_entries WHERE ref_id = '$id'"); $feed_id = (int) db_fetch_result($result, 0, "feed_id"); $rv['feed_id'] = $feed_id; //if (!$zoom_mode) { print "
get_hooks(PluginHost::HOOK_RENDER_ARTICLE) as $p) { $line = $p->hook_render_article($line); } $num_comments = (int) $line["num_comments"]; $entry_comments = ""; if ($num_comments > 0) { if ($line["comments"]) { $comments_url = htmlspecialchars($line["comments"]); } else { $comments_url = htmlspecialchars($line["link"]); } $entry_comments = "$num_comments ". _ngettext("comment", "comments", $num_comments).""; } else { if ($line["comments"] && $line["link"] != $line["comments"]) { $entry_comments = "".__("comments").""; } } if ($zoom_mode) { header("Content-Type: text/html"); $rv['content'] .= " Tiny Tiny RSS - ".$line["title"]."". stylesheet_tag("css/tt-rss.css"). stylesheet_tag("css/zoom.css"). stylesheet_tag("css/dijit.css")." "; } $rv['content'] .= "
"; $rv['content'] .= "
"; $entry_author = $line["author"]; if ($entry_author) { $entry_author = __(" - ") . $entry_author; } $parsed_updated = make_local_datetime($line["updated"], true, $owner_uid, true); if (!$zoom_mode) $rv['content'] .= "
$parsed_updated
"; if ($line["link"]) { $rv['content'] .= "
" . $line["title"] . "" . "$entry_author
"; } else { $rv['content'] .= "
" . $line["title"] . "$entry_author
"; } if ($zoom_mode) { $feed_title = htmlspecialchars($line["feed_title"]); $rv['content'] .= "
$feed_title
"; $rv['content'] .= "
$parsed_updated
"; } $tags_str = Article::format_tags_string($line["tags"], $id); $tags_str_full = join(", ", $line["tags"]); if (!$tags_str_full) $tags_str_full = __("no tags"); if (!$entry_comments) $entry_comments = " "; # placeholder $rv['content'] .= ""; $rv['content'] .= "
"; foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_LEFT_BUTTON) as $p) { $rv['content'] .= $p->hook_article_left_button($line); } $rv['content'] .= "$entry_comments
"; if ($line["orig_feed_id"]) { $tmp_result = db_query("SELECT * FROM ttrss_archived_feeds WHERE id = ".$line["orig_feed_id"] . " AND owner_uid = " . $_SESSION["uid"]); if (db_num_rows($tmp_result) != 0) { $rv['content'] .= "
"; $rv['content'] .= __("Originally from:"); $rv['content'] .= " "; $tmp_line = db_fetch_assoc($tmp_result); $rv['content'] .= "" . $tmp_line['title'] . ""; $rv['content'] .= " "; $rv['content'] .= ""; $rv['content'] .= ""; $rv['content'] .= "
"; } } $rv['content'] .= "
"; $rv['content'] .= "
"; if ($line['note']) { $rv['content'] .= Article::format_article_note($id, $line['note'], !$zoom_mode); } $rv['content'] .= "
"; if (!$line['lang']) $line['lang'] = 'en'; $rv['content'] .= "
"; $rv['content'] .= $line["content"]; if (!$zoom_mode) { $rv['content'] .= Article::format_article_enclosures($id, sql_bool_to_bool($line["always_display_enclosures"]), $line["content"], sql_bool_to_bool($line["hide_images"])); } $rv['content'] .= "
"; $rv['content'] .= "
"; } if ($zoom_mode) { $rv['content'] .= " "; $rv['content'] .= ""; } return $rv; } static function get_article_tags($id, $owner_uid = 0, $tag_cache = false) { $a_id = db_escape_string($id); if (!$owner_uid) $owner_uid = $_SESSION["uid"]; $query = "SELECT DISTINCT tag_name, owner_uid as owner FROM ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name"; $tags = array(); /* check cache first */ if ($tag_cache === false) { $result = db_query("SELECT tag_cache FROM ttrss_user_entries WHERE ref_id = '$id' AND owner_uid = $owner_uid"); if (db_num_rows($result) != 0) $tag_cache = db_fetch_result($result, 0, "tag_cache"); } if ($tag_cache) { $tags = explode(",", $tag_cache); } else { /* do it the hard way */ $tmp_result = db_query($query); while ($tmp_line = db_fetch_assoc($tmp_result)) { array_push($tags, $tmp_line["tag_name"]); } /* update the cache */ $tags_str = db_escape_string(join(",", $tags)); db_query("UPDATE ttrss_user_entries SET tag_cache = '$tags_str' WHERE ref_id = '$id' AND owner_uid = $owner_uid"); } return $tags; } static function format_tags_string($tags) { if (!is_array($tags) || count($tags) == 0) { return __("no tags"); } else { $maxtags = min(5, count($tags)); $tags_str = ""; for ($i = 0; $i < $maxtags; $i++) { $tags_str .= "" . $tags[$i] . ", "; } $tags_str = mb_substr($tags_str, 0, mb_strlen($tags_str)-2); if (count($tags) > $maxtags) $tags_str .= ", …"; return $tags_str; } } static function format_article_labels($labels) { if (!is_array($labels)) return ''; $labels_str = ""; foreach ($labels as $l) { $labels_str .= sprintf("%s", $l[2], $l[3], $l[1]); } return $labels_str; } static function format_article_note($id, $note, $allow_edit = true) { $str = "
". ($allow_edit ? __('(edit note)') : "")."
$note
"; return $str; } static function get_article_enclosures($id) { $query = "SELECT * FROM ttrss_enclosures WHERE post_id = '$id' AND content_url != ''"; $rv = array(); $result = db_query($query); if (db_num_rows($result) > 0) { while ($line = db_fetch_assoc($result)) { if (file_exists(CACHE_DIR . '/images/' . sha1($line["content_url"]))) { $line["content_url"] = get_self_url_prefix() . '/public.php?op=cached_url&hash=' . sha1($line["content_url"]); } array_push($rv, $line); } } return $rv; } static function purge_orphans($do_output = false) { // purge orphaned posts in main content table $result = db_query("DELETE FROM ttrss_entries WHERE NOT EXISTS (SELECT ref_id FROM ttrss_user_entries WHERE ref_id = id)"); if ($do_output) { $rows = db_affected_rows($result); _debug("Purged $rows orphaned posts."); } } }