From 743c1b740bd8ec0b70f7058d4b52101b346a5e9c Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 16 Jan 2019 22:26:43 +0100 Subject: Export labels (#2217) * Export labels https://github.com/FreshRSS/FreshRSS/issues/2196 * Small fixes * Backport code from 1.14.0 https://github.com/FreshRSS/FreshRSS/pull/2199/commits/4888f919f104b2d170302565e481a0b731eb4145 * More fixes --- app/Controllers/importExportController.php | 36 +++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) (limited to 'app/Controllers/importExportController.php') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 2d8d4e01d..3f2947c7a 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -650,7 +650,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { return $return; } - public function exportFile($export_opml = true, $export_starred = false, $export_feeds = array(), $maxFeedEntries = 50, $username = null) { + public function exportFile($export_opml = true, $export_starred = false, $export_labelled = false, $export_feeds = array(), $maxFeedEntries = 50, $username = null) { require_once(LIB_PATH . '/lib_opml.php'); $this->catDAO = new FreshRSS_CategoryDAO($username); @@ -674,8 +674,11 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $export_files["feeds_${day}.opml.xml"] = $this->generateOpml(); } - if ($export_starred) { - $export_files["starred_${day}.json"] = $this->generateEntries('starred'); + if ($export_starred || $export_labelled) { + $export_files["starred_${day}.json"] = $this->generateEntries( + ($export_starred ? 'S' : '') . + ($export_labelled ? 'T' : '') + ); } foreach ($export_feeds as $feed_id) { @@ -683,7 +686,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if ($feed) { $filename = "feed_${day}_" . $feed->category() . '_' . $feed->id() . '.json'; - $export_files[$filename] = $this->generateEntries('feed', $feed, $maxFeedEntries); + $export_files[$filename] = $this->generateEntries('f', $feed, $maxFeedEntries); } } @@ -725,6 +728,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $nb_files = $this->exportFile( Minz_Request::param('export_opml', false), Minz_Request::param('export_starred', false), + Minz_Request::param('export_labelled', false), Minz_Request::param('export_feeds', array()) ); } catch (FreshRSS_ZipMissing_Exception $zme) { @@ -758,27 +762,29 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { /** * This method returns a JSON file content. * - * @param string $type must be "starred" or "feed" + * @param string $type must be one of: + * 'S' (starred/favourite), 'f' (feed), 'T' (taggued/labelled), 'ST' (starred or labelled) * @param FreshRSS_Feed $feed feed of which we want to get entries. * @return string the JSON file content. */ private function generateEntries($type, $feed = null, $maxFeedEntries = 50) { $this->view->categories = $this->catDAO->listCategories(); + $tagDAO = FreshRSS_Factory::createTagDao(); - if ($type == 'starred') { + if ($type === 's' || $type === 'S' || $type === 'T' || $type === 'ST') { $this->view->list_title = _t('sub.import_export.starred_list'); $this->view->type = 'starred'; - $unread_fav = $this->entryDAO->countUnreadReadFavorites(); - $this->view->entriesRaw = $this->entryDAO->listWhereRaw( - 's', '', FreshRSS_Entry::STATE_ALL, 'ASC', $unread_fav['all'] - ); - } elseif ($type === 'feed' && $feed != null) { + $this->view->entriesId = $this->entryDAO->listIdsWhere($type, '', FreshRSS_Entry::STATE_ALL, 'ASC', -1); + $this->view->entryIdsTagNames = $tagDAO->getEntryIdsTagNames($this->view->entriesId); + //The following is a streamable query, i.e. must be last + $this->view->entriesRaw = $this->entryDAO->listWhereRaw($type, '', FreshRSS_Entry::STATE_ALL, 'ASC', -1); + } elseif ($type === 'f' && $feed != null) { $this->view->list_title = _t('sub.import_export.feed_list', $feed->name()); $this->view->type = 'feed/' . $feed->id(); - $this->view->entriesRaw = $this->entryDAO->listWhereRaw( - 'f', $feed->id(), FreshRSS_Entry::STATE_ALL, 'ASC', - $maxFeedEntries - ); + $this->view->entriesId = $this->entryDAO->listIdsWhere($type, $feed->id(), FreshRSS_Entry::STATE_ALL, 'ASC', $maxFeedEntries); + $this->view->entryIdsTagNames = $tagDAO->getEntryIdsTagNames($this->view->entriesId); + //The following is a streamable query, i.e. must be last + $this->view->entriesRaw = $this->entryDAO->listWhereRaw($type, $feed->id(), FreshRSS_Entry::STATE_ALL, 'ASC', $maxFeedEntries); $this->view->feed = $feed; } -- cgit v1.2.3 From f0684d701862d103fce834bad9e139f97544bc62 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 24 Jan 2019 19:54:34 +0100 Subject: Fix import labels (#2225) All labelled articles were wrongly marked as starred. --- app/Controllers/importExportController.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'app/Controllers/importExportController.php') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 3f2947c7a..96c5e42c2 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -41,7 +41,8 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $list_files = array( 'opml' => array(), 'json_starred' => array(), - 'json_feed' => array() + 'json_feed' => array(), + 'ttrss_starred' => array(), ); // We try to list all files according to their type @@ -505,7 +506,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $feed_id = $article_to_feed[$item['id']]; $author = isset($item['author']) ? $item['author'] : ''; - $is_starred = $starred; + $is_starred = false; $tags = $item['categories']; $labels = array(); for ($i = count($tags) - 1; $i >= 0; $i --) { @@ -522,6 +523,10 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { unset($tags[$i]); } } + if ($starred && !$is_starred) { + //If the article has no label, mark it as starred (old format) + $is_starred = empty($labels); + } $url = $item['alternate'][0]['href']; if (!empty($item['content']['content'])) { -- cgit v1.2.3 From f1ac6dd5509c6aa9e1d99401c5e1a0b894d4e7b0 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 24 Jan 2019 20:44:45 +0100 Subject: Export/import articles read/unread state (#2226) --- CHANGELOG.md | 1 + app/Controllers/importExportController.php | 10 +++++++++- app/views/helpers/export/articles.phtml | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) (limited to 'app/Controllers/importExportController.php') diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a0188431..3e3dfc302 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Features * Include articles with custom labels during export [#2196](https://github.com/FreshRSS/FreshRSS/issues/2196) + * Export/import articles read/unread state [#2226](https://github.com/FreshRSS/FreshRSS/pull/2226) * Bug fixing * Fix missing HTTP `X-Forwarded-Prefix` in cookie path behind a reverse-proxy [#2201](https://github.com/FreshRSS/FreshRSS/pull/2201) * Deployment diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 96c5e42c2..e11221610 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -436,7 +436,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { return false; } - $is_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0; + $mark_as_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0; $google_compliant = strpos($article_object['id'], 'com.google') !== false; @@ -507,6 +507,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $feed_id = $article_to_feed[$item['id']]; $author = isset($item['author']) ? $item['author'] : ''; $is_starred = false; + $is_read = null; $tags = $item['categories']; $labels = array(); for ($i = count($tags) - 1; $i >= 0; $i --) { @@ -514,6 +515,10 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if (strpos($tag, 'user/-/') !== false) { if ($tag === 'user/-/state/com.google/starred') { $is_starred = true; + } elseif ($tag === 'user/-/state/com.google/read') { + $is_read = true; + } elseif ($tag === 'user/-/state/com.google/unread') { + $is_read = false; } elseif (strpos($tag, 'user/-/label/') === 0) { $tag = trim(substr($tag, 13)); if ($tag != '') { @@ -527,6 +532,9 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { //If the article has no label, mark it as starred (old format) $is_starred = empty($labels); } + if ($is_read === null) { + $is_read = $mark_as_read; + } $url = $item['alternate'][0]['href']; if (!empty($item['content']['content'])) { diff --git a/app/views/helpers/export/articles.phtml b/app/views/helpers/export/articles.phtml index eb2cb0924..2d1fcd133 100644 --- a/app/views/helpers/export/articles.phtml +++ b/app/views/helpers/export/articles.phtml @@ -56,6 +56,7 @@ foreach ($this->entriesRaw as $entryRaw) { 'feedUrl' => $feed == null ? '' : $feed->url(), ) ); + $article['categories'][] = $entry->isRead() ? 'user/-/state/com.google/read' : 'user/-/state/com.google/unread'; if ($entry->isFavorite()) { $article['categories'][] = 'user/-/state/com.google/starred'; } -- cgit v1.2.3 From 7902d10b3adee3788d770b9a75ac63b910140b0c Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 26 Jan 2019 12:55:12 +0100 Subject: Import FeedBin (#2228) * Import FeedBin https://github.com/FreshRSS/FreshRSS/issues/2227 And more tolerant import * Mute import fake feed + Changelog * strtotime for published dates in string --- CHANGELOG.md | 1 + app/Controllers/importExportController.php | 59 +++++++++++++++++++++++++----- app/Models/FeedDAO.php | 5 ++- 3 files changed, 54 insertions(+), 11 deletions(-) (limited to 'app/Controllers/importExportController.php') diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e3dfc302..a32a1d43e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Features * Include articles with custom labels during export [#2196](https://github.com/FreshRSS/FreshRSS/issues/2196) * Export/import articles read/unread state [#2226](https://github.com/FreshRSS/FreshRSS/pull/2226) + * Import FeedBin, and more robust general import [#2228](https://github.com/FreshRSS/FreshRSS/pull/2228) * Bug fixing * Fix missing HTTP `X-Forwarded-Prefix` in cookie path behind a reverse-proxy [#2201](https://github.com/FreshRSS/FreshRSS/pull/2201) * Deployment diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index e11221610..80b9868ec 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -435,11 +435,10 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } return false; } + $items = isset($article_object['items']) ? $article_object['items'] : $article_object; $mark_as_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0; - $google_compliant = strpos($article_object['id'], 'com.google') !== false; - $error = false; $article_to_feed = array(); @@ -448,9 +447,23 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $limits = FreshRSS_Context::$system_conf->limits; // First, we check feeds of articles are in DB (and add them if needed). - foreach ($article_object['items'] as $item) { - $key = $google_compliant ? 'htmlUrl' : 'feedUrl'; - $feed = new FreshRSS_Feed($item['origin'][$key]); + foreach ($items as $item) { + if (!isset($item['origin'])) { + $item['origin'] = array('title' => 'Import'); + } + if (!empty($item['origin']['feedUrl'])) { + $feedUrl = $item['origin']['feedUrl']; + } elseif (!empty($item['origin']['streamId']) && strpos($item['origin']['streamId'], 'feed/') === 0) { + $feedUrl = substr($item['origin']['streamId'], 5); //Google Reader + $item['origin']['feedUrl'] = $feedUrl; + } elseif (!empty($item['origin']['htmlUrl'])) { + $feedUrl = $item['origin']['htmlUrl']; + } else { + $feedUrl = 'http://import.localhost/import.xml'; + $item['origin']['feedUrl'] = $feedUrl; + $item['origin']['disable'] = true; + } + $feed = new FreshRSS_Feed($feedUrl); $feed = $this->feedDAO->searchByUrl($feed->url()); if ($feed == null) { @@ -498,7 +511,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { // Then, articles are imported. $newGuids = array(); $this->entryDAO->beginTransaction(); - foreach ($article_object['items'] as $item) { + foreach ($items as $item) { if (empty($article_to_feed[$item['id']])) { // Related feed does not exist for this entry, do nothing. continue; @@ -508,7 +521,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $author = isset($item['author']) ? $item['author'] : ''; $is_starred = false; $is_read = null; - $tags = $item['categories']; + $tags = empty($item['categories']) ? array() : $item['categories']; $labels = array(); for ($i = count($tags) - 1; $i >= 0; $i --) { $tag = trim($tags[$i]); @@ -536,18 +549,41 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $is_read = $mark_as_read; } - $url = $item['alternate'][0]['href']; + if (isset($item['alternate'][0]['href'])) { + $url = $item['alternate'][0]['href']; + } elseif (isset($item['url'])) { + $url = $item['url']; //FeedBin + } else { + $url = ''; + } + if (!empty($item['content']['content'])) { $content = $item['content']['content']; } elseif (!empty($item['summary']['content'])) { $content = $item['summary']['content']; + } elseif (!empty($item['content'])) { + $content = $item['content']; //FeedBin + } else { + $content = ''; } $content = sanitizeHTML($content, $url); + if (!empty($item['published'])) { + $published = $item['published']; + } elseif (!empty($item['timestampUsec'])) { + $published = substr($item['timestampUsec'], 0, -6); + } elseif (!empty($item['updated'])) { + $published = $item['updated']; + } else { + $published = 0; + } + if (!ctype_digit('' . $published)) { + $published = strtotime($published); + } + $entry = new FreshRSS_Entry( $feed_id, $item['id'], $item['title'], $author, - $content, $url, - $item['published'], $is_read, $is_starred + $content, $url, $published, $is_read, $is_starred ); $entry->_id(min(time(), $entry->date(true)) . uSecString()); $entry->_tags($tags); @@ -639,6 +675,9 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $feed->_category(FreshRSS_CategoryDAO::DEFAULTCATEGORYID); $feed->_name($name); $feed->_website($website); + if (!empty($origin['disable'])) { + $feed->_ttl(-1 * FreshRSS_Context::$user_conf->ttl_default); + } // Call the extension hook $feed = Minz_ExtensionManager::callHook('feed_before_insert', $feed); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 7f00642f4..c9c9f6301 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -61,7 +61,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $valuesTmp['lastUpdate'], base64_encode($valuesTmp['httpAuth']), FreshRSS_Feed::KEEP_HISTORY_DEFAULT, - FreshRSS_Feed::TTL_DEFAULT, + isset($valuesTmp['ttl']) ? intval($valuesTmp['ttl']) : FreshRSS_Feed::TTL_DEFAULT, isset($valuesTmp['attributes']) ? json_encode($valuesTmp['attributes']) : '', ); @@ -95,6 +95,9 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { 'httpAuth' => $feed->httpAuth(), 'attributes' => $feed->attributes(), ); + if ($feed->mute() || $feed->ttl() != FreshRSS_Context::$user_conf->ttl_default) { + $values['ttl'] = $feed->ttl() * ($feed->mute() ? -1 : 1); + } $id = $this->addFeed($values); if ($id) { -- cgit v1.2.3