From e1f214e9e2e09a83a9920e33fbf617dfe48fbb7e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 22 Oct 2016 12:58:06 +0200 Subject: CLI list-users and create-user https://github.com/FreshRSS/FreshRSS/issues/1095 https://github.com/FreshRSS/FreshRSS/issues/1090 --- cli/create-user.php | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 cli/create-user.php (limited to 'cli/create-user.php') diff --git a/cli/create-user.php b/cli/create-user.php new file mode 100644 index 000000000..08c057af8 --- /dev/null +++ b/cli/create-user.php @@ -0,0 +1,41 @@ +#!/usr/bin/php + empty($options['language']) ? '' : $options['language'], + 'token' => empty($options['token']) ? '' : $options['token'], + )); + +invalidateHttpCache(FreshRSS_Context::$system_conf->default_user); + +echo 'Result: ', ($ok ? 'success' : 'fail'), ".\n"; +exit($ok ? 0 : 1); -- cgit v1.2.3 From 2cd370a736fbb81e90f20240d8d37649ac5f53f7 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 22 Oct 2016 13:02:37 +0200 Subject: +x execution rights --- cli/create-user.php | 0 cli/list-users.php | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 cli/create-user.php mode change 100644 => 100755 cli/list-users.php (limited to 'cli/create-user.php') diff --git a/cli/create-user.php b/cli/create-user.php old mode 100644 new mode 100755 diff --git a/cli/list-users.php b/cli/list-users.php old mode 100644 new mode 100755 -- cgit v1.2.3 From 1b8eb6c7e732f1eda4fc8f22e847b363b016f857 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 23 Oct 2016 01:46:14 +0200 Subject: CLI import ZIP/OPML/JSON for user https://github.com/FreshRSS/FreshRSS/issues/1095 https://github.com/FreshRSS/FreshRSS/issues/851 --- CHANGELOG.md | 2 +- README.fr.md | 2 +- README.md | 2 +- app/Controllers/categoryController.php | 5 +- app/Controllers/feedController.php | 6 +- app/Controllers/importExportController.php | 273 ++++++++++++++++++----------- app/Exceptions/ZipException.php | 14 ++ app/Exceptions/ZipMissingException.php | 4 + app/Models/CategoryDAO.php | 11 +- app/i18n/cz/feedback.php | 6 +- app/i18n/cz/sub.php | 4 +- app/i18n/de/feedback.php | 6 +- app/i18n/de/sub.php | 2 +- app/i18n/en/feedback.php | 6 +- app/i18n/en/sub.php | 4 +- app/i18n/fr/feedback.php | 6 +- app/i18n/fr/sub.php | 4 +- app/i18n/it/feedback.php | 6 +- app/i18n/it/sub.php | 4 +- app/i18n/nl/feedback.php | 6 +- app/i18n/nl/sub.php | 4 +- app/i18n/ru/feedback.php | 6 +- app/i18n/ru/sub.php | 4 +- app/i18n/tr/feedback.php | 6 +- app/i18n/tr/sub.php | 4 +- cli/_cli.php | 5 + cli/create-user.php | 17 +- cli/delete-user.php | 3 +- cli/import-for-user.php | 36 ++++ cli/list-users.php | 4 +- lib/Minz/ModelPdo.php | 13 +- 31 files changed, 296 insertions(+), 179 deletions(-) create mode 100644 app/Exceptions/ZipException.php create mode 100644 app/Exceptions/ZipMissingException.php create mode 100644 cli/import-for-user.php (limited to 'cli/create-user.php') diff --git a/CHANGELOG.md b/CHANGELOG.md index c35b3b578..d24ec0739 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -390,7 +390,7 @@ * Possibility to combine search filters, e.g. `date:2014-05 intitle:FreshRSS intitle:Open great reader #Internet` * Change nav menu with more buttons instead of dropdown menus and add some filters * New system of import / export - * Support OPML, Json (like Google Reader) and Zip archives + * Support OPML, Json (like Google Reader) and ZIP archives * Can export and import articles (specific option for favorites) * Refactor "Origine" theme * Some improvements diff --git a/README.fr.md b/README.fr.md index 3fcedd337..2a2da256d 100644 --- a/README.fr.md +++ b/README.fr.md @@ -34,7 +34,7 @@ Nous sommes une communauté amicale. * Serveur Web Apache2 (recommandé), ou nginx, lighttpd (non testé sur les autres) * PHP 5.3.3+ (PHP 5.4+ recommandé, et PHP 5.5+ pour les performances, et PHP 7+ pour d’encore meilleures performances) * Requis : [DOM](http://php.net/dom), [XML](http://php.net/xml), [PDO_MySQL](http://php.net/pdo-mysql) ou [PDO_SQLite](http://php.net/pdo-sqlite) ou [PDO_PGSQL](http://php.net/pdo-pgsql), [cURL](http://php.net/curl) - * Recommandés : [JSON](http://php.net/json), [GMP](http://php.net/gmp) (pour accès API sur plateformes < 64 bits), [IDN](http://php.net/intl.idn) (pour les noms de domaines internationalisés), [mbstring](http://php.net/mbstring) et/ou [iconv](http://php.net/iconv) (pour conversion d’encodages), [Zip](http://php.net/zip) (pour import/export), [zlib](http://php.net/zlib) (pour les flux compressés) + * Recommandés : [JSON](http://php.net/json), [GMP](http://php.net/gmp) (pour accès API sur plateformes < 64 bits), [IDN](http://php.net/intl.idn) (pour les noms de domaines internationalisés), [mbstring](http://php.net/mbstring) et/ou [iconv](http://php.net/iconv) (pour conversion d’encodages), [ZIP](http://php.net/zip) (pour import/export), [zlib](http://php.net/zlib) (pour les flux compressés) * MySQL 5.5.3+ (recommandé), ou SQLite 3.7.4+, ou PostgreSQL (experimental) * Un navigateur Web récent tel Firefox, Internet Explorer 11 / Edge, Chrome, Opera, Safari. * Fonctionne aussi sur mobile diff --git a/README.md b/README.md index 5c8f586fa..a541d6dea 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ We are a friendly community. * A web server: Apache2 (recommended), nginx, lighttpd (not tested on others) * PHP 5.3.3+ (PHP 5.4+ recommended, and PHP 5.5+ for performance, and PHP 7 for even higher performance) * Required extensions: [DOM](http://php.net/dom), [XML](http://php.net/xml), [PDO_MySQL](http://php.net/pdo-mysql) or [PDO_SQLite](http://php.net/pdo-sqlite) or [PDO_PGSQL](http://php.net/pdo-pgsql), [cURL](http://php.net/curl) - * Recommended extensions: [JSON](http://php.net/json), [GMP](http://php.net/gmp) (for API access on platforms < 64 bits), [IDN](http://php.net/intl.idn) (for Internationalized Domain Names), [mbstring](http://php.net/mbstring) and/or [iconv](http://php.net/iconv) (for charset conversion), [Zip](http://php.net/zip) (for import/export), [zlib](http://php.net/zlib) (for compressed feeds) + * Recommended extensions: [JSON](http://php.net/json), [GMP](http://php.net/gmp) (for API access on platforms < 64 bits), [IDN](http://php.net/intl.idn) (for Internationalized Domain Names), [mbstring](http://php.net/mbstring) and/or [iconv](http://php.net/iconv) (for charset conversion), [ZIP](http://php.net/zip) (for import/export), [zlib](http://php.net/zlib) (for compressed feeds) * MySQL 5.5.3+ (recommended), or SQLite 3.7.4+, or PostgreSQL (experimental) * A recent browser like Firefox, Internet Explorer 11 / Edge, Chrome, Opera, Safari. * Works on mobile diff --git a/app/Controllers/categoryController.php b/app/Controllers/categoryController.php index e65c146de..922f92844 100644 --- a/app/Controllers/categoryController.php +++ b/app/Controllers/categoryController.php @@ -117,7 +117,6 @@ class FreshRSS_category_Controller extends Minz_ActionController { public function deleteAction() { $feedDAO = FreshRSS_Factory::createFeedDao(); $catDAO = new FreshRSS_CategoryDAO(); - $default_category = $catDAO->getDefault(); $url_redirect = array('c' => 'subscription', 'a' => 'index'); if (Minz_Request::isPost()) { @@ -128,11 +127,11 @@ class FreshRSS_category_Controller extends Minz_ActionController { Minz_Request::bad(_t('feedback.sub.category.no_id'), $url_redirect); } - if ($id === $default_category->id()) { + if ($id === FreshRSS_CategoryDAO::defaultCategoryId) { Minz_Request::bad(_t('feedback.sub.category.not_delete_default'), $url_redirect); } - if ($feedDAO->changeCategory($id, $default_category->id()) === false) { + if ($feedDAO->changeCategory($id, FreshRSS_CategoryDAO::defaultCategoryId) === false) { Minz_Request::bad(_t('feedback.sub.category.error'), $url_redirect); } diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index ed3229687..c4115584a 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -40,9 +40,8 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } if ($cat == null) { $catDAO->checkDefault(); - $cat = $catDAO->getDefault(); } - $cat_id = $cat->id(); + $cat_id = $cat == null ? FreshRSS_CategoryDAO::defaultCategoryId : $cat->id(); $feed = new FreshRSS_Feed($url); //Throws FreshRSS_BadUrl_Exception $feed->_httpAuth($http_auth); @@ -504,8 +503,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } if ($cat_id <= 1) { $catDAO->checkDefault(); - $cat = $catDAO->getDefault(); - $cat_id = $cat->id(); + $cat_id = FreshRSS_CategoryDAO::defaultCategoryId; } $feedDAO = FreshRSS_Factory::createFeedDao(); diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index a1f789805..fbebb2a78 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -29,32 +29,14 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { Minz_View::prependTitle(_t('sub.import_export.title') . ' · '); } - /** - * This action handles import action. - * - * It must be reached by a POST request. - * - * Parameter is: - * - file (default: nothing!) - * Available file types are: zip, json or xml. - */ - public function importAction() { - if (!Minz_Request::isPost()) { - Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true); - } - - $file = $_FILES['file']; - $status_file = $file['error']; - - if ($status_file !== 0) { - Minz_Log::warning('File cannot be uploaded. Error code: ' . $status_file); - Minz_Request::bad(_t('feedback.import_export.file_cannot_be_uploaded'), - array('c' => 'importExport', 'a' => 'index')); - } + public function importFile($name, $path, $username = null) { + require_once(LIB_PATH . '/lib_opml.php'); - @set_time_limit(300); + $this->catDAO = new FreshRSS_CategoryDAO($username); + $this->entryDAO = FreshRSS_Factory::createEntryDao($username); + $this->feedDAO = FreshRSS_Factory::createFeedDao($username); - $type_file = $this->guessFileType($file['name']); + $type_file = self::guessFileType($name); $list_files = array( 'opml' => array(), @@ -65,21 +47,17 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { // We try to list all files according to their type $list = array(); if ($type_file === 'zip' && extension_loaded('zip')) { - $zip = zip_open($file['tmp_name']); - + $zip = zip_open($path); if (!is_resource($zip)) { // zip_open cannot open file: something is wrong - Minz_Log::warning('Zip archive cannot be imported. Error code: ' . $zip); - Minz_Request::bad(_t('feedback.import_export.zip_error'), - array('c' => 'importExport', 'a' => 'index')); + throw new FreshRSS_Zip_Exception($zip); } - while (($zipfile = zip_read($zip)) !== false) { if (!is_resource($zipfile)) { // zip_entry() can also return an error code! - Minz_Log::warning('Zip file cannot be imported. Error code: ' . $zipfile); + throw new FreshRSS_Zip_Exception($zipfile); } else { - $type_zipfile = $this->guessFileType(zip_entry_name($zipfile)); + $type_zipfile = self::guessFileType(zip_entry_name($zipfile)); if ($type_file !== 'unknown') { $list_files[$type_zipfile][] = zip_entry_read( $zipfile, @@ -88,29 +66,82 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } } } - zip_close($zip); } elseif ($type_file === 'zip') { - // Zip extension is not loaded - Minz_Request::bad(_t('feedback.import_export.no_zip_extension'), - array('c' => 'importExport', 'a' => 'index')); + // ZIP extension is not loaded + throw new FreshRSS_ZipMissing_Exception(); } elseif ($type_file !== 'unknown') { - $list_files[$type_file][] = file_get_contents($file['tmp_name']); + $list_files[$type_file][] = file_get_contents($path); } // Import file contents. // 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; + $ok = true; foreach ($list_files['opml'] as $opml_file) { - $error = $this->importOpml($opml_file); + if (!$this->importOpml($opml_file)) { + $ok = false; + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error during OPML import' . "\n"); + } + } } foreach ($list_files['json_starred'] as $article_file) { - $error = $this->importJson($article_file, true); + if (!$this->importJson($article_file, true)) { + $ok = false; + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error during JSON stars import' . "\n"); + } + } } foreach ($list_files['json_feed'] as $article_file) { - $error = $this->importJson($article_file); + if (!$this->importJson($article_file)) { + $ok = false; + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error during JSON feeds import' . "\n"); + } + } + } + + return $ok; + } + + /** + * This action handles import action. + * + * It must be reached by a POST request. + * + * Parameter is: + * - file (default: nothing!) + * Available file types are: zip, json or xml. + */ + public function importAction() { + if (!Minz_Request::isPost()) { + Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true); + } + + $file = $_FILES['file']; + $status_file = $file['error']; + + if ($status_file !== 0) { + Minz_Log::warning('File cannot be uploaded. Error code: ' . $status_file); + Minz_Request::bad(_t('feedback.import_export.file_cannot_be_uploaded'), + array('c' => 'importExport', 'a' => 'index')); + } + + @set_time_limit(300); + + $error = false; + try { + $error = !$this->importFile($file['name'], $file['tmp_name']); + } catch (FreshRSS_ZipMissing_Exception $zme) { + Minz_Request::bad(_t('feedback.import_export.no_zip_extension'), + array('c' => 'importExport', 'a' => 'index')); + } catch (FreshRSS_Zip_Exception $ze) { + Minz_Log::warning('ZIP archive cannot be imported. Error code: ' . $ze->zipErrorCode()); + Minz_Request::bad(_t('feedback.import_export.zip_error'), + array('c' => 'importExport', 'a' => 'index')); } // And finally, we get import status and redirect to the home page @@ -126,7 +157,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * Itis a *very* basic guess file type function. Only based on filename. * That's could be improved but should be enough for what we have to do. */ - private function guessFileType($filename) { + private static function guessFileType($filename) { if (substr_compare($filename, '.zip', -4) === 0) { return 'zip'; } elseif (substr_compare($filename, '.opml', -5) === 0 || @@ -146,15 +177,19 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * This method parses and imports an OPML file. * * @param string $opml_file the OPML file content. - * @return boolean true if an error occured, false else. + * @return boolean false if an error occured, true otherwise. */ private function importOpml($opml_file) { $opml_array = array(); try { $opml_array = libopml_parse_string($opml_file, false); } catch (LibOPML_Exception $e) { - Minz_Log::warning($e->getMessage()); - return true; + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error during OPML parsing: ' . $e->getMessage() . "\n"); + } else { + Minz_Log::warning($e->getMessage()); + } + return false; } $this->catDAO->checkDefault(); @@ -167,51 +202,53 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * * @param array $opml_elements an OPML element (body or outline). * @param string $parent_cat the name of the parent category. - * @return boolean true if an error occured, false else. + * @return boolean false if an error occured, true otherwise. */ private function addOpmlElements($opml_elements, $parent_cat = null) { - $error = false; + $ok = true; $nb_feeds = count($this->feedDAO->listFeeds()); $nb_cats = count($this->catDAO->listCategories(false)); $limits = FreshRSS_Context::$system_conf->limits; foreach ($opml_elements as $elt) { - $is_error = false; if (isset($elt['xmlUrl'])) { // If xmlUrl exists, it means it is a feed - if ($nb_feeds >= $limits['max_feeds']) { - Minz_Log::warning(_t('feedback.sub.feed.over_max', - $limits['max_feeds'])); - $is_error = true; - continue; + if (!FreshRSS_Context::$isCli) { + if ($nb_feeds >= $limits['max_feeds']) { + Minz_Log::warning(_t('feedback.sub.feed.over_max', + $limits['max_feeds'])); + $ok = false; + continue; + } } - $is_error = $this->addFeedOpml($elt, $parent_cat); - if (!$is_error) { - $nb_feeds += 1; + if ($this->addFeedOpml($elt, $parent_cat)) { + $nb_feeds++; + } else { + $ok = false; } } else { // No xmlUrl? It should be a category! $limit_reached = ($nb_cats >= $limits['max_categories']); - if ($limit_reached) { - Minz_Log::warning(_t('feedback.sub.category.over_max', - $limits['max_categories'])); + if (!FreshRSS_Context::$isCli) { + if ($limit_reached) { + Minz_Log::warning(_t('feedback.sub.category.over_max', + $limits['max_categories'])); + } + $ok = false; + continue; } - $is_error = $this->addCategoryOpml($elt, $parent_cat, $limit_reached); - if (!$is_error) { - $nb_cats += 1; + if ($this->addCategoryOpml($elt, $parent_cat, $limit_reached)) { + $nb_cats++; + } else { + $ok = false; } } - - if (!$error && $is_error) { - // oops: there is at least one error! - $error = $is_error; - } } - return $error; + return $ok; } /** @@ -219,21 +256,23 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * * @param array $feed_elt an OPML element (must be a feed element). * @param string $parent_cat the name of the parent category. - * @return boolean true if an error occured, false else. + * @return boolean false if an error occured, true otherwise. */ private function addFeedOpml($feed_elt, $parent_cat) { - $default_cat = $this->catDAO->getDefault(); - if (is_null($parent_cat)) { + if ($parent_cat == null) { // This feed has no parent category so we get the default one + $this->catDAO->checkDefault(); + $default_cat = $this->catDAO->getDefault(); $parent_cat = $default_cat->name(); } $cat = $this->catDAO->searchByName($parent_cat); - if (is_null($cat)) { + if ($cat == null) { // If there is not $cat, it means parent category does not exist in // database. // If it happens, take the default category. - $cat = $default_cat; + $this->catDAO->checkDefault(); + $cat = $this->catDAO->getDefault(); } // We get different useful information @@ -259,7 +298,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { // Call the extension hook $feed = Minz_ExtensionManager::callHook('feed_before_insert', $feed); - if (!is_null($feed)) { + if ($feed != null) { // addFeedObject checks if feed is already in DB so nothing else to // check here $id = $this->feedDAO->addFeedObject($feed); @@ -268,11 +307,23 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $error = true; } } catch (FreshRSS_Feed_Exception $e) { - Minz_Log::warning($e->getMessage()); + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error during OPML feed import: ' . $e->getMessage() . "\n"); + } else { + Minz_Log::warning($e->getMessage()); + } $error = true; } - return $error; + if ($error) { + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error during OPML feed import from URL: ' . $url . ' in category ' . $cat->id() . "\n"); + } else { + Minz_Log::warning('Error during OPML feed import from URL: ' . $url . ' in category ' . $cat->id()); + } + } + + return !$error; } /** @@ -282,29 +333,34 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * @param string $parent_cat the name of the parent category. * @param boolean $cat_limit_reached indicates if category limit has been reached. * if yes, category is not added (but we try for feeds!) - * @return boolean true if an error occured, false else. + * @return boolean false if an error occured, true otherwise. */ private function addCategoryOpml($cat_elt, $parent_cat, $cat_limit_reached) { // Create a new Category object - $cat = new FreshRSS_Category(Minz_Helper::htmlspecialchars_utf8($cat_elt['text'])); + $catName = Minz_Helper::htmlspecialchars_utf8($cat_elt['text']); + $cat = new FreshRSS_Category($catName); $error = true; - if (!$cat_limit_reached) { + if (FreshRSS_Context::$isCli || !$cat_limit_reached) { $id = $this->catDAO->addCategoryObject($cat); $error = ($id === false); } + if ($error) { + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error during OPML category import from URL: ' . $catName . "\n"); + } else { + Minz_Log::warning('Error during OPML category import from URL: ' . $catName); + } + } 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; - } + $error &= !$this->addOpmlElements($cat_elt['@outlines'], $catName); } - return $error; + return !$error; } /** @@ -312,13 +368,17 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * * @param string $article_file the JSON file content. * @param boolean $starred true if articles from the file must be starred. - * @return boolean true if an error occured, false else. + * @return boolean false if an error occured, true otherwise. */ private function importJson($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; + if ($article_object == null) { + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error trying to import a non-JSON file' . "\n"); + } else { + Minz_Log::warning('Try to import a non-JSON file'); + } + return false; } $is_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0; @@ -337,25 +397,24 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $feed = new FreshRSS_Feed($item['origin'][$key]); $feed = $this->feedDAO->searchByUrl($feed->url()); - if (is_null($feed)) { + if ($feed == null) { // Feed does not exist in DB,we should to try to add it. - if ($nb_feeds >= $limits['max_feeds']) { + if ((!FreshRSS_Context::$isCli) && ($nb_feeds >= $limits['max_feeds'])) { // Oops, no more place! Minz_Log::warning(_t('feedback.sub.feed.over_max', $limits['max_feeds'])); } else { $feed = $this->addFeedJson($item['origin'], $google_compliant); } - if (is_null($feed)) { + if ($feed == null) { // Still null? It means something went wrong. $error = true; } else { - // Nice! Increase the counter. - $nb_feeds += 1; + $nb_feeds++; } } - if (!is_null($feed)) { + if ($feed != null) { $article_to_feed[$item['id']] = $feed->id(); } } @@ -384,7 +443,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if ($google_compliant) { // Remove tags containing "/state/com.google" which are useless. $tags = array_filter($tags, function($var) { - return strpos($var, '/state/com.google') === false; + return strpos($var, '/state/com.google') !== false; }); } @@ -397,7 +456,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $entry->_tags($tags); $entry = Minz_ExtensionManager::callHook('entry_before_insert', $entry); - if (is_null($entry)) { + if ($entry == null) { // An extension has returned a null value, there is nothing to insert. continue; } @@ -415,7 +474,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } $this->entryDAO->commit(); - return $error; + return !$error; } /** @@ -427,8 +486,6 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * else null. */ private function addFeedJson($origin, $google_compliant) { - $default_cat = $this->catDAO->getDefault(); - $return = null; $key = $google_compliant ? 'htmlUrl' : 'feedUrl'; $url = $origin[$key]; @@ -438,13 +495,13 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { try { // Create a Feed object and add it in database. $feed = new FreshRSS_Feed($url); - $feed->_category($default_cat->id()); + $feed->_category(FreshRSS_CategoryDAO::defaultCategoryId); $feed->_name($name); $feed->_website($website); // Call the extension hook $feed = Minz_ExtensionManager::callHook('feed_before_insert', $feed); - if (!is_null($feed)) { + if ($feed != null) { // addFeedObject checks if feed is already in DB so nothing else to // check here. $id = $this->feedDAO->addFeedObject($feed); @@ -455,7 +512,11 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } } } catch (FreshRSS_Feed_Exception $e) { - Minz_Log::warning($e->getMessage()); + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS error during JSON feed import: ' . $e->getMessage() . "\n"); + } else { + Minz_Log::warning($e->getMessage()); + } } return $return; @@ -503,18 +564,18 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $nb_files = count($export_files); if ($nb_files > 1) { - // If there are more than 1 file to export, we need a zip archive. + // If there are more than 1 file to export, we need a ZIP archive. try { $this->exportZip($export_files); } catch (Exception $e) { - # Oops, there is no Zip extension! + # Oops, there is no ZIP extension! Minz_Request::bad(_t('feedback.import_export.export_no_zip_extension'), array('c' => 'importExport', 'a' => 'index')); } } elseif ($nb_files === 1) { // Only one file? Guess its type and export it. $filename = key($export_files); - $type = $this->guessFileType($filename); + $type = self::guessFileType($filename); $this->exportFile('freshrss_' . $filename, $export_files[$filename], $type); } else { // Nothing to do... @@ -555,7 +616,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $this->view->entries = $this->entryDAO->listWhere( 's', '', FreshRSS_Entry::STATE_ALL, 'ASC', $unread_fav['all'] ); - } elseif ($type == 'feed' && !is_null($feed)) { + } elseif ($type === 'feed' && $feed != null) { $this->view->list_title = _t('sub.import_export.feed_list', $feed->name()); $this->view->type = 'feed/' . $feed->id(); $this->view->entries = $this->entryDAO->listWhere( diff --git a/app/Exceptions/ZipException.php b/app/Exceptions/ZipException.php new file mode 100644 index 000000000..8441daedf --- /dev/null +++ b/app/Exceptions/ZipException.php @@ -0,0 +1,14 @@ +zipErrorCode = $zipErrorCode; + } + + public function zipErrorCode() { + return $this->zipErrorCode; + } +} diff --git a/app/Exceptions/ZipMissingException.php b/app/Exceptions/ZipMissingException.php new file mode 100644 index 000000000..864cc3991 --- /dev/null +++ b/app/Exceptions/ZipMissingException.php @@ -0,0 +1,4 @@ +prefix . 'category`(name) VALUES(?)'; $stm = $this->bd->prepare($sql); @@ -50,7 +53,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable } public function deleteCategory($id) { - if ($id <= 1) { + if ($id <= self::defaultCategoryId) { return false; } $sql = 'DELETE FROM `' . $this->prefix . 'category` WHERE id=?'; @@ -120,7 +123,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable } public function getDefault() { - $sql = 'SELECT * FROM `' . $this->prefix . 'category` WHERE id=1'; + $sql = 'SELECT * FROM `' . $this->prefix . 'category` WHERE id=' . self::defaultCategoryId; $stm = $this->bd->prepare($sql); $stm->execute(); @@ -134,11 +137,11 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable } } public function checkDefault() { - $def_cat = $this->searchById(1); + $def_cat = $this->searchById(self::defaultCategoryId); if ($def_cat == null) { $cat = new FreshRSS_Category(_t('gen.short.default_category')); - $cat->_id(1); + $cat->_id(self::defaultCategoryId); $values = array( 'id' => $cat->id(), diff --git a/app/i18n/cz/feedback.php b/app/i18n/cz/feedback.php index 81302afca..f2bd87c77 100644 --- a/app/i18n/cz/feedback.php +++ b/app/i18n/cz/feedback.php @@ -43,12 +43,12 @@ return array( 'not_found' => '%s neexistuje', ), 'import_export' => array( - 'export_no_zip_extension' => 'Na serveru není naistalována podpora zip. Zkuste prosím exportovat soubory jeden po druhém.', + 'export_no_zip_extension' => 'Na serveru není naistalována podpora ZIP. Zkuste prosím exportovat soubory jeden po druhém.', 'feeds_imported' => 'Vaše kanály byly naimportovány a nyní budou aktualizovány', 'feeds_imported_with_errors' => 'Vaše kanály byly naimportovány, došlo ale k nějakým chybám', 'file_cannot_be_uploaded' => 'Soubor nelze nahrát!', - 'no_zip_extension' => 'Na serveru není naistalována podpora zip.', - 'zip_error' => 'Během importu zip souboru došlo k chybě.', + 'no_zip_extension' => 'Na serveru není naistalována podpora ZIP.', + 'zip_error' => 'Během importu ZIP souboru došlo k chybě.', ), 'sub' => array( 'actualize' => 'Aktualizovat', diff --git a/app/i18n/cz/sub.php b/app/i18n/cz/sub.php index cea0541e3..274cf16e1 100644 --- a/app/i18n/cz/sub.php +++ b/app/i18n/cz/sub.php @@ -44,8 +44,8 @@ return array( 'export_opml' => 'Exportovat seznam kanálů (OPML)', 'export_starred' => 'Exportovat oblíbené', 'feed_list' => 'Seznam %s článků', - 'file_to_import' => 'Soubor k importu
(OPML, Json nebo Zip)', - 'file_to_import_no_zip' => 'Soubor k importu
(OPML nebo Json)', + 'file_to_import' => 'Soubor k importu
(OPML, JSON nebo ZIP)', + 'file_to_import_no_zip' => 'Soubor k importu
(OPML nebo JSON)', 'import' => 'Import', 'starred_list' => 'Seznam oblíbených článků', 'title' => 'Import / export', diff --git a/app/i18n/de/feedback.php b/app/i18n/de/feedback.php index f93992982..195083b36 100644 --- a/app/i18n/de/feedback.php +++ b/app/i18n/de/feedback.php @@ -43,12 +43,12 @@ return array( 'not_found' => '%s existiert nicht', ), 'import_export' => array( - 'export_no_zip_extension' => 'Die Zip-Erweiterung fehlt auf Ihrem Server. Bitte versuchen Sie die Dateien eine nach der anderen zu exportieren.', + 'export_no_zip_extension' => 'Die ZIP-Erweiterung fehlt auf Ihrem Server. Bitte versuchen Sie die Dateien eine nach der anderen zu exportieren.', 'feeds_imported' => 'Ihre Feeds sind importiert worden und werden jetzt aktualisiert', 'feeds_imported_with_errors' => 'Ihre Feeds sind importiert worden, aber es traten einige Fehler auf', 'file_cannot_be_uploaded' => 'Die Datei kann nicht hochgeladen werden!', - 'no_zip_extension' => 'Die Zip-Erweiterung ist auf Ihrem Server nicht vorhanden.', - 'zip_error' => 'Ein Fehler trat während des Zip-Imports auf.', + 'no_zip_extension' => 'Die ZIP-Erweiterung ist auf Ihrem Server nicht vorhanden.', + 'zip_error' => 'Ein Fehler trat während des ZIP-Imports auf.', ), 'sub' => array( 'actualize' => 'Aktualisieren', diff --git a/app/i18n/de/sub.php b/app/i18n/de/sub.php index 0f05a5635..cc98fd25c 100644 --- a/app/i18n/de/sub.php +++ b/app/i18n/de/sub.php @@ -44,7 +44,7 @@ return array( 'export_opml' => 'Liste der Feeds exportieren (OPML)', 'export_starred' => 'Ihre Favoriten exportieren', 'feed_list' => 'Liste von %s Artikeln', - 'file_to_import' => 'Zu importierende Datei
(OPML, JSON oder Zip)', + 'file_to_import' => 'Zu importierende Datei
(OPML, JSON oder ZIP)', 'file_to_import_no_zip' => 'Zu importierende Datei
(OPML oder JSON)', 'import' => 'Importieren', 'starred_list' => 'Liste der Lieblingsartikel', diff --git a/app/i18n/en/feedback.php b/app/i18n/en/feedback.php index 7ce2ae9cf..e7f6b9f85 100644 --- a/app/i18n/en/feedback.php +++ b/app/i18n/en/feedback.php @@ -43,12 +43,12 @@ return array( 'not_found' => '%s does not exist', ), 'import_export' => array( - 'export_no_zip_extension' => 'Zip extension is not present on your server. Please try to export files one by one.', + 'export_no_zip_extension' => 'ZIP extension is not present on your server. Please try to export files one by one.', 'feeds_imported' => 'Your feeds have been imported and will now be updated', 'feeds_imported_with_errors' => 'Your feeds have been imported but some errors occurred', 'file_cannot_be_uploaded' => 'File cannot be uploaded!', - 'no_zip_extension' => 'Zip extension is not present on your server.', - 'zip_error' => 'An error occured during Zip import.', + 'no_zip_extension' => 'ZIP extension is not present on your server.', + 'zip_error' => 'An error occured during ZIP import.', ), 'sub' => array( 'actualize' => 'Actualise', diff --git a/app/i18n/en/sub.php b/app/i18n/en/sub.php index aaaa02827..789433ee6 100644 --- a/app/i18n/en/sub.php +++ b/app/i18n/en/sub.php @@ -44,8 +44,8 @@ return array( 'export_opml' => 'Export list of feeds (OPML)', 'export_starred' => 'Export your favourites', 'feed_list' => 'List of %s articles', - 'file_to_import' => 'File to import
(OPML, Json or Zip)', - 'file_to_import_no_zip' => 'File to import
(OPML or Json)', + 'file_to_import' => 'File to import
(OPML, JSON or ZIP)', + 'file_to_import_no_zip' => 'File to import
(OPML or JSON)', 'import' => 'Import', 'starred_list' => 'List of favourite articles', 'title' => 'Import / export', diff --git a/app/i18n/fr/feedback.php b/app/i18n/fr/feedback.php index 15f3ab859..5966fc3a7 100644 --- a/app/i18n/fr/feedback.php +++ b/app/i18n/fr/feedback.php @@ -43,12 +43,12 @@ return array( 'not_found' => '%s n’existe pas', ), 'import_export' => array( - 'export_no_zip_extension' => 'L’extension Zip n’est pas présente sur votre serveur. Veuillez essayer d’exporter les fichiers un par un.', + 'export_no_zip_extension' => 'L’extension ZIP n’est pas présente sur votre serveur. Veuillez essayer d’exporter les fichiers un par un.', 'feeds_imported' => 'Vos flux ont été importés et vont maintenant être actualisés.', 'feeds_imported_with_errors' => 'Vos flux ont été importés mais des erreurs sont survenues.', 'file_cannot_be_uploaded' => 'Le fichier ne peut pas être téléchargé !', - 'no_zip_extension' => 'L’extension Zip n’est pas présente sur votre serveur.', - 'zip_error' => 'Une erreur est survenue durant l’import du fichier Zip.', + 'no_zip_extension' => 'L’extension ZIP n’est pas présente sur votre serveur.', + 'zip_error' => 'Une erreur est survenue durant l’import du fichier ZIP.', ), 'sub' => array( 'actualize' => 'Actualiser', diff --git a/app/i18n/fr/sub.php b/app/i18n/fr/sub.php index e3631eb8b..bb3f2fefc 100644 --- a/app/i18n/fr/sub.php +++ b/app/i18n/fr/sub.php @@ -44,8 +44,8 @@ return array( 'export_opml' => 'Exporter la liste des flux (OPML)', 'export_starred' => 'Exporter les favoris', 'feed_list' => 'Liste des articles de %s', - 'file_to_import' => 'Fichier à importer
(OPML, Json ou Zip)', - 'file_to_import_no_zip' => 'Fichier à importer
(OPML ou Json)', + 'file_to_import' => 'Fichier à importer
(OPML, JSON ou ZIP)', + 'file_to_import_no_zip' => 'Fichier à importer
(OPML ou JSON)', 'import' => 'Importer', 'starred_list' => 'Liste des articles favoris', 'title' => 'Importer / exporter', diff --git a/app/i18n/it/feedback.php b/app/i18n/it/feedback.php index f217586b0..5851cb2e6 100644 --- a/app/i18n/it/feedback.php +++ b/app/i18n/it/feedback.php @@ -43,12 +43,12 @@ return array( 'not_found' => '%s non disponibile', ), 'import_export' => array( - 'export_no_zip_extension' => 'Estensione Zip non presente sul server. Per favore esporta i files singolarmente.', + 'export_no_zip_extension' => 'Estensione ZIP non presente sul server. Per favore esporta i files singolarmente.', 'feeds_imported' => 'I tuoi feed sono stati importati e saranno aggiornati', 'feeds_imported_with_errors' => 'I tuoi feeds sono stati importati ma si sono verificati alcuni errori', 'file_cannot_be_uploaded' => 'Il file non può essere caricato!', - 'no_zip_extension' => 'Estensione Zip non presente sul server.', - 'zip_error' => 'Si è verificato un errore importando il file Zip', + 'no_zip_extension' => 'Estensione ZIP non presente sul server.', + 'zip_error' => 'Si è verificato un errore importando il file ZIP', ), 'sub' => array( 'actualize' => 'Aggiorna', diff --git a/app/i18n/it/sub.php b/app/i18n/it/sub.php index dfcee2ce3..758e322c5 100644 --- a/app/i18n/it/sub.php +++ b/app/i18n/it/sub.php @@ -44,8 +44,8 @@ return array( 'export_opml' => 'Esporta tutta la lista dei feed (OPML)', 'export_starred' => 'Esporta i tuoi preferiti', 'feed_list' => 'Elenco di %s articoli', - 'file_to_import' => 'File da importare
(OPML, Json o Zip)', - 'file_to_import_no_zip' => 'File da importare
(OPML o Json)', + 'file_to_import' => 'File da importare
(OPML, JSON o ZIP)', + 'file_to_import_no_zip' => 'File da importare
(OPML o JSON)', 'import' => 'Importa', 'starred_list' => 'Elenco articoli preferiti', 'title' => 'Importa / esporta', diff --git a/app/i18n/nl/feedback.php b/app/i18n/nl/feedback.php index b703c43cf..386b8d415 100644 --- a/app/i18n/nl/feedback.php +++ b/app/i18n/nl/feedback.php @@ -43,12 +43,12 @@ return array( 'not_found' => '%s bestaat niet', ), 'import_export' => array( - 'export_no_zip_extension' => 'Zip uitbreiding is niet aanwezig op uw server. Exporteer a.u.b. uw bestanden één voor één.', + 'export_no_zip_extension' => 'ZIP uitbreiding is niet aanwezig op uw server. Exporteer a.u.b. uw bestanden één voor één.', 'feeds_imported' => 'Uw feeds zijn geimporteerd en worden nu vernieuwd', 'feeds_imported_with_errors' => 'Uw feeds zijn geimporteerd maar er zijn enige fouten opgetreden', 'file_cannot_be_uploaded' => 'Bestand kan niet worden verzonden!', - 'no_zip_extension' => 'Zip uitbreiding is niet aanwezig op uw server.', - 'zip_error' => 'Er is een fout opgetreden tijdens het imporeren van het Zip bestand.', + 'no_zip_extension' => 'ZIP uitbreiding is niet aanwezig op uw server.', + 'zip_error' => 'Er is een fout opgetreden tijdens het imporeren van het ZIP bestand.', ), 'sub' => array( 'actualize' => 'Actualiseren', diff --git a/app/i18n/nl/sub.php b/app/i18n/nl/sub.php index 159a58b27..a1ba3f03d 100644 --- a/app/i18n/nl/sub.php +++ b/app/i18n/nl/sub.php @@ -44,8 +44,8 @@ return array( 'export_opml' => 'Exporteer lijst van feeds (OPML)', 'export_starred' => 'Exporteer je fovorieten', 'feed_list' => 'Lijst van %s artikelen', - 'file_to_import' => 'Bestand om te importeren
(OPML, Json of Zip)', - 'file_to_import_no_zip' => 'Bestand om te importeren
(OPML of Json)', + 'file_to_import' => 'Bestand om te importeren
(OPML, JSON of ZIP)', + 'file_to_import_no_zip' => 'Bestand om te importeren
(OPML of JSON)', 'import' => 'Importeer', 'starred_list' => 'Lijst van favoriete artikelen', 'title' => 'Importeren / exporteren', diff --git a/app/i18n/ru/feedback.php b/app/i18n/ru/feedback.php index 7ce2ae9cf..e7f6b9f85 100644 --- a/app/i18n/ru/feedback.php +++ b/app/i18n/ru/feedback.php @@ -43,12 +43,12 @@ return array( 'not_found' => '%s does not exist', ), 'import_export' => array( - 'export_no_zip_extension' => 'Zip extension is not present on your server. Please try to export files one by one.', + 'export_no_zip_extension' => 'ZIP extension is not present on your server. Please try to export files one by one.', 'feeds_imported' => 'Your feeds have been imported and will now be updated', 'feeds_imported_with_errors' => 'Your feeds have been imported but some errors occurred', 'file_cannot_be_uploaded' => 'File cannot be uploaded!', - 'no_zip_extension' => 'Zip extension is not present on your server.', - 'zip_error' => 'An error occured during Zip import.', + 'no_zip_extension' => 'ZIP extension is not present on your server.', + 'zip_error' => 'An error occured during ZIP import.', ), 'sub' => array( 'actualize' => 'Actualise', diff --git a/app/i18n/ru/sub.php b/app/i18n/ru/sub.php index aaaa02827..789433ee6 100644 --- a/app/i18n/ru/sub.php +++ b/app/i18n/ru/sub.php @@ -44,8 +44,8 @@ return array( 'export_opml' => 'Export list of feeds (OPML)', 'export_starred' => 'Export your favourites', 'feed_list' => 'List of %s articles', - 'file_to_import' => 'File to import
(OPML, Json or Zip)', - 'file_to_import_no_zip' => 'File to import
(OPML or Json)', + 'file_to_import' => 'File to import
(OPML, JSON or ZIP)', + 'file_to_import_no_zip' => 'File to import
(OPML or JSON)', 'import' => 'Import', 'starred_list' => 'List of favourite articles', 'title' => 'Import / export', diff --git a/app/i18n/tr/feedback.php b/app/i18n/tr/feedback.php index a53316206..87361ff51 100644 --- a/app/i18n/tr/feedback.php +++ b/app/i18n/tr/feedback.php @@ -43,12 +43,12 @@ return array( 'not_found' => '%s bulunmamaktadır', ), 'import_export' => array( - 'export_no_zip_extension' => 'Zip eklentisi mevcut sunucunuzda yer almıyor. Lütfen başka dosya formatında dışarı aktarmayı deneyin.', + 'export_no_zip_extension' => 'ZIP eklentisi mevcut sunucunuzda yer almıyor. Lütfen başka dosya formatında dışarı aktarmayı deneyin.', 'feeds_imported' => 'Akışlarınız içe aktarıldı ve şimdi güncellenecek', 'feeds_imported_with_errors' => 'Akışlarınız içeri aktarıldı ama bazı hatalar meydana geldi', 'file_cannot_be_uploaded' => 'Dosya yüklenemedi!', - 'no_zip_extension' => 'Zip eklentisi mevcut sunucunuzda yer almıyor.', - 'zip_error' => 'Zip içe aktarımı sırasında hata meydana geldi.', + 'no_zip_extension' => 'ZIP eklentisi mevcut sunucunuzda yer almıyor.', + 'zip_error' => 'ZIP içe aktarımı sırasında hata meydana geldi.', ), 'sub' => array( 'actualize' => 'Güncelleme', diff --git a/app/i18n/tr/sub.php b/app/i18n/tr/sub.php index 5ab367ebb..7592096d9 100644 --- a/app/i18n/tr/sub.php +++ b/app/i18n/tr/sub.php @@ -44,8 +44,8 @@ return array( 'export_opml' => 'Akış listesini dışarı aktar (OPML)', 'export_starred' => 'Favorileri dışarı aktar', 'feed_list' => '%s makalenin listesi', - 'file_to_import' => 'Dosyadan içe aktar
(OPML, Json or Zip)', - 'file_to_import_no_zip' => 'Dosyadan içe aktar
(OPML or Json)', + 'file_to_import' => 'Dosyadan içe aktar
(OPML, JSON or ZIP)', + 'file_to_import_no_zip' => 'Dosyadan içe aktar
(OPML or JSON)', 'import' => 'İçe aktar', 'starred_list' => 'Favori makaleleirn listesi', 'title' => 'İçe / dışa aktar', diff --git a/cli/_cli.php b/cli/_cli.php index cb6d8ec32..d81d83d66 100644 --- a/cli/_cli.php +++ b/cli/_cli.php @@ -37,3 +37,8 @@ function cliInitUser($username) { return $username; } + +function done($ok) { + echo 'Result: ', ($ok ? 'success' : 'fail'), ".\n"; + exit($ok ? 0 : 1); +} diff --git a/cli/create-user.php b/cli/create-user.php index 08c057af8..387b503b6 100755 --- a/cli/create-user.php +++ b/cli/create-user.php @@ -15,19 +15,19 @@ if (empty($options['user'])) { fail('Usage: ' . basename(__FILE__) . " --user=username --password='password' --api-password='api_password'" . " --language=en --email=user@example.net --token='longRandomString'"); } -$new_user_name = $options['user']; -if (!ctype_alnum($new_user_name)) { - fail('FreshRSS error: invalid username “' . $new_user_name . '”'); +$username = $options['user']; +if (!ctype_alnum($username)) { + fail('FreshRSS error: invalid username “' . $username . '”'); } $usernames = listUsers(); -if (preg_grep("/^$new_user_name$/i", $usernames)) { - fail('FreshRSS error: username already taken “' . $new_user_name . '”'); +if (preg_grep("/^$username$/i", $usernames)) { + fail('FreshRSS error: username already taken “' . $username . '”'); } -echo 'FreshRSS creating user “', $new_user_name, "”…\n"; +echo 'FreshRSS creating user “', $username, "”…\n"; -$ok = FreshRSS_user_Controller::createUser($new_user_name, +$ok = FreshRSS_user_Controller::createUser($username, empty($options['password']) ? '' : $options['password'], empty($options['api-password']) ? '' : $options['api-password'], array( @@ -37,5 +37,4 @@ $ok = FreshRSS_user_Controller::createUser($new_user_name, invalidateHttpCache(FreshRSS_Context::$system_conf->default_user); -echo 'Result: ', ($ok ? 'success' : 'fail'), ".\n"; -exit($ok ? 0 : 1); +done($ok); diff --git a/cli/delete-user.php b/cli/delete-user.php index 46332fe34..da48103f7 100755 --- a/cli/delete-user.php +++ b/cli/delete-user.php @@ -29,5 +29,4 @@ $ok = FreshRSS_user_Controller::deleteUser($username); invalidateHttpCache(FreshRSS_Context::$system_conf->default_user); -echo 'Result: ', ($ok ? 'success' : 'fail'), ".\n"; -exit($ok ? 0 : 1); +done($ok); diff --git a/cli/import-for-user.php b/cli/import-for-user.php new file mode 100644 index 000000000..308786599 --- /dev/null +++ b/cli/import-for-user.php @@ -0,0 +1,36 @@ +#!/usr/bin/php +importFile($filename, $filename, $username); +} catch (FreshRSS_ZipMissing_Exception $zme) { + fail('FreshRSS error: Lacking php-zip extension!'); +} catch (FreshRSS_Zip_Exception $ze) { + fail('FreshRSS error: ZIP archive cannot be imported! Error code: ' . $ze->zipErrorCode()); +} +invalidateHttpCache($username); + +done($ok); diff --git a/cli/list-users.php b/cli/list-users.php index cc1cf5269..e690ff451 100755 --- a/cli/list-users.php +++ b/cli/list-users.php @@ -4,8 +4,8 @@ require('_cli.php'); $users = listUsers(); sort($users); -if ($system_conf->default_user !== '') { - array_unshift($users, $system_conf->default_user); +if (FreshRSS_Context::$system_conf->default_user !== '') { + array_unshift($users, FreshRSS_Context::$system_conf->default_user); $users = array_unique($users); } diff --git a/lib/Minz/ModelPdo.php b/lib/Minz/ModelPdo.php index da28909df..45139b0d6 100644 --- a/lib/Minz/ModelPdo.php +++ b/lib/Minz/ModelPdo.php @@ -36,22 +36,21 @@ class Minz_ModelPdo { * HOST, BASE, USER et PASS définies dans le fichier de configuration */ public function __construct($currentUser = null) { - if (self::$useSharedBd && self::$sharedBd != null && $currentUser === null) { + if ($currentUser === null) { + $currentUser = Minz_Session::param('currentUser', '_'); + } + if (self::$useSharedBd && self::$sharedBd != null && $currentUser === self::$sharedCurrentUser) { $this->bd = self::$sharedBd; $this->prefix = self::$sharedPrefix; $this->current_user = self::$sharedCurrentUser; return; } + $this->current_user = $currentUser; + self::$sharedCurrentUser = $currentUser; $conf = Minz_Configuration::get('system'); $db = $conf->db; - if ($currentUser === null) { - $currentUser = Minz_Session::param('currentUser', '_'); - } - $this->current_user = $currentUser; - self::$sharedCurrentUser = $currentUser; - $driver_options = isset($conf->db['pdo_options']) && is_array($conf->db['pdo_options']) ? $conf->db['pdo_options'] : array(); $dbServer = parse_url('db://' . $db['host']); -- cgit v1.2.3 From fcb9280fc87c159539f5832ab35f174cd515654e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 23 Oct 2016 13:37:48 +0200 Subject: CLI export ZIP export, OPML export. Corrected height of feeds select in Pafat theme. https://github.com/FreshRSS/FreshRSS/pull/1338 https://github.com/FreshRSS/FreshRSS/issues/1039 https://github.com/FreshRSS/FreshRSS/issues/1277 --- app/Controllers/importExportController.php | 84 +++++++++++++++++++----------- app/Models/FeedDAO.php | 7 +++ app/views/importExport/index.phtml | 2 +- cli/_cli.php | 4 +- cli/create-user.php | 14 ++--- cli/delete-user.php | 2 +- cli/export-opml-for-user.php | 24 +++++++++ cli/export-zip-for-user.php | 30 +++++++++++ cli/import-for-user.php | 3 +- p/themes/Pafat/pafat.css | 3 -- 10 files changed, 128 insertions(+), 45 deletions(-) create mode 100644 cli/export-opml-for-user.php create mode 100644 cli/export-zip-for-user.php (limited to 'cli/create-user.php') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index fbebb2a78..bb22ac739 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -522,26 +522,21 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { return $return; } - /** - * This action handles export action. - * - * This action must be reached by a POST request. - * - * Parameters are: - * - export_opml (default: false) - * - export_starred (default: false) - * - export_feeds (default: array()) a list of feed ids - */ - public function exportAction() { - if (!Minz_Request::isPost()) { - Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true); - } + public function exportFile($export_opml = true, $export_starred = false, $export_feeds = array(), $maxFeedEntries = 50, $username = null) { + require_once(LIB_PATH . '/lib_opml.php'); - $this->view->_useLayout(false); + $this->catDAO = new FreshRSS_CategoryDAO($username); + $this->entryDAO = FreshRSS_Factory::createEntryDao($username); + $this->feedDAO = FreshRSS_Factory::createFeedDao($username); + + if ($export_feeds === true) { + //All feeds + $export_feeds = $this->feedDAO->listFeedsIds(); + } + if (!is_array($export_feeds)) { + $export_feeds = array(); + } - $export_opml = Minz_Request::param('export_opml', false); - $export_starred = Minz_Request::param('export_starred', false); - $export_feeds = Minz_Request::param('export_feeds', array()); $day = date('Y-m-d'); $export_files = array(); @@ -558,7 +553,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); + $export_files[$filename] = $this->generateEntries('feed', $feed, $maxFeedEntries); } } @@ -566,18 +561,49 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if ($nb_files > 1) { // If there are more than 1 file to export, we need a ZIP archive. try { - $this->exportZip($export_files); + $this->sendZip($export_files); } catch (Exception $e) { - # Oops, there is no ZIP extension! - Minz_Request::bad(_t('feedback.import_export.export_no_zip_extension'), - array('c' => 'importExport', 'a' => 'index')); + throw new FreshRSS_ZipMissing_Exception($e); } } elseif ($nb_files === 1) { // Only one file? Guess its type and export it. $filename = key($export_files); $type = self::guessFileType($filename); - $this->exportFile('freshrss_' . $filename, $export_files[$filename], $type); - } else { + $this->sendFile('freshrss_' . $filename, $export_files[$filename], $type); + } + return $nb_files; + } + + /** + * This action handles export action. + * + * This action must be reached by a POST request. + * + * Parameters are: + * - export_opml (default: false) + * - export_starred (default: false) + * - export_feeds (default: array()) a list of feed ids + */ + public function exportAction() { + if (!Minz_Request::isPost()) { + Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true); + } + $this->view->_useLayout(false); + + $nb_files = 0; + try { + $nb_files = $this->exportFile( + Minz_Request::param('export_opml', false), + Minz_Request::param('export_starred', false), + Minz_Request::param('export_feeds', array()) + ); + } catch (FreshRSS_ZipMissing_Exception $zme) { + # Oops, there is no ZIP extension! + Minz_Request::bad(_t('feedback.import_export.export_no_zip_extension'), + array('c' => 'importExport', 'a' => 'index')); + } + + if ($nb_files < 1) { // Nothing to do... Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true); } @@ -606,7 +632,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * @param FreshRSS_Feed $feed feed of which we want to get entries. * @return string the JSON file content. */ - private function generateEntries($type, $feed = NULL) { + private function generateEntries($type, $feed = NULL, $maxFeedEntries = 50) { $this->view->categories = $this->catDAO->listCategories(); if ($type == 'starred') { @@ -621,7 +647,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $this->view->type = 'feed/' . $feed->id(); $this->view->entries = $this->entryDAO->listWhere( 'f', $feed->id(), FreshRSS_Entry::STATE_ALL, 'ASC', - FreshRSS_Context::$user_conf->posts_per_page + $maxFeedEntries ); $this->view->feed = $feed; } @@ -635,7 +661,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * @param array $files list of files where key is filename and value the content. * @throws Exception if Zip extension is not loaded. */ - private function exportZip($files) { + private function sendZip($files) { if (!extension_loaded('zip')) { throw new Exception(); } @@ -667,7 +693,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * @param string $type the file type (opml, json_feed or json_starred). * If equals to unknown, nothing happens. */ - private function exportFile($filename, $content, $type) { + private function sendFile($filename, $content, $type) { if ($type === 'unknown') { return; } diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index b21f19b66..68398efd5 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -212,6 +212,13 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } } + public function listFeedsIds() { + $sql = 'SELECT id FROM `' . $this->prefix . 'feed`'; + $stm = $this->bd->prepare($sql); + $stm->execute(); + return $stm->fetchAll(PDO::FETCH_COLUMN, 0); + } + public function listFeeds() { $sql = 'SELECT * FROM `' . $this->prefix . 'feed` ORDER BY name'; $stm = $this->bd->prepare($sql); diff --git a/app/views/importExport/index.phtml b/app/views/importExport/index.phtml index c0bc412c3..c5049e3ea 100644 --- a/app/views/importExport/index.phtml +++ b/app/views/importExport/index.phtml @@ -44,7 +44,7 @@ $select_args = ' size="' . min(10, count($this->feeds)) .'" multiple="multiple"'; } ?> - size="10"> '; ?> feeds as $feed) { ?> diff --git a/cli/_cli.php b/cli/_cli.php index d81d83d66..66506f07a 100644 --- a/cli/_cli.php +++ b/cli/_cli.php @@ -10,7 +10,7 @@ Minz_Configuration::register('system', DATA_PATH . '/config.php', DATA_PATH . '/config.default.php'); FreshRSS_Context::$system_conf = Minz_Configuration::get('system'); -Minz_Translate::init(); +Minz_Translate::init('en'); FreshRSS_Context::$isCli = true; @@ -39,6 +39,6 @@ function cliInitUser($username) { } function done($ok) { - echo 'Result: ', ($ok ? 'success' : 'fail'), ".\n"; + fwrite(STDERR, 'Result: ' . ($ok ? 'success' : 'fail') . "\n"); exit($ok ? 0 : 1); } diff --git a/cli/create-user.php b/cli/create-user.php index 387b503b6..243e65a35 100755 --- a/cli/create-user.php +++ b/cli/create-user.php @@ -4,16 +4,16 @@ require('_cli.php'); $options = getopt('', array( 'user:', - 'password::', - 'api-password::', - 'language::', - 'email::', - 'token::', + 'password:', + 'api-password:', + 'language:', + 'email:', + 'token:', )); if (empty($options['user'])) { - fail('Usage: ' . basename(__FILE__) . " --user=username --password='password' --api-password='api_password'" . - " --language=en --email=user@example.net --token='longRandomString'"); + fail('Usage: ' . basename(__FILE__) . " --user username --password 'password' --api-password 'api_password'" . + " --language en --email user@example.net --token 'longRandomString'"); } $username = $options['user']; if (!ctype_alnum($username)) { diff --git a/cli/delete-user.php b/cli/delete-user.php index da48103f7..6f0e86e17 100755 --- a/cli/delete-user.php +++ b/cli/delete-user.php @@ -7,7 +7,7 @@ $options = getopt('', array( )); if (empty($options['user'])) { - fail('Usage: ' . basename(__FILE__) . " --user=username"); + fail('Usage: ' . basename(__FILE__) . " --user username"); } $username = $options['user']; if (!ctype_alnum($username)) { diff --git a/cli/export-opml-for-user.php b/cli/export-opml-for-user.php new file mode 100644 index 000000000..bce4efd63 --- /dev/null +++ b/cli/export-opml-for-user.php @@ -0,0 +1,24 @@ +#!/usr/bin/php + /path/to/file.opml.xml'"); +} + +$username = cliInitUser($options['user']); + +fwrite(STDERR, 'FreshRSS exporting OPML for user “' . $username . "”…\n"); + +$importController = new FreshRSS_importExport_Controller(); + +$ok = false; +$ok = $importController->exportFile(true, false, array(), 0, $username); + +invalidateHttpCache($username); + +done($ok); diff --git a/cli/export-zip-for-user.php b/cli/export-zip-for-user.php new file mode 100644 index 000000000..f8d24238b --- /dev/null +++ b/cli/export-zip-for-user.php @@ -0,0 +1,30 @@ +#!/usr/bin/php + /path/to/file.zip'"); +} + +$username = cliInitUser($options['user']); + +fwrite(STDERR, 'FreshRSS exporting ZIP for user “' . $username . "”…\n"); + +$importController = new FreshRSS_importExport_Controller(); + +$ok = false; +try { + $ok = $importController->exportFile(true, true, true, + empty($options['max-feed-entries']) ? 100 : intval($options['max-feed-entries']), + $username); +} catch (FreshRSS_ZipMissing_Exception $zme) { + fail('FreshRSS error: Lacking php-zip extension!'); +} +invalidateHttpCache($username); + +done($ok); diff --git a/cli/import-for-user.php b/cli/import-for-user.php index 308786599..29084f062 100755 --- a/cli/import-for-user.php +++ b/cli/import-for-user.php @@ -5,11 +5,10 @@ require('_cli.php'); $options = getopt('', array( 'user:', 'filename:', - 'clear-existing', )); if (empty($options['user']) || empty($options['filename'])) { - fail('Usage: ' . basename(__FILE__) . " --user=username --filename='/path/to/file.ext' --clear-existing"); + fail('Usage: ' . basename(__FILE__) . " --user username --filename /path/to/file.ext"); } $username = cliInitUser($options['user']); diff --git a/p/themes/Pafat/pafat.css b/p/themes/Pafat/pafat.css index 5e46a63ca..abe808eab 100644 --- a/p/themes/Pafat/pafat.css +++ b/p/themes/Pafat/pafat.css @@ -48,9 +48,6 @@ input, select, textarea { vertical-align: middle; } -select{ - height:29px; -} option { padding: 0 .5em; } -- cgit v1.2.3 From ab4ece6780cf841f6ce4e89f7b81a1ff1661f615 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 24 Oct 2016 01:41:09 +0200 Subject: CLI do-install https://github.com/FreshRSS/FreshRSS/issues/1095 https://github.com/FreshRSS/FreshRSS/issues/1090 --- app/Controllers/userController.php | 5 +- app/install.php | 161 +++++++------------------------------ cli/_cli.php | 7 +- cli/create-user.php | 10 ++- cli/do-install.php | 102 +++++++++++++++++++++++ lib/lib_install.php | 115 ++++++++++++++++++++++++++ 6 files changed, 260 insertions(+), 140 deletions(-) create mode 100644 cli/do-install.php create mode 100644 lib/lib_install.php (limited to 'cli/create-user.php') diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index 2f04c7a1d..9dee16e8c 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -24,7 +24,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { } } - private static function hashPassword($passwordPlain) { + public static function hashPassword($passwordPlain) { if (!function_exists('password_hash')) { include_once(LIB_PATH . '/password_compat.php'); } @@ -112,9 +112,6 @@ class FreshRSS_user_Controller extends Minz_ActionController { $userConfig['language'] = 'en'; } - $default_user = FreshRSS_Context::$system_conf->default_user; - $ok &= (strcasecmp($new_user_name, $default_user) !== 0); //It is forbidden to alter the default user - $ok &= !in_array(strtoupper($new_user_name), array_map('strtoupper', listUsers())); //Not an existing user, case-insensitive $configPath = join_path(DATA_PATH, 'users', $new_user_name, 'config.php'); diff --git a/app/install.php b/app/install.php index 1972379e5..6956761c7 100644 --- a/app/install.php +++ b/app/install.php @@ -4,15 +4,12 @@ if (function_exists('opcache_reset')) { } header("Content-Security-Policy: default-src 'self'"); -define('BCRYPT_COST', 9); +require(LIB_PATH . '/lib_install.php'); session_name('FreshRSS'); session_set_cookie_params(0, dirname(empty($_SERVER['REQUEST_URI']) ? '/' : dirname($_SERVER['REQUEST_URI'])), null, false, true); session_start(); -Minz_Configuration::register('default_system', join_path(DATA_PATH, 'config.default.php')); -Minz_Configuration::register('default_user', join_path(USERS_PATH, '_', 'config.default.php')); - if (isset($_GET['step'])) { define('STEP',(int)$_GET['step']); } else { @@ -26,13 +23,13 @@ if (STEP === 3 && isset($_POST['type'])) { if (isset($_SESSION['bd_type'])) { switch ($_SESSION['bd_type']) { case 'mysql': - include(APP_PATH . '/SQL/install.sql.mysql.php'); + include_once(APP_PATH . '/SQL/install.sql.mysql.php'); break; case 'sqlite': - include(APP_PATH . '/SQL/install.sql.sqlite.php'); + include_once(APP_PATH . '/SQL/install.sql.sqlite.php'); break; case 'pgsql': - include(APP_PATH . '/SQL/install.sql.pgsql.php'); + include_once(APP_PATH . '/SQL/install.sql.pgsql.php'); break; } } @@ -131,12 +128,7 @@ function saveStep2() { $password_plain = param('passwordPlain', false); if ($password_plain !== false && cryptAvailable()) { - if (!function_exists('password_hash')) { - include_once(LIB_PATH . '/password_compat.php'); - } - $passwordHash = password_hash($password_plain, PASSWORD_BCRYPT, array('cost' => BCRYPT_COST)); - $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js - $_SESSION['passwordHash'] = $passwordHash; + $_SESSION['passwordHash'] = FreshRSS_user_Controller::hashPassword($password_plain); } if (empty($_SESSION['old_entries']) || @@ -149,7 +141,7 @@ function saveStep2() { return false; } - $_SESSION['salt'] = sha1(uniqid(mt_rand(), true).implode('', stat(__FILE__))); + $_SESSION['salt'] = generateSalt(); if ((!ctype_digit($_SESSION['old_entries'])) ||($_SESSION['old_entries'] < 1)) { $_SESSION['old_entries'] = $user_default_config->old_entries; } @@ -171,7 +163,7 @@ function saveStep2() { recursive_unlink($user_dir); mkdir($user_dir); - file_put_contents($user_config_path, " 0 && $s0['all'] != 'ok') { @@ -279,49 +265,6 @@ function checkStep0() { ); } -function checkStep1() { - $php = version_compare(PHP_VERSION, '5.3.3') >= 0; - $minz = file_exists(join_path(LIB_PATH, 'Minz')); - $curl = extension_loaded('curl'); - $pdo_mysql = extension_loaded('pdo_mysql'); - $pdo_sqlite = extension_loaded('pdo_sqlite'); - $pdo_pgsql = extension_loaded('pdo_pgsql'); - $pdo = $pdo_mysql || $pdo_sqlite || $pdo_pgsql; - $pcre = extension_loaded('pcre'); - $ctype = extension_loaded('ctype'); - $dom = class_exists('DOMDocument'); - $xml = function_exists('xml_parser_create'); - $json = function_exists('json_encode'); - $data = DATA_PATH && is_writable(DATA_PATH); - $cache = CACHE_PATH && is_writable(CACHE_PATH); - $users = USERS_PATH && is_writable(USERS_PATH); - $favicons = is_writable(join_path(DATA_PATH, 'favicons')); - $http_referer = is_referer_from_same_domain(); - - return array( - 'php' => $php ? 'ok' : 'ko', - 'minz' => $minz ? 'ok' : 'ko', - 'curl' => $curl ? 'ok' : 'ko', - 'pdo-mysql' => $pdo_mysql ? 'ok' : 'ko', - 'pdo-sqlite' => $pdo_sqlite ? 'ok' : 'ko', - 'pdo-pgsql' => $pdo_pgsql ? 'ok' : 'ko', - 'pdo' => $pdo ? 'ok' : 'ko', - 'pcre' => $pcre ? 'ok' : 'ko', - 'ctype' => $ctype ? 'ok' : 'ko', - 'dom' => $dom ? 'ok' : 'ko', - 'xml' => $xml ? 'ok' : 'ko', - 'json' => $json ? 'ok' : 'ko', - 'data' => $data ? 'ok' : 'ko', - 'cache' => $cache ? 'ok' : 'ko', - 'users' => $users ? 'ok' : 'ko', - 'favicons' => $favicons ? 'ok' : 'ko', - 'http_referer' => $http_referer ? 'ok' : 'ko', - 'all' => $php && $minz && $curl && $pdo && $pcre && $ctype && $dom && $xml && - $data && $cache && $users && $favicons && $http_referer ? - 'ok' : 'ko' - ); -} - function freshrss_already_installed() { $conf_path = join_path(DATA_PATH, 'config.php'); if (!file_exists($conf_path)) { @@ -392,60 +335,15 @@ function checkStep3() { ); } -function checkBD() { +function checkDbUser(&$dbOptions) { $ok = false; - + $str = $dbOptions['bd_dsn']; + $driver_options = $dbOptions['bd_options']; try { - $str = ''; - $driver_options = null; - switch ($_SESSION['bd_type']) { - case 'mysql': - $driver_options = array( - PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4' - ); - - try { // on ouvre une connexion juste pour créer la base si elle n'existe pas - $str = 'mysql:host=' . $_SESSION['bd_host'] . ';'; - $c = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); - $sql = sprintf(SQL_CREATE_DB, $_SESSION['bd_base']); - $res = $c->query($sql); - } catch (PDOException $e) { - } - - // on écrase la précédente connexion en sélectionnant la nouvelle BDD - $str = 'mysql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base']; - break; - case 'sqlite': - $str = 'sqlite:' . join_path(USERS_PATH, $_SESSION['default_user'], 'db.sqlite'); - $driver_options = array( - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - ); - break; - case 'pgsql': - $driver_options = array( - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - ); - - try { // on ouvre une connexion juste pour créer la base si elle n'existe pas - $str = 'pgsql:host=' . $_SESSION['bd_host'] . ';dbname=postgres'; - $c = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); - $sql = sprintf(SQL_CREATE_DB, $_SESSION['bd_base']); - $res = $c->query($sql); - } catch (PDOException $e) { - syslog(LOG_DEBUG, 'pgsql ' . $e->getMessage()); - } - - // on écrase la précédente connexion en sélectionnant la nouvelle BDD - $str = 'pgsql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base']; - break; - default: - return false; - } - - $c = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); + $c = new PDO($str, $dbOptions['bd_user'], $dbOptions['bd_password'], $driver_options); if (defined('SQL_CREATE_TABLES')) { - $sql = sprintf(SQL_CREATE_TABLES, $_SESSION['bd_prefix_user'], _t('gen.short.default_category')); + $sql = sprintf(SQL_CREATE_TABLES, $dbOptions['bd_prefix_user'], _t('gen.short.default_category')); $stm = $c->prepare($sql); $ok = $stm->execute(); } else { @@ -453,7 +351,7 @@ function checkBD() { if (is_array($SQL_CREATE_TABLES)) { $ok = true; foreach ($SQL_CREATE_TABLES as $instruction) { - $sql = sprintf($instruction, $_SESSION['bd_prefix_user'], _t('gen.short.default_category')); + $sql = sprintf($instruction, $dbOptions['bd_prefix_user'], _t('gen.short.default_category')); $stm = $c->prepare($sql); $ok &= $stm->execute(); } @@ -461,13 +359,8 @@ function checkBD() { } } catch (PDOException $e) { $ok = false; - $_SESSION['bd_error'] = $e->getMessage(); + $dbOptions['bd_error'] = $e->getMessage(); } - - if (!$ok) { - @unlink(join_path(DATA_PATH, 'config.php')); - } - return $ok; } @@ -510,7 +403,7 @@ function printStep0() { // @todo refactor this view with the check_install action function printStep1() { - $res = checkStep1(); + $res = checkRequirements(); ?> @@ -805,7 +698,9 @@ case 3: case 4: break; case 5: - deleteInstall(); + if (deleteInstall()) { + header('Location: index.php'); + } break; } ?> diff --git a/cli/_cli.php b/cli/_cli.php index 66506f07a..7d1a7c6b2 100644 --- a/cli/_cli.php +++ b/cli/_cli.php @@ -38,7 +38,12 @@ function cliInitUser($username) { return $username; } -function done($ok) { +function accessRights() { + echo '• Remember to re-apply the appropriate access rights, such as:' , "\n", + "\t", 'sudo chown -R :www-data . && sudo chmod -R g+r . && sudo chmod -R g+w ./data/', "\n"; +} + +function done($ok = true) { fwrite(STDERR, 'Result: ' . ($ok ? 'success' : 'fail') . "\n"); exit($ok ? 0 : 1); } diff --git a/cli/create-user.php b/cli/create-user.php index 243e65a35..5e93d4605 100755 --- a/cli/create-user.php +++ b/cli/create-user.php @@ -12,8 +12,8 @@ $options = getopt('', array( )); if (empty($options['user'])) { - fail('Usage: ' . basename(__FILE__) . " --user username --password 'password' --api-password 'api_password'" . - " --language en --email user@example.net --token 'longRandomString'"); + fail('Usage: ' . basename(__FILE__) . " --user username ( --password 'password' --api-password 'api_password'" . + " --language en --email user@example.net --token 'longRandomString' )"); } $username = $options['user']; if (!ctype_alnum($username)) { @@ -35,6 +35,12 @@ $ok = FreshRSS_user_Controller::createUser($username, 'token' => empty($options['token']) ? '' : $options['token'], )); +if (!$ok) { + fail('FreshRSS could not create user!'); +} + invalidateHttpCache(FreshRSS_Context::$system_conf->default_user); +accessRights(); + done($ok); diff --git a/cli/do-install.php b/cli/do-install.php new file mode 100644 index 000000000..5eeedc626 --- /dev/null +++ b/cli/do-install.php @@ -0,0 +1,102 @@ +#!/usr/bin/php + $check) { + if ($check !== 'ok' && $requirement !== 'all') { + $message .= '• ' . $requirement . "\n"; + } + } + fail($message); +} + +if (!ctype_alnum($options['default_user'])) { + fail('FreshRSS invalid default username (must be ASCII alphanumeric): ' . $options['default_user']); +} + +if (!in_array($options['auth_type'], array('form', 'http_auth', 'none'))) { + fail('FreshRSS invalid authentication method (auth_type must be one of { form, http_auth, none }: ' . $options['auth_type']); +} + +$config = array( + 'salt' => generateSalt(), + 'db' => FreshRSS_Context::$system_conf->db, + ); + +foreach ($params as $param) { + $param = rtrim($param, ':'); + if (isset($options[$param])) { + $config[$param] = $options[$param] === false ? true : $options[$param]; + } +} + +if ((!empty($config['base_url'])) && server_is_public($config['base_url'])) { + $config['pubsubhubbub_enabled'] = true; +} + +foreach ($dBparams as $dBparam) { + $dBparam = rtrim($dBparam, ':'); + if (!empty($options[$dBparam])) { + $param = substr($dBparam, strlen('db-')); + $config['db'][$param] = $options[$dBparam]; + } +} + +if (file_put_contents(join_path(DATA_PATH, 'config.php'), "= 0; + $minz = file_exists(join_path(LIB_PATH, 'Minz')); + $curl = extension_loaded('curl'); + $pdo_mysql = extension_loaded('pdo_mysql'); + $pdo_sqlite = extension_loaded('pdo_sqlite'); + $pdo_pgsql = extension_loaded('pdo_pgsql'); + $pdo = $pdo_mysql || $pdo_sqlite || $pdo_pgsql; + $pcre = extension_loaded('pcre'); + $ctype = extension_loaded('ctype'); + $dom = class_exists('DOMDocument'); + $xml = function_exists('xml_parser_create'); + $json = function_exists('json_encode'); + $data = DATA_PATH && is_writable(DATA_PATH); + $cache = CACHE_PATH && is_writable(CACHE_PATH); + $users = USERS_PATH && is_writable(USERS_PATH); + $favicons = is_writable(join_path(DATA_PATH, 'favicons')); + $http_referer = is_referer_from_same_domain(); + + return array( + 'php' => $php ? 'ok' : 'ko', + 'minz' => $minz ? 'ok' : 'ko', + 'curl' => $curl ? 'ok' : 'ko', + 'pdo-mysql' => $pdo_mysql ? 'ok' : 'ko', + 'pdo-sqlite' => $pdo_sqlite ? 'ok' : 'ko', + 'pdo-pgsql' => $pdo_pgsql ? 'ok' : 'ko', + 'pdo' => $pdo ? 'ok' : 'ko', + 'pcre' => $pcre ? 'ok' : 'ko', + 'ctype' => $ctype ? 'ok' : 'ko', + 'dom' => $dom ? 'ok' : 'ko', + 'xml' => $xml ? 'ok' : 'ko', + 'json' => $json ? 'ok' : 'ko', + 'data' => $data ? 'ok' : 'ko', + 'cache' => $cache ? 'ok' : 'ko', + 'users' => $users ? 'ok' : 'ko', + 'favicons' => $favicons ? 'ok' : 'ko', + 'http_referer' => $http_referer ? 'ok' : 'ko', + 'all' => $php && $minz && $curl && $pdo && $pcre && $ctype && $dom && $xml && + $data && $cache && $users && $favicons && $http_referer ? + 'ok' : 'ko' + ); +} + +function generateSalt() { + return sha1(uniqid(mt_rand(), true).implode('', stat(__FILE__))); +} + +function checkDb(&$dbOptions) { + $dsn = ''; + try { + $driver_options = null; + switch ($dbOptions['type']) { + case 'mysql': + include_once(APP_PATH . '/SQL/install.sql.mysql.php'); + $driver_options = array( + PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4' + ); + try { // on ouvre une connexion juste pour créer la base si elle n'existe pas + $dsn = 'mysql:host=' . $dbOptions['host'] . ';'; + $c = new PDO($dsn, $dbOptions['user'], $dbOptions['password'], $driver_options); + $sql = sprintf(SQL_CREATE_DB, $dbOptions['base']); + $res = $c->query($sql); + } catch (PDOException $e) { + syslog(LOG_DEBUG, 'FreshRSS MySQL warning: ' . $e->getMessage()); + } + // on écrase la précédente connexion en sélectionnant la nouvelle BDD + $dsn = 'mysql:host=' . $dbOptions['host'] . ';dbname=' . $dbOptions['base']; + break; + case 'sqlite': + include_once(APP_PATH . '/SQL/install.sql.sqlite.php'); + $dsn = 'sqlite:' . join_path(USERS_PATH, $dbOptions['default_user'], 'db.sqlite'); + $driver_options = array( + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ); + break; + case 'pgsql': + include_once(APP_PATH . '/SQL/install.sql.pgsql.php'); + $driver_options = array( + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ); + try { // on ouvre une connexion juste pour créer la base si elle n'existe pas + $dsn = 'pgsql:host=' . $dbOptions['host'] . ';dbname=postgres'; + $c = new PDO($dsn, $dbOptions['user'], $dbOptions['password'], $driver_options); + $sql = sprintf(SQL_CREATE_DB, $dbOptions['base']); + $res = $c->query($sql); + } catch (PDOException $e) { + syslog(LOG_DEBUG, 'FreshRSS PostgreSQL warning: ' . $e->getMessage()); + } + // on écrase la précédente connexion en sélectionnant la nouvelle BDD + $dsn = 'pgsql:host=' . $dbOptions['host'] . ';dbname=' . $dbOptions['base']; + break; + default: + return false; + } + } catch (PDOException $e) { + $dsn = ''; + $dbOptions['error'] = $e->getMessage(); + } + $dbOptions['dsn'] = $dsn; + $dbOptions['options'] = $driver_options; + return $dsn != ''; +} + +function deleteInstall() { + $path = join_path(DATA_PATH, 'do-install.txt'); + @unlink($path); + return !file_exists($path); +} -- cgit v1.2.3 From 1182129ce5f07892afed190ffbb2ea4c7fc28967 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 24 Oct 2016 20:29:08 +0200 Subject: CLI option no-default-feeds https://github.com/FreshRSS/FreshRSS/issues/1095 --- app/Controllers/userController.php | 4 ++-- app/Models/UserDAO.php | 18 +++++++++++++++++- app/SQL/install.sql.mysql.php | 3 +++ app/SQL/install.sql.pgsql.php | 4 ++++ app/SQL/install.sql.sqlite.php | 4 ++++ app/install.php | 15 +++++++++++++++ cli/create-user.php | 7 +++++-- 7 files changed, 50 insertions(+), 5 deletions(-) (limited to 'cli/create-user.php') diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index 9dee16e8c..9d6ae18e6 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -99,7 +99,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { $this->view->size_user = $entryDAO->size(); } - public static function createUser($new_user_name, $passwordPlain, $apiPasswordPlain, $userConfig = array()) { + public static function createUser($new_user_name, $passwordPlain, $apiPasswordPlain, $userConfig = array(), $insertDefaultFeeds = true) { if (!is_array($userConfig)) { $userConfig = array(); } @@ -138,7 +138,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { } if ($ok) { $userDAO = new FreshRSS_UserDAO(); - $ok &= $userDAO->createUser($new_user_name, $userConfig['language']); + $ok &= $userDAO->createUser($new_user_name, $userConfig['language'], $insertDefaultFeeds); } return $ok; } diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php index 597182693..a95ee6bc4 100644 --- a/app/Models/UserDAO.php +++ b/app/Models/UserDAO.php @@ -1,7 +1,7 @@ db; require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); @@ -28,6 +28,22 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { } } } + if ($insertDefaultFeeds) { + if (defined('SQL_INSERT_FEEDS')) { //E.g. MySQL + $sql = sprintf(SQL_INSERT_FEEDS, $bd_prefix_user); + $stm = $userPDO->bd->prepare($sql); + $ok &= $stm && $stm->execute(); + } else { //E.g. SQLite + global $SQL_INSERT_FEEDS; + if (is_array($SQL_INSERT_FEEDS)) { + foreach ($SQL_INSERT_FEEDS as $instruction) { + $sql = sprintf($instruction, $bd_prefix_user); + $stm = $userPDO->bd->prepare($sql); + $ok &= ($stm && $stm->execute()); + } + } + } + } } catch (Exception $e) { Minz_Log::error('Error while creating user: ' . $e->getMessage()); } diff --git a/app/SQL/install.sql.mysql.php b/app/SQL/install.sql.mysql.php index ca181303e..a454829d5 100644 --- a/app/SQL/install.sql.mysql.php +++ b/app/SQL/install.sql.mysql.php @@ -59,6 +59,9 @@ CREATE TABLE IF NOT EXISTS `%1$sentry` ( ENGINE = INNODB; INSERT IGNORE INTO `%1$scategory` (id, name) VALUES(1, "%2$s"); +'); + +define('SQL_INSERT_FEEDS', ' INSERT IGNORE INTO `%1$sfeed` (url, category, name, website, description, ttl) VALUES("http://freshrss.org/feeds/all.atom.xml", 1, "FreshRSS.org", "http://freshrss.org/", "FreshRSS, a free, self-hostable aggregator…", 86400); INSERT IGNORE INTO `%1$sfeed` (url, category, name, website, description, ttl) VALUES("https://github.com/FreshRSS/FreshRSS/releases.atom", 1, "FreshRSS @ GitHub", "https://github.com/FreshRSS/FreshRSS/", "FreshRSS releases @ GitHub", 86400); '); diff --git a/app/SQL/install.sql.pgsql.php b/app/SQL/install.sql.pgsql.php index b343bda86..9f4240b98 100644 --- a/app/SQL/install.sql.pgsql.php +++ b/app/SQL/install.sql.pgsql.php @@ -52,6 +52,10 @@ $SQL_CREATE_TABLES = array( 'CREATE INDEX %1$sentry_lastSeen_index ON "%1$sentry" ("lastSeen");', 'INSERT INTO "%1$scategory" (name) SELECT \'%2$s\' WHERE NOT EXISTS (SELECT id FROM "%1$scategory" WHERE id = 1);', +); + +global $SQL_INSERT_FEEDS; +$SQL_INSERT_FEEDS = array( 'INSERT INTO "%1$sfeed" (url, category, name, website, description, ttl) SELECT \'http://freshrss.org/feeds/all.atom.xml\', 1, \'FreshRSS.org\', \'http://freshrss.org/\', \'FreshRSS, a free, self-hostable aggregator…\', 86400 WHERE NOT EXISTS (SELECT id FROM "%1$sfeed" WHERE url = \'http://freshrss.org/feeds/all.atom.xml\');', 'INSERT INTO "%1$sfeed" (url, category, name, website, description, ttl) SELECT \'https://github.com/FreshRSS/FreshRSS/releases.atom\', 1, \'FreshRSS @ GitHub\', \'https://github.com/FreshRSS/FreshRSS/\', \'FreshRSS releases @ GitHub\', 86400 WHERE NOT EXISTS (SELECT id FROM "%1$sfeed" WHERE url = \'https://github.com/FreshRSS/FreshRSS/releases.atom\');', ); diff --git a/app/SQL/install.sql.sqlite.php b/app/SQL/install.sql.sqlite.php index 1d3a5d92f..68d93ba92 100644 --- a/app/SQL/install.sql.sqlite.php +++ b/app/SQL/install.sql.sqlite.php @@ -55,6 +55,10 @@ $SQL_CREATE_TABLES = array( 'CREATE INDEX IF NOT EXISTS entry_lastSeen_index ON `entry`(`lastSeen`);', //v1.1.1 'INSERT OR IGNORE INTO `category` (id, name) VALUES(1, "%2$s");', +); + +global $SQL_INSERT_FEEDS; +$SQL_INSERT_FEEDS = array( 'INSERT OR IGNORE INTO `feed` (url, category, name, website, description, ttl) VALUES("http://freshrss.org/feeds/all.atom.xml", 1, "FreshRSS.org", "http://freshrss.org/", "FreshRSS, a free, self-hostable aggregator…", 86400);', 'INSERT OR IGNORE INTO `feed` (url, category, name, website, description, ttl) VALUES("https://github.com/FreshRSS/FreshRSS/releases.atom", 1, "FreshRSS releases", "https://github.com/FreshRSS/FreshRSS/", "FreshRSS releases @ GitHub", 86400);', ); diff --git a/app/install.php b/app/install.php index 6956761c7..0daa02b1b 100644 --- a/app/install.php +++ b/app/install.php @@ -357,6 +357,21 @@ function checkDbUser(&$dbOptions) { } } } + + if (defined('SQL_INSERT_FEEDS')) { + $sql = sprintf(SQL_INSERT_FEEDS, $dbOptions['bd_prefix_user']); + $stm = $c->prepare($sql); + $ok &= $stm->execute(); + } else { + global $SQL_INSERT_FEEDS; + if (is_array($SQL_INSERT_FEEDS)) { + foreach ($SQL_INSERT_FEEDS as $instruction) { + $sql = sprintf($instruction, $dbOptions['bd_prefix_user']); + $stm = $c->prepare($sql); + $ok &= $stm->execute(); + } + } + } } catch (PDOException $e) { $ok = false; $dbOptions['bd_error'] = $e->getMessage(); diff --git a/cli/create-user.php b/cli/create-user.php index 5e93d4605..c790acb59 100755 --- a/cli/create-user.php +++ b/cli/create-user.php @@ -9,11 +9,12 @@ $options = getopt('', array( 'language:', 'email:', 'token:', + 'no-default-feeds', )); if (empty($options['user'])) { fail('Usage: ' . basename(__FILE__) . " --user username ( --password 'password' --api-password 'api_password'" . - " --language en --email user@example.net --token 'longRandomString' )"); + " --language en --email user@example.net --token 'longRandomString --no-default-feeds' )"); } $username = $options['user']; if (!ctype_alnum($username)) { @@ -33,7 +34,9 @@ $ok = FreshRSS_user_Controller::createUser($username, array( 'language' => empty($options['language']) ? '' : $options['language'], 'token' => empty($options['token']) ? '' : $options['token'], - )); + ), + !isset($options['no-default-feeds']) + ); if (!$ok) { fail('FreshRSS could not create user!'); -- cgit v1.2.3 From c8e5292ab7a4670ff67e587f18d7973eee2f00e6 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 24 Oct 2016 21:58:22 +0200 Subject: CLI minor formatting https://github.com/FreshRSS/FreshRSS/issues/1095 --- cli/create-user.php | 3 +-- cli/export-zip-for-user.php | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'cli/create-user.php') diff --git a/cli/create-user.php b/cli/create-user.php index c790acb59..008b82ce3 100755 --- a/cli/create-user.php +++ b/cli/create-user.php @@ -35,8 +35,7 @@ $ok = FreshRSS_user_Controller::createUser($username, 'language' => empty($options['language']) ? '' : $options['language'], 'token' => empty($options['token']) ? '' : $options['token'], ), - !isset($options['no-default-feeds']) - ); + !isset($options['no-default-feeds'])); if (!$ok) { fail('FreshRSS could not create user!'); diff --git a/cli/export-zip-for-user.php b/cli/export-zip-for-user.php index 9183a1c14..92fe9bf9a 100755 --- a/cli/export-zip-for-user.php +++ b/cli/export-zip-for-user.php @@ -20,8 +20,8 @@ $importController = new FreshRSS_importExport_Controller(); $ok = false; try { $ok = $importController->exportFile(true, true, true, - empty($options['max-feed-entries']) ? 100 : intval($options['max-feed-entries']), - $username); + empty($options['max-feed-entries']) ? 100 : intval($options['max-feed-entries']), + $username); } catch (FreshRSS_ZipMissing_Exception $zme) { fail('FreshRSS error: Lacking php-zip extension!'); } -- cgit v1.2.3