From 910592b49a86273a30150bfdb67d9b49409e32f2 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 2 Aug 2013 14:04:14 +0400 Subject: [PATCH] add plugin to cache images in starred articles; pass article_id to sanitize --- classes/feeds.php | 2 +- classes/handler/public.php | 5 + include/functions.php | 6 +- plugins/cache_starred_images/init.php | 168 ++++++++++++++++++++++++++ 4 files changed, 177 insertions(+), 4 deletions(-) create mode 100644 plugins/cache_starred_images/init.php diff --git a/classes/feeds.php b/classes/feeds.php index 9aa75bbe8..3b765d6bd 100644 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -510,7 +510,7 @@ class Feeds extends Handler_Protected { $tags = false; $line["content"] = sanitize($line["content"], - sql_bool_to_bool($line['hide_images']), false, $entry_site_url, $highlight_words); + sql_bool_to_bool($line['hide_images']), false, $entry_site_url, $highlight_words, $line["id"]); foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE_CDM) as $p) { $line = $p->hook_render_article_cdm($line); diff --git a/classes/handler/public.php b/classes/handler/public.php index de0ab66f8..9c0359507 100644 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -390,6 +390,11 @@ class Handler_Public extends Handler { } } + function updateTask() { + + PluginHost::getInstance()->run_hooks(PluginHost::HOOK_UPDATE_TASK, "hook_update_task", $op); + } + function globalUpdateFeeds() { RPC::updaterandomfeed_real($this->dbh); diff --git a/include/functions.php b/include/functions.php index 48111e409..56361472e 100644 --- a/include/functions.php +++ b/include/functions.php @@ -2760,7 +2760,7 @@ } - function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false, $highlight_words = false) { + function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false, $highlight_words = false, $article_id = false) { if (!$owner) $owner = $_SESSION["uid"]; $res = trim($str); if (!$res) return ''; @@ -2846,7 +2846,7 @@ $disallowed_attributes = array('id', 'style', 'class'); foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SANITIZE) as $plugin) { - $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes); + $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes, $article_id); if (is_array($retval)) { $doc = $retval[0]; $allowed_elements = $retval[1]; @@ -3183,7 +3183,7 @@ $line["content"] = sanitize($line["content"], sql_bool_to_bool($line['hide_images']), - $owner_uid, $line["site_url"]); + $owner_uid, $line["site_url"], false, $line["id"]); foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE) as $p) { $line = $p->hook_render_article($line); diff --git a/plugins/cache_starred_images/init.php b/plugins/cache_starred_images/init.php new file mode 100644 index 000000000..2366a1e2b --- /dev/null +++ b/plugins/cache_starred_images/init.php @@ -0,0 +1,168 @@ +host = $host; + + $this->cache_dir = CACHE_DIR . "/starred-images/"; + + if (!is_dir($this->cache_dir)) { + mkdir($this->cache_dir); + } + + if (is_dir($this->cache_dir)) { + chmod($this->cache_dir, 0777); + + if (is_writable($this->cache_dir)) { + $host->add_hook($host::HOOK_UPDATE_TASK, $this); + $host->add_hook($host::HOOK_SANITIZE, $this); + } else { + user_error("Starred cache directory is not writable.", E_USER_WARNING); + } + + } else { + user_error("Unable to create starred cache directory.", E_USER_WARNING); + } + } + + function image() { + $hash = basename($_REQUEST["hash"]); + + if ($hash) { + + $filename = $this->cache_dir . "/" . $hash . '.png'; + + if (file_exists($filename)) { + /* See if we can use X-Sendfile */ + $xsendfile = false; + if (function_exists('apache_get_modules') && + array_search('mod_xsendfile', apache_get_modules())) + $xsendfile = true; + + if ($xsendfile) { + header("X-Sendfile: $filename"); + header("Content-type: application/octet-stream"); + header('Content-Disposition: attachment; filename="' . basename($filename) . '"'); + } else { + header("Content-type: image/png"); + $stamp = gmdate("D, d M Y H:i:s", filemtime($filename)). " GMT"; + header("Last-Modified: $stamp", true); + ob_clean(); // discard any data in the output buffer (if possible) + flush(); // flush headers (if possible) + readfile($filename); + } + } else { + header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); + echo "File not found."; + } + } + } + + function hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes, $article_id) { + $xpath = new DOMXpath($doc); + + if ($article_id) { + $entries = $xpath->query('(//img[@src])'); + + foreach ($entries as $entry) { + if ($entry->hasAttribute('src')) { + $src = rewrite_relative_url($site_url, $entry->getAttribute('src')); + + $local_filename = $this->cache_dir . $article_id . "-" . sha1($src) . ".png"; + + if (file_exists($local_filename)) { + $entry->setAttribute("src", get_self_url_prefix() . + "/backend.php?op=pluginhandler&plugin=cache_starred_images&method=image&hash=" . + $article_id . "-" . sha1($src)); + } + + } + } + } + + return $doc; + } + + function hook_update_task() { + header("Content-type: text/plain"); + + $result = db_query("SELECT content, ttrss_user_entries.owner_uid, link, site_url, ttrss_entries.id, plugin_data + FROM ttrss_entries, ttrss_user_entries LEFT JOIN ttrss_feeds ON + (ttrss_user_entries.feed_id = ttrss_feeds.id) + WHERE ref_id = ttrss_entries.id AND + marked = true AND + UPPER(content) LIKE '%cache_article_images($line["content"], $line["site_url"], $line["owner_uid"], $line["id"]); + + if ($success) { + $plugin_data = db_escape_string("starred_cache_images,${line['owner_uid']}:" . $line["plugin_data"]); + + db_query("UPDATE ttrss_entries SET plugin_data = '$plugin_data' WHERE id = " . $line["id"]); + } + } + } + } + + function cache_article_images($content, $site_url, $owner_uid, $article_id) { + libxml_use_internal_errors(true); + + $charset_hack = ' + + '; + + $doc = new DOMDocument(); + $doc->loadHTML($charset_hack . $content); + $xpath = new DOMXPath($doc); + + $entries = $xpath->query('(//img[@src])'); + + $success = false; + $has_images = false; + + foreach ($entries as $entry) { + if ($entry->hasAttribute('src')) { + $has_images = true; + $src = rewrite_relative_url($site_url, $entry->getAttribute('src')); + + $local_filename = $this->cache_dir . $article_id . "-" . sha1($src) . ".png"; + + //_debug("cache_images: downloading: $src to $local_filename"); + + if (!file_exists($local_filename)) { + $file_content = fetch_file_contents($src); + + if ($file_content && strlen($file_content) > 0) { + file_put_contents($local_filename, $file_content); + $success = true; + } + } else { + $success = true; + } + } + } + + return $success || !$has_images; + } + + function api_version() { + return 2; + } +} +?>