From 01f7dd98027463a66080bdba0e3082f2960a045a Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Sun, 28 Jul 2013 20:10:07 -0400 Subject: [PATCH] Download full content of articles (like Readability) --- assets/css/app.css | 14 +- assets/js/app.js | 66 +- index.php | 7 + locales/fr_FR/translations.php | 5 + model.php | 52 + templates/keyboard_shortcuts.php | 1 + templates/show_item.php | 17 +- vendor/PicoFeed/Filter.php | 15 +- vendor/Readability/JSLikeHTMLElement.php | 109 +++ vendor/Readability/Readability.php | 1137 ++++++++++++++++++++++ 10 files changed, 1402 insertions(+), 21 deletions(-) create mode 100755 vendor/Readability/JSLikeHTMLElement.php create mode 100755 vendor/Readability/Readability.php diff --git a/assets/css/app.css b/assets/css/app.css index 9ff16bf..2a1c35f 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -507,11 +507,21 @@ nav .active a { .infos { padding-bottom: 20px; - color: #ddd; + color: #ccc; } .item .infos a { - color: #ddd; + color: #ccc; +} + +.downloading img { + display: inline; + margin: 0; + padding: 0; +} + +.downloading { + color: #000; } #items-paging { diff --git a/assets/js/app.js b/assets/js/app.js index 5b021f4..119dc5b 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -5,13 +5,66 @@ var queue_length = 5; + function download_item() + { + // Change link container + var container = document.getElementById("download-item"); + if (! container) return; + + var item_id = container.getAttribute("data-item-id"); + var message = container.getAttribute("data-before-message"); + + var img = document.createElement("img"); + img.src = "assets/img/refresh.gif"; + + container.innerHTML = ""; + container.className = "downloading"; + container.appendChild(img); + container.appendChild(document.createTextNode(" " + message)); + + var request = new XMLHttpRequest(); + + request.onload = function() { + + var response = JSON.parse(request.responseText); + + if (response.result) { + + var content = document.getElementById("item-content"); + if (content) content.innerHTML = response.content; + + if (container) { + + var message = container.getAttribute("data-after-message"); + + container.innerHTML = ""; + container.appendChild(document.createTextNode(" " + message)); + } + } + else { + + if (container) { + + var message = container.getAttribute("data-failure-message"); + + container.innerHTML = ""; + container.appendChild(document.createTextNode(" " + message)); + } + } + }; + + request.open("POST", "?action=download-item&id=" + item_id, true); + request.send(); + } + + function switch_status(item_id, hide) { var request = new XMLHttpRequest(); - request.onreadystatechange = function() { + request.onload = function() { - if (request.readyState === 4 && is_listing()) { + if (is_listing()) { var response = JSON.parse(request.responseText); @@ -100,7 +153,7 @@ if (container) { var img = document.createElement("img"); - img.src = "./assets/img/refresh.gif"; + img.src = "assets/img/refresh.gif"; container.appendChild(img); } @@ -444,6 +497,10 @@ var item_id = e.target.getAttribute("data-item-id"); mark_as_read(item_id); break; + case 'download-item': + e.preventDefault(); + download_item(); + break; } } }; @@ -451,6 +508,9 @@ document.onkeypress = function(e) { switch (e.keyCode || e.which) { + case 100: // d + download_item(); + break; case 112: // p case 107: // k open_previous_item(); diff --git a/index.php b/index.php index 293d3f2..c58811b 100644 --- a/index.php +++ b/index.php @@ -162,6 +162,13 @@ Router\get_action('mark-item-removed', function() { }); +// Ajax call to download an item (fetch the full content from the original website) +Router\post_action('download-item', function() { + + Response\json(Model\download_item(Request\param('id'))); +}); + + // Ajax call to mark item read Router\post_action('mark-item-read', function() { diff --git a/locales/fr_FR/translations.php b/locales/fr_FR/translations.php index f46fd6a..8bd31c8 100644 --- a/locales/fr_FR/translations.php +++ b/locales/fr_FR/translations.php @@ -1,6 +1,11 @@ 'contenu téléchargé', + 'in progress...' => 'en cours...', + 'unable to fetch content' => 'impossible de récupérer l\'article', + 'Download content' => 'Télécharger le contenu', + 'download content' => 'télécharger le contenu', 'Help' => 'Aide', 'Theme' => 'Thème', 'No item' => 'Aucun élément', diff --git a/model.php b/model.php index 580aa95..33d709c 100644 --- a/model.php +++ b/model.php @@ -2,6 +2,9 @@ namespace Model; +require_once 'vendor/PicoFeed/Encoding.php'; +require_once 'vendor/PicoFeed/Filter.php'; +require_once 'vendor/PicoFeed/Client.php'; require_once 'vendor/PicoFeed/Export.php'; require_once 'vendor/PicoFeed/Import.php'; require_once 'vendor/PicoFeed/Reader.php'; @@ -310,6 +313,55 @@ function update_feed_cache_infos($feed_id, $last_modified, $etag) } +function download_item($item_id) +{ + require_once 'vendor/Readability/Readability.php'; + + $item = get_item($item_id); + + $client = \PicoFeed\Client::create(); + $client->url = $item['url']; + $client->timeout = HTTP_TIMEOUT; + $client->user_agent = HTTP_USERAGENT; + $client->execute(); + + $content = $client->getContent(); + + if (! empty($content)) { + + $content = \PicoFeed\Encoding::toUTF8($content); + + $readability = new \Readability($content, $item['url']); + + if ($readability->init()) { + + // Get relevant content + $content = $readability->getContent()->innerHTML; + + // Filter content + $filter = new \PicoFeed\Filter($content, $item['url']); + $content = $filter->execute(); + + // Save content + \PicoTools\singleton('db') + ->table('items') + ->eq('id', $item['id']) + ->save(array('content' => $content)); + + return array( + 'result' => true, + 'content' => $content + ); + } + } + + return array( + 'result' => false, + 'content' => '' + ); +} + + function remove_feed($feed_id) { // Items are removed by a sql constraint diff --git a/templates/keyboard_shortcuts.php b/templates/keyboard_shortcuts.php index 7b29fc9..856cf2c 100644 --- a/templates/keyboard_shortcuts.php +++ b/templates/keyboard_shortcuts.php @@ -1,6 +1,7 @@