From 9d87f2f0aa8306c3e07a79d1a100b4d41ea8bc72 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 27 Mar 2014 20:36:51 +0100 Subject: Export is fully implemented - Export list of feeds (OPML) - Export list of favourites (JSON) - Export list of articles per feed (JSON) --- app/Controllers/importExportController.php | 47 +++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 11 deletions(-) (limited to 'app/Controllers/importExportController.php') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index f697f4c9e..040cf3f7b 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -13,16 +13,16 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } public function indexAction() { - $catDAO = new FreshRSS_CategoryDAO (); - $this->view->categories = $catDAO->listCategories (); + $catDAO = new FreshRSS_CategoryDAO(); + $this->view->categories = $catDAO->listCategories(); - $feedDAO = new FreshRSS_FeedDAO (); - $this->view->feeds = $feedDAO->listFeeds (); + $feedDAO = new FreshRSS_FeedDAO(); + $this->view->feeds = $feedDAO->listFeeds(); // au niveau de la vue, permet de ne pas voir un flux sélectionné dans la liste $this->view->flux = false; - Minz_View::prependTitle (Minz_Translate::t ('import_export') . ' · '); + Minz_View::prependTitle(Minz_Translate::t('import_export') . ' · '); } public function importAction() { @@ -62,7 +62,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $export_opml = Minz_Request::param('export_opml', false); $export_starred = Minz_Request::param('export_starred', false); - $export_all = Minz_Request::param('export_all', false); + $export_feeds = Minz_Request::param('export_feeds', false); // code from https://stackoverflow.com/questions/1061710/php-zip-files-on-the-fly $file = tempnam('tmp', 'zip'); @@ -76,11 +76,16 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if ($export_starred) { $zip->addFromString('starred.json', $this->generate_articles('starred')); } - if ($export_all) { - $zip->addFromString('all.json', $this->generate_articles('all')); + $feedDAO = new FreshRSS_FeedDAO (); + foreach ($export_feeds as $feed_id) { + $feed = $feedDAO->searchById($feed_id); + $zip->addFromString( + 'feed_' . $feed->category() . '_' . $feed->id() . '.json', + $this->generate_articles('feed', $feed) + ); } - // Close and send to users + // Close and send to user $zip->close(); header('Content-Type: application/zip'); header('Content-Length: ' . filesize($file)); @@ -104,8 +109,28 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { return $this->view->helperToString('export/opml'); } - private function generate_articles($type) { - // TODO: we should get articles according to $type + private function generate_articles($type, $feed = NULL) { + $entryDAO = new FreshRSS_EntryDAO(); + + $catDAO = new FreshRSS_CategoryDAO(); + $this->view->categories = $catDAO->listCategories(); + + if ($type == 'starred') { + $this->view->list_title = Minz_Translate::t("starred_list"); + $this->view->type = 'starred'; + $this->view->entries = $entryDAO->listWhere( + 's', '', 'all', 'ASC', + $entryDAO->countUnreadReadFavorites()['all'] + ); + } elseif ($type == 'feed' && !is_null($feed)) { + $this->view->list_title = Minz_Translate::t("feed_list", $feed->name()); + $this->view->type = 'feed/' . $feed->id(); + $this->view->entries = $entryDAO->listWhere( + 'f', $feed->id(), 'all', 'ASC', + $this->view->conf->posts_per_page + ); + $this->view->feed = $feed; + } return $this->view->helperToString('export/articles'); } } -- cgit v1.2.3 From 05cc97a8f43d779fbea52f5ef47fe6a71c3ab692 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Fri, 28 Mar 2014 18:44:52 +0100 Subject: Export form is not displayed if there is nothing to export --- app/Controllers/importExportController.php | 1 + app/views/importExport/index.phtml | 2 ++ 2 files changed, 3 insertions(+) (limited to 'app/Controllers/importExportController.php') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 040cf3f7b..51f0708fd 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -131,6 +131,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { ); $this->view->feed = $feed; } + return $this->view->helperToString('export/articles'); } } diff --git a/app/views/importExport/index.phtml b/app/views/importExport/index.phtml index b82e0d26b..386c88648 100644 --- a/app/views/importExport/index.phtml +++ b/app/views/importExport/index.phtml @@ -19,6 +19,7 @@ + feeds) > 0) { ?>
@@ -47,4 +48,5 @@
+ -- cgit v1.2.3 From d464833922e9cc00948698aed0a2af1f0b55e101 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Fri, 28 Mar 2014 22:13:42 +0100 Subject: Improve import / export functionnality It is not finished yet and does not even work at all!! - ZIP archive can be uploaded - Entries are imported from starred.json and feed*.json but not added in DB for the moment. - Fix export (add author, id -> guid, content -> content.content and add alternate) See https://github.com/marienfressinaud/FreshRSS/issues/163 --- app/Controllers/feedController.php | 2 + app/Controllers/importExportController.php | 260 ++++++++++++++++++++++++----- app/views/helpers/export/articles.phtml | 11 +- 3 files changed, 228 insertions(+), 45 deletions(-) (limited to 'app/Controllers/importExportController.php') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 16516ad39..43435d99d 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -328,6 +328,8 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } public function massiveImportAction () { + # TODO: this function has moved to importExportController.php + # I keep it for the moment but should be deleted in a near future. @set_time_limit(300); $this->catDAO = new FreshRSS_CategoryDAO (); diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 51f0708fd..6194ce214 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -3,21 +3,22 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { public function firstAction() { if (!$this->view->loginOk) { - Minz_Error::error ( + Minz_Error::error( 403, - array ('error' => array (Minz_Translate::t ('access_denied'))) + array('error' => array(Minz_Translate::t('access_denied'))) ); } require_once(LIB_PATH . '/lib_opml.php'); + + $this->catDAO = new FreshRSS_CategoryDAO(); + $this->entryDAO = new FreshRSS_EntryDAO(); + $this->feedDAO = new FreshRSS_FeedDAO(); } public function indexAction() { - $catDAO = new FreshRSS_CategoryDAO(); - $this->view->categories = $catDAO->listCategories(); - - $feedDAO = new FreshRSS_FeedDAO(); - $this->view->feeds = $feedDAO->listFeeds(); + $this->view->categories = $this->catDAO->listCategories(); + $this->view->feeds = $this->feedDAO->listFeeds(); // au niveau de la vue, permet de ne pas voir un flux sélectionné dans la liste $this->view->flux = false; @@ -27,38 +28,218 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { public function importAction() { if (Minz_Request::isPost() && $_FILES['file']['error'] == 0) { - invalidateHttpCache(); - // on parse le fichier OPML pour récupérer les catégories et les flux associés + @set_time_limit(300); + + $file = $_FILES['file']; + $type_file = $this->guess_file_type($file['name']); + + $list_files = array( + 'opml' => array(), + 'json_starred' => array(), + 'json_feed' => array() + ); + + // We try to list all files according to their type + // A zip file is first opened and then its files are listed + $list = array(); + if ($type_file === 'zip') { + $zip = zip_open($file['tmp_name']); + + while (($zipfile = zip_read($zip)) !== false) { + $type_zipfile = $this->guess_file_type(zip_entry_name($zipfile)); + + if ($type_file !== 'unknown') { + $list_files[$type_zipfile][] = zip_entry_read( + $zipfile, + zip_entry_filesize($zipfile) + ); + } + } + + zip_close($zip); + } elseif ($type_file !== 'unknown') { + $list_files[$type_file][] = file_get_contents($file['tmp_name']); + } + + // Import different files. + // OPML first(so categories and feeds are imported) + // Starred articles then so the "favourite" status is already set + // And finally all other files. + $error = false; + foreach ($list_files['opml'] as $opml_file) { + $error = $this->import_opml($opml_file); + } + foreach ($list_files['json_starred'] as $article_file) { + $error = $this->import_articles($article_file, true); + } + foreach ($list_files['json_feed'] as $article_file) { + $error = $this->import_articles($article_file); + } + + // And finally, we get import status and redirect to the home page + $notif = null; + if ($error === true) { + $notif = array( + 'type' => 'good', + 'content' => Minz_Translate::t('feeds_imported_with_errors') + ); + } else { + $notif = array( + 'type' => 'good', + 'content' => Minz_Translate::t('feeds_imported') + ); + } + + Minz_Session::_param('notification', $notif); + Minz_Session::_param('actualize_feeds', true); + + Minz_Request::forward(array( + 'c' => 'index', + 'a' => 'index' + ), true); + } + + // What are you doing? you have to call this controller + // with a POST request! + Minz_Request::forward(array( + 'c' => 'importExport', + 'a' => 'index' + )); + } + + private function guess_file_type($filename) { + // A *very* basic guess file type function. Only based on filename + // That's could be improved but should be enough, at least for a first + // implementation. + // TODO: improve this function? + + if (substr_compare($filename, '.zip', -4) === 0) { + return 'zip'; + } elseif (substr_compare($filename, '.opml', -5) === 0) { + return 'opml'; + } elseif (strcmp($filename, 'starred.json') === 0) { + return 'json_starred'; + } elseif (substr_compare($filename, '.json', -5) === 0 && + strpos($filename, 'feed_') === 0) { + return 'json_feed'; + } else { + return 'unknown'; + } + } + + private function import_opml($opml_file) { + $categories = array(); + $feeds = array(); + try { + list($categories, $feeds) = opml_import($opml_file); + } catch (FreshRSS_Opml_Exception $e) { + Minz_Log::warning($e->getMessage()); + return true; + } + + $this->catDAO->checkDefault(); + + // on ajoute les catégories en masse dans une fonction à part + $this->addCategories($categories); + + // on calcule la date des articles les plus anciens qu'on accepte + $nb_month_old = $this->view->conf->old_entries; + $date_min = time() -(3600 * 24 * 30 * $nb_month_old); + + // la variable $error permet de savoir si une erreur est survenue + // Le but est de ne pas arrêter l'import même en cas d'erreur + // L'utilisateur sera mis au courant s'il y a eu des erreurs, mais + // ne connaîtra pas les détails. Ceux-ci seront toutefois logguées + $error = false; + foreach ($feeds as $feed) { try { - list ($categories, $feeds) = opml_import ( - file_get_contents ($_FILES['file']['tmp_name']) + $values = array( + 'id' => $feed->id(), + 'url' => $feed->url(), + 'category' => $feed->category(), + 'name' => $feed->name(), + 'website' => $feed->website(), + 'description' => $feed->description(), + 'lastUpdate' => 0, + 'httpAuth' => $feed->httpAuth() ); - // On redirige vers le controller feed qui va se charger d'insérer les flux en BDD - // les flux sont mis au préalable dans des variables de Request - Minz_Request::_param ('categories', $categories); - Minz_Request::_param ('feeds', $feeds); - Minz_Request::forward (array ('c' => 'feed', 'a' => 'massiveImport')); - } catch (FreshRSS_Opml_Exception $e) { - Minz_Log::record ($e->getMessage (), Minz_Log::WARNING); - - $notif = array ( - 'type' => 'bad', - 'content' => Minz_Translate::t ('bad_opml_file') + // ajout du flux que s'il n'est pas déjà en BDD + if (!$this->feedDAO->searchByUrl($values['url'])) { + $id = $this->feedDAO->addFeed($values); + if ($id) { + $feed->_id($id); + $feed->faviconPrepare(); + } else { + $error = true; + } + } + } catch (FreshRSS_Feed_Exception $e) { + $error = true; + Minz_Log::record($e->getMessage(), Minz_Log::WARNING); + } + } + + return $error; + } + + private function addCategories($categories) { + foreach ($categories as $cat) { + if (!$this->catDAO->searchByName($cat->name())) { + $values = array( + 'id' => $cat->id(), + 'name' => $cat->name(), ); - Minz_Session::_param ('notification', $notif); + $this->catDAO->addCategory($values); + } + } + } + + private function import_articles($article_file, $starred = false) { + $article_object = json_decode($article_file, true); + if (is_null($article_object)) { + Minz_Log::warning('Try to import a non-JSON file'); + return true; + } + + $google_compliant = (strpos($article_object['id'], 'com.google') !== false); + + foreach ($article_object['items'] as $item) { + $key = $google_compliant ? 'htmlUrl' : 'feedUrl'; + $feed = $this->feedDAO->searchByUrl($item['origin'][$key]); + if (is_null($feed)) { + $feed = new FreshRSS_Feed($item['origin'][$key]); + $feed->_name ($item['origin']['title']); + $feed->_website ($item['origin']['htmlUrl']); + + $error = $this->addFeed($feed); // TODO - Minz_Request::forward (array ( - 'c' => 'configure', - 'a' => 'importExport' - ), true); + if ($error) { + continue; + } } + + $author = isset($item['author']) ? $item['author'] : ''; + $key_content = $google_compliant && !isset($item['content']) ? 'summary' : 'content'; + $tags = $item['categories']; + if ($google_compliant) { + $tags = array_filter($tags, function($var) { + return strpos($var, '/state/com.google') === false; + }); + } + $entry = new FreshRSS_Entry( + $feed->id(), $item['id'], $item['title'], $author, + $item[$key_content]['content'], $item['alternate'][0]['href'], + $item['published'], false, $starred, $tags + ); + + Minz_Log::debug(print_r($entry, true)); // TODO } } public function exportAction() { if (Minz_Request::isPost()) { - $this->view->_useLayout (false); + $this->view->_useLayout(false); $export_opml = Minz_Request::param('export_opml', false); $export_starred = Minz_Request::param('export_starred', false); @@ -76,9 +257,8 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if ($export_starred) { $zip->addFromString('starred.json', $this->generate_articles('starred')); } - $feedDAO = new FreshRSS_FeedDAO (); foreach ($export_feeds as $feed_id) { - $feed = $feedDAO->searchById($feed_id); + $feed = $this->feedDAO->searchById($feed_id); $zip->addFromString( 'feed_' . $feed->category() . '_' . $feed->id() . '.json', $this->generate_articles('feed', $feed) @@ -96,13 +276,10 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } private function generate_opml() { - $feedDAO = new FreshRSS_FeedDAO (); - $catDAO = new FreshRSS_CategoryDAO (); - - $list = array (); - foreach ($catDAO->listCategories () as $key => $cat) { - $list[$key]['name'] = $cat->name (); - $list[$key]['feeds'] = $feedDAO->listByCategory ($cat->id ()); + $list = array(); + foreach ($this->catDAO->listCategories() as $key => $cat) { + $list[$key]['name'] = $cat->name(); + $list[$key]['feeds'] = $this->feedDAO->listByCategory($cat->id()); } $this->view->categories = $list; @@ -110,22 +287,19 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } private function generate_articles($type, $feed = NULL) { - $entryDAO = new FreshRSS_EntryDAO(); - - $catDAO = new FreshRSS_CategoryDAO(); - $this->view->categories = $catDAO->listCategories(); + $this->view->categories = $this->catDAO->listCategories(); if ($type == 'starred') { $this->view->list_title = Minz_Translate::t("starred_list"); $this->view->type = 'starred'; - $this->view->entries = $entryDAO->listWhere( + $this->view->entries = $this->entryDAO->listWhere( 's', '', 'all', 'ASC', $entryDAO->countUnreadReadFavorites()['all'] ); } elseif ($type == 'feed' && !is_null($feed)) { $this->view->list_title = Minz_Translate::t("feed_list", $feed->name()); $this->view->type = 'feed/' . $feed->id(); - $this->view->entries = $entryDAO->listWhere( + $this->view->entries = $this->entryDAO->listWhere( 'f', $feed->id(), 'all', 'ASC', $this->view->conf->posts_per_page ); diff --git a/app/views/helpers/export/articles.phtml b/app/views/helpers/export/articles.phtml index 71ac22f44..ffdca1daa 100644 --- a/app/views/helpers/export/articles.phtml +++ b/app/views/helpers/export/articles.phtml @@ -16,12 +16,19 @@ } $articles['items'][] = array( - 'id' => $entry->id(), + 'id' => $entry->guid(), 'categories' => array_values($entry->tags()), 'title' => $entry->title(), + 'author' => $entry->author(), 'published' => $entry->date(true), 'updated' => $entry->date(true), - 'content' => $entry->content(), + 'alternate' => array(array( + 'href' => $entry->link(), + 'type' => 'text/html' + )), + 'content' => array( + 'content' => $entry->content() + ), 'origin' => array( 'streamId' => $feed->id(), 'title' => $feed->name(), -- cgit v1.2.3 From 7676a197a4767f735dabae6ad9cf40ef65e91aa7 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sat, 29 Mar 2014 19:10:18 +0100 Subject: Fix typo + improve detection OPML file --- app/Controllers/importExportController.php | 7 ++++--- app/views/importExport/index.phtml | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'app/Controllers/importExportController.php') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 6194ce214..cbadeb6ca 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -115,7 +115,8 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if (substr_compare($filename, '.zip', -4) === 0) { return 'zip'; - } elseif (substr_compare($filename, '.opml', -5) === 0) { + } elseif (substr_compare($filename, '.opml', -5) === 0 || + substr_compare($filename, '.xml', -4) === 0) { return 'opml'; } elseif (strcmp($filename, 'starred.json') === 0) { return 'json_starred'; @@ -144,7 +145,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { // on calcule la date des articles les plus anciens qu'on accepte $nb_month_old = $this->view->conf->old_entries; - $date_min = time() -(3600 * 24 * 30 * $nb_month_old); + $date_min = time() - (3600 * 24 * 30 * $nb_month_old); // la variable $error permet de savoir si une erreur est survenue // Le but est de ne pas arrêter l'import même en cas d'erreur @@ -294,7 +295,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $this->view->type = 'starred'; $this->view->entries = $this->entryDAO->listWhere( 's', '', 'all', 'ASC', - $entryDAO->countUnreadReadFavorites()['all'] + $this->entryDAO->countUnreadReadFavorites()['all'] ); } elseif ($type == 'feed' && !is_null($feed)) { $this->view->list_title = Minz_Translate::t("feed_list", $feed->name()); diff --git a/app/views/importExport/index.phtml b/app/views/importExport/index.phtml index 386c88648..309058959 100644 --- a/app/views/importExport/index.phtml +++ b/app/views/importExport/index.phtml @@ -34,7 +34,7 @@ - feeds as $feed) { ?> -- cgit v1.2.3 From 9ea3819402746d8425d4a608f2d5f3c0f5bc29fb Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sat, 29 Mar 2014 20:18:57 +0100 Subject: Better OPML import / export - use a new OPML library (https://github.com/marienfressinaud/lib_opml) - import has been completely rewritten (far better!) - introduce addFeedObject and addCategoryObject (in DAO for the moment). Permit to add easily feeds and categories (check if they already exist in DB) - introduce html_chars_utf8 (wrap htmlspecialchars for UTF-8) --- app/Controllers/importExportController.php | 124 ++++++++----- app/Exceptions/OpmlException.php | 6 - app/Models/CategoryDAO.php | 12 ++ app/Models/FeedDAO.php | 29 +++ app/views/helpers/export/opml.phtml | 43 +++-- lib/lib_opml.php | 277 ++++++++++++++++++++--------- lib/lib_rss.php | 4 + 7 files changed, 345 insertions(+), 150 deletions(-) delete mode 100644 app/Exceptions/OpmlException.php (limited to 'app/Controllers/importExportController.php') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index cbadeb6ca..b6b4d0fed 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -129,71 +129,101 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } private function import_opml($opml_file) { - $categories = array(); - $feeds = array(); + $opml_array = array(); try { - list($categories, $feeds) = opml_import($opml_file); - } catch (FreshRSS_Opml_Exception $e) { + $opml_array = libopml_parse_string($opml_file); + } catch (LibOPML_Exception $e) { Minz_Log::warning($e->getMessage()); return true; } $this->catDAO->checkDefault(); - // on ajoute les catégories en masse dans une fonction à part - $this->addCategories($categories); - - // on calcule la date des articles les plus anciens qu'on accepte - $nb_month_old = $this->view->conf->old_entries; - $date_min = time() - (3600 * 24 * 30 * $nb_month_old); + return $this->addOpmlElements($opml_array['body']); + } - // la variable $error permet de savoir si une erreur est survenue - // Le but est de ne pas arrêter l'import même en cas d'erreur - // L'utilisateur sera mis au courant s'il y a eu des erreurs, mais - // ne connaîtra pas les détails. Ceux-ci seront toutefois logguées + private function addOpmlElements($opml_elements, $parent_cat = null) { $error = false; - foreach ($feeds as $feed) { - try { - $values = array( - 'id' => $feed->id(), - 'url' => $feed->url(), - 'category' => $feed->category(), - 'name' => $feed->name(), - 'website' => $feed->website(), - 'description' => $feed->description(), - 'lastUpdate' => 0, - 'httpAuth' => $feed->httpAuth() - ); + foreach ($opml_elements as $elt) { + $res = false; + if (isset($elt['xmlUrl'])) { + $res = $this->addFeedOpml($elt, $parent_cat); + } else { + $res = $this->addCategoryOpml($elt, $parent_cat); + } - // ajout du flux que s'il n'est pas déjà en BDD - if (!$this->feedDAO->searchByUrl($values['url'])) { - $id = $this->feedDAO->addFeed($values); - if ($id) { - $feed->_id($id); - $feed->faviconPrepare(); - } else { - $error = true; - } - } - } catch (FreshRSS_Feed_Exception $e) { - $error = true; - Minz_Log::record($e->getMessage(), Minz_Log::WARNING); + if (!$error && $res) { + // oops: there is at least one error! + $error = $res; } } return $error; } - private function addCategories($categories) { - foreach ($categories as $cat) { - if (!$this->catDAO->searchByName($cat->name())) { - $values = array( - 'id' => $cat->id(), - 'name' => $cat->name(), - ); - $this->catDAO->addCategory($values); + private function addFeedOpml($feed_elt, $parent_cat) { + if (is_null($parent_cat)) { + // This feed has no parent category so we get the default one + $parent_cat = $catDAO->getDefault()->name(); + } + + $cat = $this->catDAO->searchByName($parent_cat); + + if (!$cat) { + return true; + } + + // We get different useful information + $url = html_chars_utf8($feed_elt['xmlUrl']); + $name = html_chars_utf8($feed_elt['text']); + $website = ''; + if (isset($feed_elt['htmlUrl'])) { + $website = html_chars_utf8($feed_elt['htmlUrl']); + } + $description = ''; + if (isset($feed_elt['description'])) { + $description = html_chars_utf8($feed_elt['description']); + } + + $error = false; + try { + // Create a Feed object and add it in DB + $feed = new FreshRSS_Feed($url); + $feed->_category($cat->id()); + $feed->_name($name); + $feed->_website($website); + $feed->_description($description); + + // addFeedObject checks if feed is already in DB so nothing else to + // check here + $id = $this->feedDAO->addFeedObject($feed); + $error = ($id === false); + } catch (FreshRSS_Feed_Exception $e) { + Minz_Log::record($e->getMessage(), Minz_Log::WARNING); + $error = true; + } + + return $error; + } + + private function addCategoryOpml($cat_elt, $parent_cat) { + // Create a new Category object + $cat = new FreshRSS_Category(html_chars_utf8($cat_elt['text'])); + + $id = $this->catDAO->addCategoryObject($cat); + $error = ($id === false); + + if (isset($cat_elt['@outlines'])) { + // Our cat_elt contains more categories or more feeds, so we + // add them recursively. + // Note: FreshRSS does not support yet category arborescence + $res = $this->addOpmlElements($cat_elt['@outlines'], $cat->name()); + if (!$error && $res) { + $error = true; } } + + return $error; } private function import_articles($article_file, $starred = false) { diff --git a/app/Exceptions/OpmlException.php b/app/Exceptions/OpmlException.php deleted file mode 100644 index e0ea3e493..000000000 --- a/app/Exceptions/OpmlException.php +++ /dev/null @@ -1,6 +0,0 @@ -searchByName($category->name())) { + // Category does not exist yet in DB so we add it before continue + $values = array( + 'name' => $category->name(), + ); + return $this->addCategory($values); + } + + return false; + } + public function updateCategory ($id, $valuesTmp) { $sql = 'UPDATE `' . $this->prefix . 'category` SET name=? WHERE id=?'; $stm = $this->bd->prepare ($sql); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index ca25c3aeb..eac21df7e 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -24,6 +24,35 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { } } + public function addFeedObject($feed) { + // TODO: not sure if we should write this method in DAO since DAO + // should not be aware about feed class + + // Add feed only if we don't find it in DB + if (!$this->searchByUrl($feed->url())) { + $values = array( + 'id' => $feed->id(), + 'url' => $feed->url(), + 'category' => $feed->category(), + 'name' => $feed->name(), + 'website' => $feed->website(), + 'description' => $feed->description(), + 'lastUpdate' => 0, + 'httpAuth' => $feed->httpAuth() + ); + + $id = $this->addFeed($values); + if ($id) { + $feed->_id($id); + $feed->faviconPrepare(); + } + + return $id; + } + + return false; + } + public function updateFeed ($id, $valuesTmp) { $set = ''; foreach ($valuesTmp as $key => $v) { diff --git a/app/views/helpers/export/opml.phtml b/app/views/helpers/export/opml.phtml index 2e66e5054..adbac904d 100644 --- a/app/views/helpers/export/opml.phtml +++ b/app/views/helpers/export/opml.phtml @@ -1,15 +1,30 @@ '; -?> - - - - <?php echo Minz_Configuration::title (); ?> OPML Feed - - - -categories); ?> - - + +$opml_array = array( + 'head' => array( + 'title' => Minz_Configuration::title(), + 'dateCreated' => date('D, d M Y H:i:s') + ), + 'body' => array() +); + +foreach ($this->categories as $key => $cat) { + $opml_array['body'][$key] = array( + 'text' => $cat['name'], + '@outlines' => array() + ); + + foreach ($cat['feeds'] as $feed) { + $opml_array['body'][$key]['@outlines'][] = array( + 'text' => $feed->name(), + 'type' => 'rss', + 'xmlUrl' => $feed->url(), + 'htmlUrl' => $feed->website(), + 'description' => htmlspecialchars( + $feed->description(), ENT_COMPAT, 'UTF-8' + ) + ); + } +} + +echo libopml_render($opml_array); diff --git a/lib/lib_opml.php b/lib/lib_opml.php index 05e54d85e..16a9921ea 100644 --- a/lib/lib_opml.php +++ b/lib/lib_opml.php @@ -1,23 +1,86 @@ ' . "\n"; - - foreach ($cat['feeds'] as $feed) { - $txt .= "\t" . '' . "\n"; +/* * + * lib_opml is a free library to manage OPML format in PHP. + * It takes in consideration only version 2.0 (http://dev.opml.org/spec2.html). + * Basically it means "text" attribute for outline elements is required. + * + * lib_opml requires SimpleXML (http://php.net/manual/en/book.simplexml.php) + * + * Usages: + * > include('lib_opml.php'); + * > $filename = 'my_opml_file.xml'; + * > $opml_array = libopml_parse_file($filename); + * > print_r($opml_array); + * + * > $opml_string = [...]; + * > $opml_array = libopml_parse_string($opml_string); + * > print_r($opml_array); + * + * > $opml_array = [...]; + * > $opml_string = libopml_render($opml_array); + * > $opml_object = libopml_render($opml_array, true); + * > echo $opml_string; + * > print_r($opml_object); + * + * If parsing fails for any reason (e.g. not an XML string, does not match with + * the specifications), a LibOPML_Exception is raised. + * + * Author: Marien Fressinaud + * Url: https://github.com/marienfressinaud/lib_opml + * Version: 0.1 + * Date: 2014-03-29 + * License: public domain + * + * */ + +class LibOPML_Exception extends Exception {} + + +// These elements are optional +define('HEAD_ELEMENTS', serialize(array( + 'title', 'dateCreated', 'dateModified', 'ownerName', 'ownerEmail', + 'ownerId', 'docs', 'expansionState', 'vertScrollState', 'windowTop', + 'windowLeft', 'windowBottom', 'windowRight' +))); + + +function libopml_parse_outline($outline_xml) { + $outline = array(); + + // An outline may contain any kind of attributes but "text" attribute is + // required ! + $text_is_present = false; + foreach ($outline_xml->attributes() as $key => $value) { + $outline[$key] = (string)$value; + + if ($key === 'text') { + $text_is_present = true; } + } - $txt .= '' . "\n"; + if (!$text_is_present) { + throw new LibOPML_Exception( + 'Outline does not contain any text attribute' + ); } - return $txt; + foreach ($outline_xml->children() as $key => $value) { + // An outline may contain any number of outline children + if ($key === 'outline') { + $outline['@outlines'][] = libopml_parse_outline($value); + } else { + throw new LibOPML_Exception( + 'Body can contain only outline elements' + ); + } + } + + return $outline; } -function opml_import ($xml) { - $xml = html_only_entity_decode($xml); //!\ Assume UTF-8 +function libopml_parse_string($xml) { $dom = new DOMDocument(); $dom->recover = true; $dom->strictErrorChecking = false; @@ -27,94 +90,142 @@ function opml_import ($xml) { $opml = simplexml_import_dom($dom); if (!$opml) { - throw new FreshRSS_Opml_Exception (); + throw new LibOPML_Exception(); } - $catDAO = new FreshRSS_CategoryDAO(); - $catDAO->checkDefault(); - $defCat = $catDAO->getDefault(); + $array = array( + 'version' => (string)$opml['version'], + 'head' => array(), + 'body' => array() + ); + + // First, we get all "head" elements. Head is required but its sub-elements + // are optional. + foreach ($opml->head->children() as $key => $value) { + if (in_array($key, unserialize(HEAD_ELEMENTS), true)) { + $array['head'][$key] = (string)$value; + } else { + throw new LibOPML_Exception( + $key . 'is not part of OPML format' + ); + } + } - $categories = array (); - $feeds = array (); + // Then, we get body oulines. Body must contain at least one outline + // element. + $at_least_one_outline = false; + foreach ($opml->body->children() as $key => $value) { + if ($key === 'outline') { + $at_least_one_outline = true; + $array['body'][] = libopml_parse_outline($value); + } else { + throw new LibOPML_Exception( + 'Body can contain only outline elements' + ); + } + } + + if (!$at_least_one_outline) { + throw new LibOPML_Exception( + 'Body must contain at least one outline element' + ); + } - foreach ($opml->body->outline as $outline) { - if (!isset ($outline['xmlUrl'])) { - // Catégorie - $title = ''; + return $array; +} - if (isset ($outline['text'])) { - $title = (string) $outline['text']; - } elseif (isset ($outline['title'])) { - $title = (string) $outline['title']; - } - if ($title) { - // Permet d'éviter les soucis au niveau des id : - // ceux-ci sont générés en fonction de la date, - // un flux pourrait être dans une catégorie X avec l'id Y - // alors qu'il existe déjà la catégorie X mais avec l'id Z - // Y ne sera pas ajouté et le flux non plus vu que l'id - // de sa catégorie n'exisera pas - $title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); - $catDAO = new FreshRSS_CategoryDAO (); - $cat = $catDAO->searchByName ($title); - if ($cat == null) { - $cat = new FreshRSS_Category ($title); - $values = array ( - 'name' => $cat->name () - ); - $cat->_id ($catDAO->addCategory ($values)); - } - - $feeds = array_merge ($feeds, getFeedsOutline ($outline, $cat->id ())); +function libopml_parse_file($filename) { + $file_content = file_get_contents($filename); + + if ($file_content === false) { + throw new LibOPML_Exception( + $filename . ' cannot be found' + ); + } + + return libopml_parse_string($file_content); +} + + +function libopml_render_outline($parent_elt, $outline) { + // Outline MUST be an array! + if (!is_array($outline)) { + throw new LibOPML_Exception( + 'Outline element must be defined as array' + ); + } + + $outline_elt = $parent_elt->addChild('outline'); + $text_is_present = false; + foreach ($outline as $key => $value) { + // Only outlines can be an array and so we consider children are also + // outline elements. + if ($key === '@outlines' && is_array($value)) { + foreach ($value as $outline_child) { + libopml_render_outline($outline_elt, $outline_child); } + } elseif (is_array($value)) { + throw new LibOPML_Exception( + 'Type of outline elements cannot be array: ' . $key + ); } else { - // Flux rss sans catégorie, on récupère l'ajoute dans la catégorie par défaut - $feeds[] = getFeed ($outline, $defCat->id()); + // Detect text attribute is present, that's good :) + if ($key === 'text') { + $text_is_present = true; + } + + $outline_elt->addAttribute($key, $value); } } - return array ($categories, $feeds); + if (!$text_is_present) { + throw new LibOPML_Exception( + 'You must define at least a text element for all outlines' + ); + } } -/** - * import all feeds of a given outline tag - */ -function getFeedsOutline ($outline, $cat_id) { - $feeds = array (); - foreach ($outline->children () as $child) { - if (isset ($child['xmlUrl'])) { - $feeds[] = getFeed ($child, $cat_id); - } else { - $feeds = array_merge( - $feeds, - getFeedsOutline ($child, $cat_id) - ); +function libopml_render($array, $as_xml_object = false) { + $opml = new SimpleXMLElement(''); + + // Create head element. $array['head'] is optional but head element will + // exist in the final XML object. + $head = $opml->addChild('head'); + if (isset($array['head'])) { + foreach ($array['head'] as $key => $value) { + if (in_array($key, unserialize(HEAD_ELEMENTS), true)) { + $head->addChild($key, $value); + } } } - return $feeds; -} + // Check body is set and contains at least one element + if (!isset($array['body'])) { + throw new LibOPML_Exception( + '$array must contain a body element' + ); + } + if (count($array['body']) <= 0) { + throw new LibOPML_Exception( + 'Body element must contain at least one element (array)' + ); + } -function getFeed ($outline, $cat_id) { - $url = (string) $outline['xmlUrl']; - $url = htmlspecialchars($url, ENT_COMPAT, 'UTF-8'); - $title = ''; - if (isset ($outline['text'])) { - $title = (string) $outline['text']; - } elseif (isset ($outline['title'])) { - $title = (string) $outline['title']; - } - $title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); - $feed = new FreshRSS_Feed ($url); - $feed->_category ($cat_id); - $feed->_name ($title); - if (isset($outline['htmlUrl'])) { - $feed->_website(htmlspecialchars((string)$outline['htmlUrl'], ENT_COMPAT, 'UTF-8')); - } - if (isset($outline['description'])) { - $feed->_description(sanitizeHTML((string)$outline['description'])); - } - return $feed; + // Create outline elements + $body = $opml->addChild('body'); + foreach ($array['body'] as $outline) { + libopml_render_outline($body, $outline); + } + + // And return the final result + if ($as_xml_object) { + return $opml; + } else { + $dom = dom_import_simplexml($opml)->ownerDocument; + $dom->formatOutput = true; + $dom->encoding = 'UTF-8'; + return $dom->saveXML(); + } } diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 2077fe63f..0f8161129 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -244,3 +244,7 @@ function cryptAvailable() { } return false; } + +function html_chars_utf8($str) { + return htmlspecialchars($str, ENT_COMPAT, 'UTF-8'); +} -- cgit v1.2.3 From 779afe9c4ee00f8099a423e1fceee40197a90cfa Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 30 Mar 2014 14:28:13 +0200 Subject: Import of articles is implemented! - Remove massiveImportAction and addCategories from FeedController - Fix typo for some methods (camelCase) - addCategoryObject and addFeedObject return id if corresponding object already exists in DB - introduce addEntryObject. Return -1 if Entry already exist (in order to keep quite good performances) - Complete importArticles method Need some more tests + better performance --- app/Controllers/feedController.php | 88 ---------------------------- app/Controllers/importExportController.php | 92 +++++++++++++++++++++--------- app/Models/CategoryDAO.php | 5 +- app/Models/EntryDAO.php | 30 ++++++++++ app/Models/FeedDAO.php | 5 +- 5 files changed, 100 insertions(+), 120 deletions(-) (limited to 'app/Controllers/importExportController.php') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 43435d99d..95c9c1e9c 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -327,82 +327,6 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } - public function massiveImportAction () { - # TODO: this function has moved to importExportController.php - # I keep it for the moment but should be deleted in a near future. - @set_time_limit(300); - - $this->catDAO = new FreshRSS_CategoryDAO (); - $this->catDAO->checkDefault (); - - $entryDAO = new FreshRSS_EntryDAO (); - $feedDAO = new FreshRSS_FeedDAO (); - - $categories = Minz_Request::param ('categories', array (), true); - $feeds = Minz_Request::param ('feeds', array (), true); - - // on ajoute les catégories en masse dans une fonction à part - $this->addCategories ($categories); - - // on calcule la date des articles les plus anciens qu'on accepte - $nb_month_old = $this->view->conf->old_entries; - $date_min = time () - (3600 * 24 * 30 * $nb_month_old); - - // la variable $error permet de savoir si une erreur est survenue - // Le but est de ne pas arrêter l'import même en cas d'erreur - // L'utilisateur sera mis au courant s'il y a eu des erreurs, mais - // ne connaîtra pas les détails. Ceux-ci seront toutefois logguées - $error = false; - $i = 0; - foreach ($feeds as $feed) { - try { - $values = array ( - 'id' => $feed->id (), - 'url' => $feed->url (), - 'category' => $feed->category (), - 'name' => $feed->name (), - 'website' => $feed->website (), - 'description' => $feed->description (), - 'lastUpdate' => 0, - 'httpAuth' => $feed->httpAuth () - ); - - // ajout du flux que s'il n'est pas déjà en BDD - if (!$feedDAO->searchByUrl ($values['url'])) { - $id = $feedDAO->addFeed ($values); - if ($id) { - $feed->_id ($id); - $feed->faviconPrepare(); - } else { - $error = true; - } - } - } catch (FreshRSS_Feed_Exception $e) { - $error = true; - Minz_Log::record ($e->getMessage (), Minz_Log::WARNING); - } - } - - if ($error) { - $res = Minz_Translate::t ('feeds_imported_with_errors'); - } else { - $res = Minz_Translate::t ('feeds_imported'); - } - - $notif = array ( - 'type' => 'good', - 'content' => $res - ); - Minz_Session::_param ('notification', $notif); - Minz_Session::_param ('actualize_feeds', true); - - // et on redirige vers la page d'accueil - Minz_Request::forward (array ( - 'c' => 'index', - 'a' => 'index' - ), true); - } - public function deleteAction () { if (Minz_Request::isPost ()) { $type = Minz_Request::param ('type', 'feed'); @@ -446,16 +370,4 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } } - - private function addCategories ($categories) { - foreach ($categories as $cat) { - if (!$this->catDAO->searchByName ($cat->name ())) { - $values = array ( - 'id' => $cat->id (), - 'name' => $cat->name (), - ); - $catDAO->addCategory ($values); - } - } - } } diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index b6b4d0fed..3b6bf2c5c 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -31,7 +31,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { @set_time_limit(300); $file = $_FILES['file']; - $type_file = $this->guess_file_type($file['name']); + $type_file = $this->guessFileType($file['name']); $list_files = array( 'opml' => array(), @@ -46,7 +46,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $zip = zip_open($file['tmp_name']); while (($zipfile = zip_read($zip)) !== false) { - $type_zipfile = $this->guess_file_type(zip_entry_name($zipfile)); + $type_zipfile = $this->guessFileType(zip_entry_name($zipfile)); if ($type_file !== 'unknown') { $list_files[$type_zipfile][] = zip_entry_read( @@ -67,13 +67,13 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { // And finally all other files. $error = false; foreach ($list_files['opml'] as $opml_file) { - $error = $this->import_opml($opml_file); + $error = $this->importOpml($opml_file); } foreach ($list_files['json_starred'] as $article_file) { - $error = $this->import_articles($article_file, true); + $error = $this->importArticles($article_file, true); } foreach ($list_files['json_feed'] as $article_file) { - $error = $this->import_articles($article_file); + $error = $this->importArticles($article_file); } // And finally, we get import status and redirect to the home page @@ -107,7 +107,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { )); } - private function guess_file_type($filename) { + private function guessFileType($filename) { // A *very* basic guess file type function. Only based on filename // That's could be improved but should be enough, at least for a first // implementation. @@ -128,7 +128,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } } - private function import_opml($opml_file) { + private function importOpml($opml_file) { $opml_array = array(); try { $opml_array = libopml_parse_string($opml_file); @@ -164,7 +164,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { private function addFeedOpml($feed_elt, $parent_cat) { if (is_null($parent_cat)) { // This feed has no parent category so we get the default one - $parent_cat = $catDAO->getDefault()->name(); + $parent_cat = $this->catDAO->getDefault()->name(); } $cat = $this->catDAO->searchByName($parent_cat); @@ -199,7 +199,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $id = $this->feedDAO->addFeedObject($feed); $error = ($id === false); } catch (FreshRSS_Feed_Exception $e) { - Minz_Log::record($e->getMessage(), Minz_Log::WARNING); + Minz_Log::warning($e->getMessage()); $error = true; } @@ -226,28 +226,23 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { return $error; } - private function import_articles($article_file, $starred = false) { + private function importArticles($article_file, $starred = false) { $article_object = json_decode($article_file, true); if (is_null($article_object)) { Minz_Log::warning('Try to import a non-JSON file'); return true; } + $is_read = $this->view->conf->mark_when['reception'] ? 1 : 0; + $google_compliant = (strpos($article_object['id'], 'com.google') !== false); + $error = false; foreach ($article_object['items'] as $item) { - $key = $google_compliant ? 'htmlUrl' : 'feedUrl'; - $feed = $this->feedDAO->searchByUrl($item['origin'][$key]); + $feed = $this->addFeedArticles($item['origin'], $google_compliant); if (is_null($feed)) { - $feed = new FreshRSS_Feed($item['origin'][$key]); - $feed->_name ($item['origin']['title']); - $feed->_website ($item['origin']['htmlUrl']); - - $error = $this->addFeed($feed); // TODO - - if ($error) { - continue; - } + $error = true; + continue; } $author = isset($item['author']) ? $item['author'] : ''; @@ -258,14 +253,55 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { return strpos($var, '/state/com.google') === false; }); } + $entry = new FreshRSS_Entry( $feed->id(), $item['id'], $item['title'], $author, $item[$key_content]['content'], $item['alternate'][0]['href'], - $item['published'], false, $starred, $tags + $item['published'], $is_read, $starred ); + $entry->_tags($tags); - Minz_Log::debug(print_r($entry, true)); // TODO + $id = $this->entryDAO->addEntryObject( + $entry, $this->view->conf, $feed->keepHistory() + ); + + if (!$error && ($id === false)) { + $error = true; + } } + + return $error; + } + + private function addFeedArticles($origin, $google_compliant) { + $default_cat = $this->catDAO->getDefault(); + + $return = null; + $key = $google_compliant ? 'htmlUrl' : 'feedUrl'; + $url = $origin[$key]; + $name = $origin['title']; + $website = $origin['htmlUrl']; + $error = false; + try { + // Create a Feed object and add it in DB + $feed = new FreshRSS_Feed($url); + $feed->_category($default_cat->id()); + $feed->_name($name); + $feed->_website($website); + + // addFeedObject checks if feed is already in DB so nothing else to + // check here + $id = $this->feedDAO->addFeedObject($feed); + + if ($id !== false) { + $feed->_id($id); + $return = $feed; + } + } catch (FreshRSS_Feed_Exception $e) { + Minz_Log::warning($e->getMessage()); + } + + return $return; } public function exportAction() { @@ -283,16 +319,16 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { // Stuff with content if ($export_opml) { - $zip->addFromString('feeds.opml', $this->generate_opml()); + $zip->addFromString('feeds.opml', $this->generateOpml()); } if ($export_starred) { - $zip->addFromString('starred.json', $this->generate_articles('starred')); + $zip->addFromString('starred.json', $this->generateArticles('starred')); } foreach ($export_feeds as $feed_id) { $feed = $this->feedDAO->searchById($feed_id); $zip->addFromString( 'feed_' . $feed->category() . '_' . $feed->id() . '.json', - $this->generate_articles('feed', $feed) + $this->generateArticles('feed', $feed) ); } @@ -306,7 +342,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } } - private function generate_opml() { + private function generateOpml() { $list = array(); foreach ($this->catDAO->listCategories() as $key => $cat) { $list[$key]['name'] = $cat->name(); @@ -317,7 +353,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { return $this->view->helperToString('export/opml'); } - private function generate_articles($type, $feed = NULL) { + private function generateArticles($type, $feed = NULL) { $this->view->categories = $this->catDAO->listCategories(); if ($type == 'starred') { diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index 8be732b98..6a9b839b9 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -19,7 +19,8 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { } public function addCategoryObject($category) { - if (!$this->searchByName($category->name())) { + $cat = $this->searchByName($category->name()); + if (!$cat) { // Category does not exist yet in DB so we add it before continue $values = array( 'name' => $category->name(), @@ -27,7 +28,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { return $this->addCategory($values); } - return false; + return $cat->id(); } public function updateCategory ($id, $valuesTmp) { diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index e4cf128ea..6d00967fc 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -35,6 +35,36 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } } + public function addEntryObject($entry, $conf, $feedHistory) { + $existingGuids = array_fill_keys( + $this->listLastGuidsByFeed($entry->feed(), 20), 1 + ); + + $nb_month_old = max($conf->old_entries, 1); + $date_min = time() - (3600 * 24 * 30 * $nb_month_old); + + $eDate = $entry->date(true); + + if ($feedHistory == -2) { + $feedHistory = $conf->keep_history_default; + } + + if (!isset($existingGuids[$entry->guid()]) && + ($feedHistory != 0 || $eDate >= $date_min)) { + $values = $entry->toArray(); + + $useDeclaredDate = empty($existingGuids); + $values['id'] = ($useDeclaredDate || $eDate < $date_min) ? + min(time(), $eDate) . uSecString() : + uTimeString(); + + return $this->addEntry($values); + } + + // We don't return Entry object to avoid a research in DB + return -1; + } + public function markFavorite($ids, $is_favorite = true) { if (!is_array($ids)) { $ids = array($ids); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index eac21df7e..b65ff4af0 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -29,7 +29,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { // should not be aware about feed class // Add feed only if we don't find it in DB - if (!$this->searchByUrl($feed->url())) { + $feed_search = $this->searchByUrl($feed->url()); + if (!$feed_search) { $values = array( 'id' => $feed->id(), 'url' => $feed->url(), @@ -50,7 +51,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return $id; } - return false; + return $feed_search->id(); } public function updateFeed ($id, $valuesTmp) { -- cgit v1.2.3 From 34b17b748efb996f0202815dcf095a45d28e38da Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 30 Mar 2014 14:55:29 +0200 Subject: Fix coding style --- app/Controllers/importExportController.php | 49 ++++++++++++++++++------------ app/layout/aside_feed.phtml | 2 +- 2 files changed, 31 insertions(+), 20 deletions(-) (limited to 'app/Controllers/importExportController.php') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 3b6bf2c5c..02d62c890 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -20,9 +20,6 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $this->view->categories = $this->catDAO->listCategories(); $this->view->feeds = $this->feedDAO->listFeeds(); - // au niveau de la vue, permet de ne pas voir un flux sélectionné dans la liste - $this->view->flux = false; - Minz_View::prependTitle(Minz_Translate::t('import_export') . ' · '); } @@ -46,7 +43,9 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $zip = zip_open($file['tmp_name']); while (($zipfile = zip_read($zip)) !== false) { - $type_zipfile = $this->guessFileType(zip_entry_name($zipfile)); + $type_zipfile = $this->guessFileType( + zip_entry_name($zipfile) + ); if ($type_file !== 'unknown') { $list_files[$type_zipfile][] = zip_entry_read( @@ -58,7 +57,9 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { zip_close($zip); } elseif ($type_file !== 'unknown') { - $list_files[$type_file][] = file_get_contents($file['tmp_name']); + $list_files[$type_file][] = file_get_contents( + $file['tmp_name'] + ); } // Import different files. @@ -79,18 +80,19 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { // And finally, we get import status and redirect to the home page $notif = null; if ($error === true) { - $notif = array( - 'type' => 'good', - 'content' => Minz_Translate::t('feeds_imported_with_errors') + $content_notif = Minz_Translate::t( + 'feeds_imported_with_errors' ); } else { - $notif = array( - 'type' => 'good', - 'content' => Minz_Translate::t('feeds_imported') + $content_notif = Minz_Translate::t( + 'feeds_imported' ); } - Minz_Session::_param('notification', $notif); + Minz_Session::_param('notification', array( + 'type' => 'good', + 'content' => $content_notif + )); Minz_Session::_param('actualize_feeds', true); Minz_Request::forward(array( @@ -235,7 +237,9 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $is_read = $this->view->conf->mark_when['reception'] ? 1 : 0; - $google_compliant = (strpos($article_object['id'], 'com.google') !== false); + $google_compliant = ( + strpos($article_object['id'], 'com.google') !== false + ); $error = false; foreach ($article_object['items'] as $item) { @@ -246,7 +250,8 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } $author = isset($item['author']) ? $item['author'] : ''; - $key_content = $google_compliant && !isset($item['content']) ? 'summary' : 'content'; + $key_content = ($google_compliant && !isset($item['content'])) ? + 'summary' : 'content'; $tags = $item['categories']; if ($google_compliant) { $tags = array_filter($tags, function($var) { @@ -312,17 +317,21 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $export_starred = Minz_Request::param('export_starred', false); $export_feeds = Minz_Request::param('export_feeds', false); - // code from https://stackoverflow.com/questions/1061710/php-zip-files-on-the-fly + // From https://stackoverflow.com/questions/1061710/php-zip-files-on-the-fly $file = tempnam('tmp', 'zip'); $zip = new ZipArchive(); $zip->open($file, ZipArchive::OVERWRITE); // Stuff with content if ($export_opml) { - $zip->addFromString('feeds.opml', $this->generateOpml()); + $zip->addFromString( + 'feeds.opml', $this->generateOpml() + ); } if ($export_starred) { - $zip->addFromString('starred.json', $this->generateArticles('starred')); + $zip->addFromString( + 'starred.json', $this->generateArticles('starred') + ); } foreach ($export_feeds as $feed_id) { $feed = $this->feedDAO->searchById($feed_id); @@ -357,14 +366,16 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $this->view->categories = $this->catDAO->listCategories(); if ($type == 'starred') { - $this->view->list_title = Minz_Translate::t("starred_list"); + $this->view->list_title = Minz_Translate::t('starred_list'); $this->view->type = 'starred'; $this->view->entries = $this->entryDAO->listWhere( 's', '', 'all', 'ASC', $this->entryDAO->countUnreadReadFavorites()['all'] ); } elseif ($type == 'feed' && !is_null($feed)) { - $this->view->list_title = Minz_Translate::t("feed_list", $feed->name()); + $this->view->list_title = Minz_Translate::t( + 'feed_list', $feed->name() + ); $this->view->type = 'feed/' . $feed->id(); $this->view->entries = $this->entryDAO->listWhere( 'f', $feed->id(), 'all', 'ASC', diff --git a/app/layout/aside_feed.phtml b/app/layout/aside_feed.phtml index ae16efd96..1fad60af5 100644 --- a/app/layout/aside_feed.phtml +++ b/app/layout/aside_feed.phtml @@ -56,7 +56,7 @@ feeds)) { ?> feeds as $feed) { ?> nbEntries (); ?> -
  • +
  • ✇ name (); ?> -- cgit v1.2.3