From 002d42cd87e13001c3a8c37110f729c01cf99284 Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Tue, 6 Mar 2018 07:03:49 +0100 Subject: Fix faulty id --- app/views/configure/queries.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/configure/queries.phtml b/app/views/configure/queries.phtml index 0dffa268d..c9bc966ef 100644 --- a/app/views/configure/queries.phtml +++ b/app/views/configure/queries.phtml @@ -14,7 +14,7 @@
- + -- cgit v1.2.3 From ab9a57cb49dd2ea1e0563214ec3aba77eb0e80bd Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Tue, 6 Mar 2018 07:28:36 +0100 Subject: Add tooltips on user queries buttons --- app/i18n/cz/conf.php | 2 ++ app/i18n/de/conf.php | 2 ++ app/i18n/en/conf.php | 2 ++ app/i18n/es/conf.php | 2 ++ app/i18n/fr/conf.php | 2 ++ app/i18n/he/conf.php | 2 ++ app/i18n/it/conf.php | 2 ++ app/i18n/kr/conf.php | 2 ++ app/i18n/nl/conf.php | 2 ++ app/i18n/pt-br/conf.php | 2 ++ app/i18n/ru/conf.php | 2 ++ app/i18n/tr/conf.php | 2 ++ app/i18n/zh-cn/conf.php | 2 ++ app/views/configure/queries.phtml | 4 ++-- 14 files changed, 28 insertions(+), 2 deletions(-) diff --git a/app/i18n/cz/conf.php b/app/i18n/cz/conf.php index a7bcf6c08..e73ab168f 100644 --- a/app/i18n/cz/conf.php +++ b/app/i18n/cz/conf.php @@ -42,6 +42,7 @@ return array( 'query' => array( '_' => 'Uživatelské dotazy', 'deprecated' => 'Tento dotaz již není platný. Odkazovaná kategorie nebo kanál byly smazány.', + 'display' => 'Display user query results', // TODO 'filter' => 'Filtr aplikován:', 'get_all' => 'Zobrazit všechny články', 'get_category' => 'Zobrazit "%s" kategorii', @@ -52,6 +53,7 @@ return array( 'number' => 'Dotaz n°%d', 'order_asc' => 'Zobrazit nejdříve nejstarší články', 'order_desc' => 'Zobrazit nejdříve nejnovější články', + 'remove' => 'Remove user query', // TODO 'search' => 'Hledat "%s"', 'state_0' => 'Zobrazit všechny články', 'state_1' => 'Zobrazit přečtené články', diff --git a/app/i18n/de/conf.php b/app/i18n/de/conf.php index 6b0d7c7f9..93b2323a7 100644 --- a/app/i18n/de/conf.php +++ b/app/i18n/de/conf.php @@ -42,6 +42,7 @@ return array( 'query' => array( '_' => 'Benutzerabfragen', 'deprecated' => 'Diese Abfrage ist nicht länger gültig. Die referenzierte Kategorie oder der Feed ist gelöscht worden.', + 'display' => 'Display user query results', // TODO 'filter' => 'Angewendeter Filter:', 'get_all' => 'Alle Artikel anzeigen', 'get_category' => 'Kategorie "%s" anzeigen', @@ -52,6 +53,7 @@ return array( 'number' => 'Abfrage Nr. %d', 'order_asc' => 'Älteste Artikel zuerst anzeigen', 'order_desc' => 'Neueste Artikel zuerst anzeigen', + 'remove' => 'Remove user query', // TODO 'search' => 'Suche nach "%s"', 'state_0' => 'Alle Artikel anzeigen', 'state_1' => 'Gelesene Artikel anzeigen', diff --git a/app/i18n/en/conf.php b/app/i18n/en/conf.php index a1c3fc949..ff954c68f 100644 --- a/app/i18n/en/conf.php +++ b/app/i18n/en/conf.php @@ -42,6 +42,7 @@ return array( 'query' => array( '_' => 'User queries', 'deprecated' => 'This query is no longer valid. The referenced category or feed has been deleted.', + 'display' => 'Display user query results', 'filter' => 'Filter applied:', 'get_all' => 'Display all articles', 'get_category' => 'Display "%s" category', @@ -52,6 +53,7 @@ return array( 'number' => 'Query n°%d', 'order_asc' => 'Display oldest articles first', 'order_desc' => 'Display newest articles first', + 'remove' => 'Remove user query', 'search' => 'Search for "%s"', 'state_0' => 'Display all articles', 'state_1' => 'Display read articles', diff --git a/app/i18n/es/conf.php b/app/i18n/es/conf.php index 464bebc4f..0e198caf8 100755 --- a/app/i18n/es/conf.php +++ b/app/i18n/es/conf.php @@ -42,6 +42,7 @@ return array( 'query' => array( '_' => 'Consultas de usuario', 'deprecated' => 'Esta consulta ya no es válida. La categoría referenciada o fuente ha sido eliminada.', + 'display' => 'Display user query results', // TODO 'filter' => 'Filtro aplicado:', 'get_all' => 'Mostrar todos los artículos', 'get_category' => 'Mostrar la categoría "%s"', @@ -52,6 +53,7 @@ return array( 'number' => 'Consulta n° %d', 'order_asc' => 'Mostrar primero los artículos más antiguos', 'order_desc' => 'Mostrar primero los artículos más recientes', + 'remove' => 'Remove user query', // TODO 'search' => 'Buscar "%s"', 'state_0' => 'Mostrar todos los artículos', 'state_1' => 'Mostrar artículos leídos', diff --git a/app/i18n/fr/conf.php b/app/i18n/fr/conf.php index 402a97b1c..52b2f7e0d 100644 --- a/app/i18n/fr/conf.php +++ b/app/i18n/fr/conf.php @@ -42,6 +42,7 @@ return array( 'query' => array( '_' => 'Filtres utilisateurs', 'deprecated' => 'Ce filtre n’est plus valide. La catégorie ou le flux concerné a été supprimé.', + 'display' => 'Afficher les résultats du filtre', 'filter' => 'Filtres appliqués :', 'get_all' => 'Afficher tous les articles', 'get_category' => 'Afficher la catégorie "%s"', @@ -52,6 +53,7 @@ return array( 'number' => 'Filtre n°%d', 'order_asc' => 'Afficher les articles les plus anciens en premier', 'order_desc' => 'Afficher les articles les plus récents en premier', + 'remove' => 'Supprimer le filtre', 'search' => 'Recherche de "%s"', 'state_0' => 'Afficher tous les articles', 'state_1' => 'Afficher les articles lus', diff --git a/app/i18n/he/conf.php b/app/i18n/he/conf.php index 3d0534fdc..a682461a6 100644 --- a/app/i18n/he/conf.php +++ b/app/i18n/he/conf.php @@ -42,6 +42,7 @@ return array( 'query' => array( '_' => 'שאילתות', 'deprecated' => 'שאילתה זו אינה בתוקף יותר, הפיד או הקטגוריה לייחוס נמחקו.', + 'display' => 'Display user query results', // TODO 'filter' => 'מסננים בשימוש:', 'get_all' => 'הצגת כל המאמרים', 'get_category' => 'הצגת קטגוריה "%s"', @@ -52,6 +53,7 @@ return array( 'number' => 'שאילתה מספר °%d', 'order_asc' => 'הצגת מאמרים ישנים בראש', 'order_desc' => 'הצגת מאמרים חדשים בראש', + 'remove' => 'Remove user query', // TODO 'search' => 'חיפוש "%s"', 'state_0' => 'הצגת כל המאמרים', 'state_1' => 'הצגת מאמרים שנקראו', diff --git a/app/i18n/it/conf.php b/app/i18n/it/conf.php index 5ab343c4d..65b979c51 100644 --- a/app/i18n/it/conf.php +++ b/app/i18n/it/conf.php @@ -42,6 +42,7 @@ return array( 'query' => array( '_' => 'Ricerche personali', 'deprecated' => 'Questa query non è più valida. La categoria o il feed di riferimento non stati cancellati.', + 'display' => 'Display user query results', // TODO 'filter' => 'Filtro applicato:', 'get_all' => 'Mostra tutti gli articoli', 'get_category' => 'Mostra la categoria "%s" ', @@ -52,6 +53,7 @@ return array( 'number' => 'Ricerca n°%d', 'order_asc' => 'Mostra prima gli articoli più vecchi', 'order_desc' => 'Mostra prima gli articoli più nuovi', + 'remove' => 'Remove user query', // TODO 'search' => 'Cerca per "%s"', 'state_0' => 'Mostra tutti gli articoli', 'state_1' => 'Mostra gli articoli letti', diff --git a/app/i18n/kr/conf.php b/app/i18n/kr/conf.php index c9e91a804..558505973 100644 --- a/app/i18n/kr/conf.php +++ b/app/i18n/kr/conf.php @@ -42,6 +42,7 @@ return array( 'query' => array( '_' => '사용자 쿼리', 'deprecated' => '이 쿼리는 더 이상 유효하지 않습니다. 해당하는 카테고리나 피드가 삭제되었습니다.', + 'display' => 'Display user query results', // TODO 'filter' => '적용된 필터:', 'get_all' => '모든 글 표시', 'get_category' => '"%s" 카테고리 표시', @@ -52,6 +53,7 @@ return array( 'number' => '쿼리 #%d', 'order_asc' => '오래된 글 먼저 표시', 'order_desc' => '최근 글 먼저 표시', + 'remove' => 'Remove user query', // TODO 'search' => '"%s"의 검색 결과', 'state_0' => '모든 글 표시', 'state_1' => '읽은 글 표시', diff --git a/app/i18n/nl/conf.php b/app/i18n/nl/conf.php index 847c735d1..6d739a0b7 100644 --- a/app/i18n/nl/conf.php +++ b/app/i18n/nl/conf.php @@ -42,6 +42,7 @@ return array( 'query' => array( '_' => 'Gebruikers queries (informatie aanvragen)', 'deprecated' => 'Deze query (informatie aanvraag) is niet langer geldig. De bedoelde categorie of feed is al verwijderd.', + 'display' => 'Display user query results', // TODO 'filter' => 'Filter toegepast:', 'get_all' => 'Toon alle artikelen', 'get_category' => 'Toon "%s" categorie', @@ -52,6 +53,7 @@ return array( 'number' => 'Query n°%d', 'order_asc' => 'Toon oudste artikelen eerst', 'order_desc' => 'Toon nieuwste artikelen eerst', + 'remove' => 'Remove user query', // TODO 'search' => 'Zoek naar "%s"', 'state_0' => 'Toon alle artikelen', 'state_1' => 'Toon gelezen artikelen', diff --git a/app/i18n/pt-br/conf.php b/app/i18n/pt-br/conf.php index 864c80e61..61a12160c 100644 --- a/app/i18n/pt-br/conf.php +++ b/app/i18n/pt-br/conf.php @@ -42,6 +42,7 @@ return array( 'query' => array( '_' => 'Queries do usuário', 'deprecated' => 'Esta não é mais válida. A categoria ou feed relacionado foi deletado.', + 'display' => 'Display user query results', // TODO 'filter' => 'Filtro aplicado:', 'get_all' => 'Mostrar todos os artigos', 'get_category' => 'Visualizar "%s" categoria', @@ -52,6 +53,7 @@ return array( 'number' => 'Query n°%d', 'order_asc' => 'Exibir artigos mais antigos primeiro', 'order_desc' => 'Exibir artigos mais novos primeiro', + 'remove' => 'Remove user query', // TODO 'search' => 'Busca por "%s"', 'state_0' => 'Exibir todos os artigos', 'state_1' => 'Exibir artigos lidos', diff --git a/app/i18n/ru/conf.php b/app/i18n/ru/conf.php index 1b5cd8085..90a1a6797 100644 --- a/app/i18n/ru/conf.php +++ b/app/i18n/ru/conf.php @@ -42,6 +42,7 @@ return array( 'query' => array( '_' => 'User queries', 'deprecated' => 'This query is no longer valid. The referenced category or feed has been deleted.', + 'display' => 'Display user query results', // TODO 'filter' => 'Filter applied:', 'get_all' => 'Display all articles', 'get_category' => 'Display "%s" category', @@ -52,6 +53,7 @@ return array( 'number' => 'Query n°%d', 'order_asc' => 'Display oldest articles first', 'order_desc' => 'Display newest articles first', + 'remove' => 'Remove user query', // TODO 'search' => 'Search for "%s"', 'state_0' => 'Display all articles', 'state_1' => 'Display read articles', diff --git a/app/i18n/tr/conf.php b/app/i18n/tr/conf.php index 0671da79e..cae1e4cac 100644 --- a/app/i18n/tr/conf.php +++ b/app/i18n/tr/conf.php @@ -42,6 +42,7 @@ return array( 'query' => array( '_' => 'Kullanıcı sorguları', 'deprecated' => 'Bu sorgu artık geçerli değil. İlgili akış veya kategori silinmiş.', + 'display' => 'Display user query results', // TODO 'filter' => 'Filtre uygulandı:', 'get_all' => 'Tüm makaleleri göster', 'get_category' => '"%s" kategorisini göster', @@ -52,6 +53,7 @@ return array( 'number' => 'Sorgu n°%d', 'order_asc' => 'Önce eski makaleleri göster', 'order_desc' => 'Önce yeni makaleleri göster', + 'remove' => 'Remove user query', // TODO 'search' => '"%s" için arama', 'state_0' => 'Tüm makaleleri göster', 'state_1' => 'Okunmuş makaleleri göster', diff --git a/app/i18n/zh-cn/conf.php b/app/i18n/zh-cn/conf.php index c57738c5b..00bea4d79 100644 --- a/app/i18n/zh-cn/conf.php +++ b/app/i18n/zh-cn/conf.php @@ -42,6 +42,7 @@ return array( 'query' => array( '_' => '自定义查询', 'deprecated' => '此查询不再有效。相关的分类或 RSS 源已被删除。', + 'display' => 'Display user query results', // TODO 'filter' => '生效的过滤器:', 'get_all' => '显示所有文章', 'get_category' => '显示分类 "%s"', @@ -52,6 +53,7 @@ return array( 'number' => '查询 n°%d', 'order_asc' => '由旧到新显示文章', 'order_desc' => '由新到旧显示文章', + 'remove' => 'Remove user query', // TODO 'search' => '搜索 "%s"', 'state_0' => '显示所有文章', 'state_1' => '显示已读文章', diff --git a/app/views/configure/queries.phtml b/app/views/configure/queries.phtml index c9bc966ef..baaf74954 100644 --- a/app/views/configure/queries.phtml +++ b/app/views/configure/queries.phtml @@ -29,11 +29,11 @@ data-leave-validation="getName(); ?>" /> - + - +
-- cgit v1.2.3 From 0f43440d25b0b5f5a282ccd54e54c6e99df336d0 Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Tue, 6 Mar 2018 07:40:02 +0100 Subject: Change English translation to make it consistent --- app/i18n/en/conf.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/i18n/en/conf.php b/app/i18n/en/conf.php index ff954c68f..fd91ed8f6 100644 --- a/app/i18n/en/conf.php +++ b/app/i18n/en/conf.php @@ -171,8 +171,8 @@ return array( 'see_on_website' => 'See on original website', 'shift_for_all_read' => '+ shift to mark all articles as read', 'title' => 'Shortcuts', - 'user_filter' => 'Access user filters', - 'user_filter_help' => 'If there is only one user filter, it is used. Otherwise, filters are accessible by their number.', + 'user_filter' => 'Access user queries', + 'user_filter_help' => 'If there is only one user query, it is used. Otherwise, queries are accessible by their number.', 'views' => 'Views', ), 'user' => array( -- cgit v1.2.3 From be47672e009080782e69a8210ae176d06f5f8143 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 9 Mar 2018 11:10:09 +0100 Subject: Start FreshRSS 1.10.3-dev --- CHANGELOG.md | 4 ++++ constants.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 501bff9c4..3918b3e36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # FreshRSS changelog +## 2018-0X-XX FreshRSS 1.10.3-dev + + + ## 2018-03-09 FreshRSS 1.10.2 (Docker only) * Bug fixing diff --git a/constants.php b/constants.php index 188560eb5..af676247c 100644 --- a/constants.php +++ b/constants.php @@ -2,7 +2,7 @@ //NB: Do not edit; use ./constants.local.php instead. // -define('FRESHRSS_VERSION', '1.10.2'); +define('FRESHRSS_VERSION', '1.10.3-dev'); define('FRESHRSS_WEBSITE', 'https://freshrss.org'); define('FRESHRSS_WIKI', 'https://freshrss.github.io/FreshRSS/'); -- cgit v1.2.3 From 948b44ee1e90773ba11d00cb2d348224ee11bc97 Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Sat, 10 Mar 2018 16:23:00 +0100 Subject: Change code to be more robust The code doesn't rely on positionnal arguments anymore. It uses options. I've added some check to validate that the action performed are configured properly. --- cli/manipulate.translation.php | 102 ++++++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 42 deletions(-) diff --git a/cli/manipulate.translation.php b/cli/manipulate.translation.php index 0e06993ef..19b229878 100644 --- a/cli/manipulate.translation.php +++ b/cli/manipulate.translation.php @@ -1,13 +1,13 @@ load(); -switch ($argv[1]) { - case 'add_language' : - $i18nData->addLanguage($argv[2]); - break; - case 'add_key' : - if (3 === $argc) { - help(); +switch ($options['a']) { + case 'add' : + if (array_key_exists('k', $options) && array_key_exists('v', $options) && array_key_exists('l', $options)) { + $i18nData->addValue($options['k'], $options['v'], $options['l']); + } elseif (array_key_exists('k', $options) && array_key_exists('v', $options)) { + $i18nData->addKey($options['k'], $options['v']); + } elseif (array_key_exists('l', $options)) { + $i18nData->addLanguage($options['l']); + } else { + error('You need to specify a valid set of options.'); } - $i18nData->addKey($argv[2], $argv[3]); break; - case 'add_value': - if (4 === $argc) { - help(); + case 'delete' : + if (array_key_exists('k', $options)) { + $i18nData->removeKey($options['k']); + } else { + error('You need to specify the key to delete.'); } - $i18nData->addValue($argv[2], $argv[3], $argv[4]); - break; - case 'duplicate_key' : - $i18nData->duplicateKey($argv[2]); break; - case 'delete_key' : - $i18nData->removeKey($argv[2]); + case 'duplicate' : + if (array_key_exists('k', $options)) { + $i18nData->duplicateKey($options['k']); + } else { + error('You need to specify the key to duplicate'); + } break; case 'format' : $i18nFile->dump($i18nData); @@ -48,47 +52,61 @@ if ($i18nData->hasChanged()) { $i18nFile->dump($i18nData); } +/** + * Output error message. + */ +function error($message) { + $error = << Date: Sat, 10 Mar 2018 16:32:35 +0100 Subject: Improve error catching --- cli/i18n/I18nData.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cli/i18n/I18nData.php b/cli/i18n/I18nData.php index b8f958288..77e0e2993 100644 --- a/cli/i18n/I18nData.php +++ b/cli/i18n/I18nData.php @@ -49,7 +49,8 @@ class I18nData { * @throws Exception */ public function addKey($key, $value) { - if (array_key_exists($key, $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)])) { + if (array_key_exists($this->getFilenamePrefix($key), $this->data[static::REFERENCE_LANGUAGE]) && + array_key_exists($key, $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)])) { throw new Exception('The selected key already exist.'); } $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)][$key] = $value; @@ -67,7 +68,8 @@ class I18nData { if (!in_array($language, $this->getAvailableLanguages())) { throw new Exception('The selected language does not exist.'); } - if (!array_key_exists($key, $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)])) { + if (!array_key_exists($this->getFilenamePrefix($key), $this->data[static::REFERENCE_LANGUAGE]) || + !array_key_exists($key, $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)])) { throw new Exception('The selected key does not exist for the selected language.'); } $this->data[$language][$this->getFilenamePrefix($key)][$key] = $value; @@ -80,7 +82,8 @@ class I18nData { * @throws Exception */ public function duplicateKey($key) { - if (!array_key_exists($key, $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)])) { + if (!array_key_exists($this->getFilenamePrefix($key), $this->data[static::REFERENCE_LANGUAGE]) || + !array_key_exists($key, $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)])) { throw new Exception('The selected key does not exist.'); } $value = $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)][$key]; @@ -102,7 +105,8 @@ class I18nData { * @throws Exception */ public function removeKey($key) { - if (!array_key_exists($key, $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)])) { + if (!array_key_exists($this->getFilenamePrefix($key), $this->data[static::REFERENCE_LANGUAGE]) || + !array_key_exists($key, $this->data[static::REFERENCE_LANGUAGE][$this->getFilenamePrefix($key)])) { throw new Exception('The selected key does not exist.'); } foreach ($this->getAvailableLanguages() as $language) { -- cgit v1.2.3 From f4da01294023146cd1f4bc13f1ba5f77702bb41b Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Sun, 11 Mar 2018 10:53:32 +0100 Subject: Add option to ignore keys in cli tool --- cli/i18n/I18nData.php | 24 ++++++++++++++++ cli/i18n/I18nFile.php | 4 ++- cli/i18n/I18nFileInterface.php | 10 +++++++ cli/i18n/I18nIgnoreFile.php | 64 ++++++++++++++++++++++++++++++++++++++++++ cli/manipulate.translation.php | 26 +++++++++++++---- 5 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 cli/i18n/I18nFileInterface.php create mode 100644 cli/i18n/I18nIgnoreFile.php diff --git a/cli/i18n/I18nData.php b/cli/i18n/I18nData.php index 77e0e2993..2178d330d 100644 --- a/cli/i18n/I18nData.php +++ b/cli/i18n/I18nData.php @@ -116,6 +116,30 @@ class I18nData { } } + /** + * WARNING! This is valid only for ignore files. It's not the best way to + * handle that but as it's meant to be used only for the cli tool, there + * is no point of spending time on making it better than that. + * + * Ignore a key from a language, or reverse it. + * + * @param string $key + * @param string $language + * @param boolean $reverse + */ + public function ignore($key, $language, $reverse = false) { + $index = array_search($key, $this->data[$language]); + + if ($index && $reverse) { + unset($this->data[$language][$index]); + return; + } + if ($index && !$reverse) { + return; + } + $this->data[$language][] = $key; + } + /** * Check if the data has changed * diff --git a/cli/i18n/I18nFile.php b/cli/i18n/I18nFile.php index a07efdf88..bdcf3c079 100644 --- a/cli/i18n/I18nFile.php +++ b/cli/i18n/I18nFile.php @@ -1,8 +1,9 @@ i18nPath); foreach ($dirs as $dir) { if ($dir->isDot()) { diff --git a/cli/i18n/I18nFileInterface.php b/cli/i18n/I18nFileInterface.php new file mode 100644 index 000000000..c5aaf9fcd --- /dev/null +++ b/cli/i18n/I18nFileInterface.php @@ -0,0 +1,10 @@ +i18nPath = __DIR__ . '/ignore'; + } + + public function dump(I18nData $i18n) { + foreach ($i18n->getData() as $language => $content) { + $filename = $this->i18nPath . DIRECTORY_SEPARATOR . $language . '.php'; + file_put_contents($filename, $this->format($content)); + } + } + + public function load() { + $i18n = array(); + $files = new DirectoryIterator($this->i18nPath); + foreach ($files as $file) { + if (!$file->isFile()) { + continue; + } + $i18n[$file->getBasename('.php')] = (include $file->getPathname()); + } + + return new I18nData($i18n); + } + + /** + * Format an array of translation + * + * It takes an array of translation and format it to be dumped in a + * translation file. The array is first converted to a string then some + * formatting regexes are applied to match the original content. + * + * @param array $translation + * @return string + */ + private function format($translation) { + $translation = var_export(($translation), true); + $patterns = array( + '/array \(/', + '/=>\s*array/', + '/ {2}/', + '/\d+ => /', + ); + $replacements = array( + 'array(', + '=> array', + "\t", // Double quoting is mandatory to have a tab instead of the \t string + '', + ); + $translation = preg_replace($patterns, $replacements, $translation); + + // Double quoting is mandatory to have new lines instead of \n strings + return sprintf("load(); switch ($options['a']) { @@ -44,6 +48,13 @@ switch ($options['a']) { case 'format' : $i18nFile->dump($i18nData); break; + case 'ignore' : + if (array_key_exists('l', $options) && array_key_exists('k', $options)) { + $i18nData->ignore($options['k'], $options['l'], array_key_exists('r', $options)); + } else { + error('You need to specify a valid set of options.'); + } + break; default : help(); } @@ -80,7 +91,7 @@ DESCRIPTION -a=ACTION select the action to perform. Available actions are add, delete, - duplicate, and format. This option is mandatory. + duplicate, format, and ignore. This option is mandatory. -k=KEY select the key to work on. -v=VAL select the value to set. -l=LANG select the language to work on. @@ -105,6 +116,11 @@ Exemple 5: duplicate a key. It duplicates the key from the referential in every Exemple 6: format i18n files. php %1\$s -a format +Exemple 7: ignore a key. It adds the key in the ignore file to mark it as translated. + php %1\$s -a ignore -k my_key -l my_lang + +Exemple 8: revert ignore a key. It removes the key from the ignore file. + php %1\$s -a ignore -r -k my_key -l my_lang\n\n HELP; $file = str_replace(__DIR__ . '/', '', __FILE__); echo sprintf($help, $file); -- cgit v1.2.3 From eea69848661c6385d6c29c84c98a2315072c5e8e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 11 Mar 2018 11:53:28 +0100 Subject: SQLite more error logs https://github.com/FreshRSS/FreshRSS/issues/1816 And reformat SQL, which required vertical scrolling for me :-) --- app/Models/EntryDAOSQLite.php | 70 +++++++++++-------------------------------- app/Models/FeedDAO.php | 2 +- 2 files changed, 18 insertions(+), 54 deletions(-) diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index 0f57dc1ba..90aafb200 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -7,7 +7,6 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { } protected function autoUpdateDb($errorInfo) { - Minz_Log::error('FreshRSS_EntryDAO::autoUpdateDb error: ' . print_r($errorInfo, true)); if ($tableInfo = $this->bd->query("SELECT sql FROM sqlite_master where name='entrytmp'")) { $showCreate = $tableInfo->fetchColumn(); if (stripos($showCreate, 'entrytmp') === false) { @@ -27,63 +26,28 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { public function commitNewEntries() { $sql = ' - CREATE TEMP TABLE `tmp` AS - SELECT - id, - guid, - title, - author, - content, - link, - date, - `lastSeen`, - hash, is_read, - is_favorite, - id_feed, - tags - FROM `' . $this->prefix . 'entrytmp` - ORDER BY date; - INSERT OR IGNORE INTO `' . $this->prefix . 'entry` - ( - id, - guid, - title, - author, - content, - link, - date, - `lastSeen`, - hash, - is_read, - is_favorite, - id_feed, - tags - ) - SELECT rowid + (SELECT MAX(id) - COUNT(*) FROM `tmp`) AS - id, - guid, - title, - author, - content, - link, - date, - `lastSeen`, - hash, - is_read, - is_favorite, - id_feed, - tags - FROM `tmp` - ORDER BY date; - DELETE FROM `' . $this->prefix . 'entrytmp` - WHERE id <= (SELECT MAX(id) - FROM `tmp`); - DROP TABLE `tmp`;'; +DROP TABLE IF EXISTS `tmp`; +CREATE TEMP TABLE `tmp` AS + SELECT id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags + FROM `' . $this->prefix . 'entrytmp` + ORDER BY date; +INSERT OR IGNORE INTO `' . $this->prefix . 'entry` + (id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) + SELECT rowid + (SELECT MAX(id) - COUNT(*) FROM `tmp`) AS id, + guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags + FROM `tmp` + ORDER BY date; +DELETE FROM `' . $this->prefix . 'entrytmp` WHERE id <= (SELECT MAX(id) FROM `tmp`); +DROP TABLE IF EXISTS `tmp`; +'; $hadTransaction = $this->bd->inTransaction(); if (!$hadTransaction) { $this->bd->beginTransaction(); } $result = $this->bd->exec($sql) !== false; + if (!$result) { + Minz_Log::error('SQL error commitNewEntries: ' . json_encode($this->bd->errorInfo())); + } if (!$hadTransaction) { $this->bd->commit(); } diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 0c25ab0ba..5c6e613d3 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -105,7 +105,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::error('SQL error updateFeed: ' . $info[2]); + Minz_Log::error('SQL error updateFeed: ' . $info[2] . ' for feed ' . $id); return false; } } -- cgit v1.2.3 From deb9cf1695351d84798879c9a0e5250b24668b6a Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 11 Mar 2018 12:29:15 +0100 Subject: Changelog 1816 1823 1830 https://github.com/FreshRSS/FreshRSS/issues/1816 https://github.com/FreshRSS/FreshRSS/pull/1823 https://github.com/FreshRSS/FreshRSS/pull/1830 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3918b3e36..6638c8e8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 2018-0X-XX FreshRSS 1.10.3-dev +* UI + * Add tooltips on user queries [#1823](https://github.com/FreshRSS/FreshRSS/pull/1823) +* Misc. + * Add error log information when SQLite has not enough temp space [#1816](https://github.com/FreshRSS/FreshRSS/issues/1816) ## 2018-03-09 FreshRSS 1.10.2 (Docker only) -- cgit v1.2.3 From ebfb170b140bd3ce9c25bd1206fe58ae40aa1587 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 12 Mar 2018 21:24:24 +0100 Subject: Minor docs --- cli/README.md | 1 + config.default.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/README.md b/cli/README.md index d531b8c3d..0d1c0a7d4 100644 --- a/cli/README.md +++ b/cli/README.md @@ -38,6 +38,7 @@ cd /usr/share/FreshRSS ./cli/do-install.php --default_user admin ( --auth_type form --environment production --base_url https://rss.example.net/ --language en --title FreshRSS --allow_anonymous --api_enabled --db-type mysql --db-host localhost:3306 --db-user freshrss --db-password dbPassword123 --db-base freshrss --db-prefix freshrss ) # --auth_type can be: 'form' (default), 'http_auth' (using the Web server access control), 'none' (dangerous) # --db-type can be: 'sqlite' (default), 'mysql' (MySQL or MariaDB), 'pgsql' (PostgreSQL) +# --base_url should be a public (routable) URL if possible, and is used for push (PubSubHubbub), for some API functions (e.g. favicons), and external URLs in FreshRSS. # --environment can be: 'production' (default), 'development' (for additional log messages) # --language can be: 'en' (default), 'fr', or one of the [supported languages](../app/i18n/) # --db-prefix is an optional prefix in front of the names of the tables. We suggest using 'freshrss_' diff --git a/config.default.php b/config.default.php index 4e4c97e67..e6297a160 100644 --- a/config.default.php +++ b/config.default.php @@ -1,7 +1,7 @@ Date: Mon, 12 Mar 2018 21:29:55 +0100 Subject: SVG version of FreshRSS logo + text In two versions, one using a font, another one an SVG path. Tested in Web browsers (Firefox, Chrome, Edge, IE) + Inkscape --- docs/img/FreshRSS-logo-font.svg | 16 ++++++++++++++++ docs/img/FreshRSS-logo.svg | 25 +++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 docs/img/FreshRSS-logo-font.svg create mode 100644 docs/img/FreshRSS-logo.svg diff --git a/docs/img/FreshRSS-logo-font.svg b/docs/img/FreshRSS-logo-font.svg new file mode 100644 index 000000000..134adfdfc --- /dev/null +++ b/docs/img/FreshRSS-logo-font.svg @@ -0,0 +1,16 @@ + + Logo FreshRSS + + + + + + + + + + FreshRSS + + diff --git a/docs/img/FreshRSS-logo.svg b/docs/img/FreshRSS-logo.svg new file mode 100644 index 000000000..4eb268c8a --- /dev/null +++ b/docs/img/FreshRSS-logo.svg @@ -0,0 +1,25 @@ + + Logo FreshRSS + + + + + + + + + + + FreshRSS + + + + + + + + + + + -- cgit v1.2.3 From 51a08cfb1a967c229b7654ef92a73b798727aa8b Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Wed, 28 Feb 2018 08:08:09 +0100 Subject: Add configuration documentation Writting documentation is really long. Writting good documentation is really hard. I hope the changes I've made are worth it. --- docs/en/img/users/configuration.article.icons.png | Bin 0 -> 11154 bytes .../img/users/configuration.navigation.button.png | Bin 0 -> 491 bytes docs/en/img/users/configuration.sharing.png | Bin 0 -> 15297 bytes docs/en/users/05_Configuration.md | 89 +++++++++++++++++++-- 4 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 docs/en/img/users/configuration.article.icons.png create mode 100644 docs/en/img/users/configuration.navigation.button.png create mode 100644 docs/en/img/users/configuration.sharing.png diff --git a/docs/en/img/users/configuration.article.icons.png b/docs/en/img/users/configuration.article.icons.png new file mode 100644 index 000000000..d1dd768ad Binary files /dev/null and b/docs/en/img/users/configuration.article.icons.png differ diff --git a/docs/en/img/users/configuration.navigation.button.png b/docs/en/img/users/configuration.navigation.button.png new file mode 100644 index 000000000..045956472 Binary files /dev/null and b/docs/en/img/users/configuration.navigation.button.png differ diff --git a/docs/en/img/users/configuration.sharing.png b/docs/en/img/users/configuration.sharing.png new file mode 100644 index 000000000..a3cfd0570 Binary files /dev/null and b/docs/en/img/users/configuration.sharing.png differ diff --git a/docs/en/users/05_Configuration.md b/docs/en/users/05_Configuration.md index d0951e905..154cd98b5 100644 --- a/docs/en/users/05_Configuration.md +++ b/docs/en/users/05_Configuration.md @@ -3,9 +3,29 @@ ## Language -At the moment, FreshRSS is available in French and English. After you confirm your choice, the whole interface will be displayed in the chosen language. - -There are parts of FreshRSS that are not translated and are not intended to be translated. For now, the logs visible in the application as well as the one generated by the script of automatic update are part of it. +At the moment, FreshRSS is available in 13 languages. After you confirm your choice, the interface will be displayed in the chosen language. +Depending on the chosen language, there might be parts of the interface that are still not translated. If you're willing to help translating +the missing bits or add a new language, please check how you can [contribute to the project](../contributing.md#contribute-to-internationalization-i18n). + +There are parts of FreshRSS that are not translated and are not intended to be translated. For now, the logs visible in the application as well as the one generated by automatic update scripts are part of it. + +Not all languages are equals regarding completion: + +| Language | Completion | +|----------|-----------:| +| cz | 87.4% | +| de | 88.1% | +| en | 100% | +| es | 88.7% | +| fr | 99.3% | +| he | 69.6% | +| it | 86.4% | +| kr | 96.3% | +| nl | 95.4% | +| pt-br | 87.4% | +| ru | 36.4% | +| tr | 88.1% | +| zh-cn | 99.0% | ## Theme @@ -33,7 +53,16 @@ There are some who prefer short lines of text while others prefer to maximize th ## Article icons -**TODO** +It worth noting that this section only has effects in normal view. + +![Article icons configuration](../img/users/configuration.article.icons.png) + +Each article is rendered with a header (top line) and a footer (bottom line). +In that section, you can choose what will be displayed in those. + +If you disable every item in the top line, you'll still be able to see it since +there is the feed name and the article title. But if you do the same thing for +the bottom line, it will be empty. ## HTML5 notification timout @@ -41,6 +70,14 @@ After the automatic updates of the feeds, FreshRSS uses the HTML5 notification A The duration of this notification can be set. By default, the value is 0. +## Show the navigation button + +By default, FreshRSS displays buttons to ease the article navigation when browsing on mobile. The drawback is that they eat up some precious space. + +![navigation button configuration](../img/users/configuration.navigation.button.png) + +If you don't use those buttons because you never browse on mobile or because you browse with gestures, you can disable them from the interface. + # Reading **TODO** @@ -51,15 +88,53 @@ The duration of this notification can be set. By default, the value is 0. # Sharing -**TODO** +To make your life easier, you can share directly an article within FreshRSS. + +At the moment, FreshRSS supports 15 sharing methods ranging from self-hosted services (Shaarli, etc.) to proprietary services (Facebook, etc.). + +By default, the sharing list is empty. +![Sharing configuration](../img/users/configuration.sharing.png) + +To add a new item in the list, follow those simple steps: + + 1. Select the share method in the drop-down. + 1. Press the ```✚``` sign to add it to the list. + 1. Configure the method in the list. All method names can be modified in the display. Some methods need the sharing URL to be able to work properly (ex: Shaarli). + 1. Submit your changes. + +To remove an item from the list, follow those simple steps: + + 1. Press the ```❌``` sign next to the share method you want to remove. + 1. Submit your changes. # Shortcuts -**TODO** +To ease the use of the application, FreshRSS comes with a lot of predefined keyboard shortcuts. +They allow actions to improve the user experience with a keyboard. + +Of course, if you're not satisfied with the key mapping, you can change you configuration to fit your needs. + +There are 4 types of shortcuts: + + 1. Views: they allow switching views with ease. + 1. Navigation: they allow navigation through articles, feeds, and categories. + 1. Article actions: they allow interactions with an article, like sharing or opening it on the original web-site. + 1. Other actions: they allow other interactions with the application, like opening the user queries menu or accessing the documentation. + +It's worth noting that the share article action has two levels. Once you press the shortcut, a menu containing all the share options opens. +To choose one share option, you need to select it by its number. When there is only one option, it's selected automatically though. + +The same process applies to the user queries. + +Be aware that there is no validation on the selected shortcuts. +This means that if you assign a shortcut to more than one action, you'll end up with some unexpected behavior. # User queries -**TODO** +You can configure your [user queries](./03_Main_view.md) in that section. There is not much to say here as it is pretty straightforward. +You can only change user query titles or drop them. + +At the moment, there is no helper to build a user query from here. # Users -- cgit v1.2.3 From 84d891f8cf43e4bb097a8b05a85dfeb4c48bd215 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 14 Mar 2018 17:20:41 +0100 Subject: Light Boolean search implementation (#1828) * Light Boolean search implementation "Hello intitle:World OR date:P1D example" https://github.com/FreshRSS/FreshRSS/issues/879 * Doc Boolean search * Doc typos --- app/Controllers/entryController.php | 2 +- app/Controllers/indexController.php | 2 +- app/Models/BooleanSearch.php | 55 +++++++++++ app/Models/EntryDAO.php | 186 ++++++++++++++++++++---------------- app/Models/EntryDAOSQLite.php | 8 +- app/Models/UserQuery.php | 2 +- docs/en/users/03_Main_view.md | 3 + docs/fr/users/03_Main_view.md | 5 +- p/api/greader.php | 4 +- 9 files changed, 172 insertions(+), 95 deletions(-) create mode 100644 app/Models/BooleanSearch.php diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index 28f0cb745..73e181b07 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -40,7 +40,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $get = Minz_Request::param('get'); $next_get = Minz_Request::param('nextGet', $get); $id_max = Minz_Request::param('idMax', 0); - FreshRSS_Context::$search = new FreshRSS_Search(Minz_Request::param('search', '')); + FreshRSS_Context::$search = new FreshRSS_BooleanSearch(Minz_Request::param('search', '')); FreshRSS_Context::$state = Minz_Request::param('state', 0); if (FreshRSS_Context::isStateEnabled(FreshRSS_Entry::STATE_FAVORITE)) { diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index e3dbd4664..8567b4657 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -182,7 +182,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { FreshRSS_Context::$state |= FreshRSS_Entry::STATE_READ; } - FreshRSS_Context::$search = new FreshRSS_Search(Minz_Request::param('search', '')); + FreshRSS_Context::$search = new FreshRSS_BooleanSearch(Minz_Request::param('search', '')); FreshRSS_Context::$order = Minz_Request::param( 'order', FreshRSS_Context::$user_conf->sort_order ); diff --git a/app/Models/BooleanSearch.php b/app/Models/BooleanSearch.php new file mode 100644 index 000000000..6e016f7e9 --- /dev/null +++ b/app/Models/BooleanSearch.php @@ -0,0 +1,55 @@ +raw_input = $input; + + $input = preg_replace('/:"(.*?)"/', ':"\1"', $input); + $splits = preg_split('/\b(OR)\b/i', $input, -1, PREG_SPLIT_DELIM_CAPTURE); + + $segment = ''; + $ns = count($splits); + for ($i = 0; $i < $ns; $i++) { + $segment = $segment . $splits[$i]; + if (trim($segment) == '' || strcasecmp($segment, 'OR') === 0) { + $segment = ''; + } else { + $quotes = substr_count($segment, '"') + substr_count($segment, '"'); + if ($quotes % 2 === 0) { + $segment = trim($segment); + if ($segment != '') { + $this->searches[] = new FreshRSS_Search($segment); + } + $segment = ''; + } + } + } + $segment = trim($segment); + if ($segment != '') { + $this->searches[] = new FreshRSS_Search($segment); + } + } + + public function searches() { + return $this->searches; + } + + public function __toString() { + return $this->getRawInput(); + } + + public function getRawInput() { + return $this->raw_input; + } +} diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 8cdebedc5..516aad3b8 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -437,7 +437,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { * @param integer $priorityMin * @return integer affected rows */ - public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0, $filter = null, $state = 0) { + public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0, $filters = null, $state = 0) { FreshRSS_UserDAO::touch(); if ($idMax == 0) { $idMax = time() . '000000'; @@ -454,7 +454,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } $values = array($idMax); - list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filter, $state); + list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filters, $state); $stm = $this->bd->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { @@ -480,7 +480,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { * @param integer $idMax fail safe article ID * @return integer affected rows */ - public function markReadCat($id, $idMax = 0, $filter = null, $state = 0) { + public function markReadCat($id, $idMax = 0, $filters = null, $state = 0) { FreshRSS_UserDAO::touch(); if ($idMax == 0) { $idMax = time() . '000000'; @@ -492,7 +492,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { . 'WHERE f.category=? AND e.is_read=0 AND e.id <= ?'; $values = array($id, $idMax); - list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filter, $state); + list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filters, $state); $stm = $this->bd->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { @@ -518,7 +518,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { * @param integer $idMax fail safe article ID * @return integer affected rows */ - public function markReadFeed($id_feed, $idMax = 0, $filter = null, $state = 0) { + public function markReadFeed($id_feed, $idMax = 0, $filters = null, $state = 0) { FreshRSS_UserDAO::touch(); if ($idMax == 0) { $idMax = time() . '000000'; @@ -531,7 +531,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { . 'WHERE id_feed=? AND is_read=0 AND id <= ?'; $values = array($id_feed, $idMax); - list($searchValues, $search) = $this->sqlListEntriesWhere('', $filter, $state); + list($searchValues, $search) = $this->sqlListEntriesWhere('', $filters, $state); $stm = $this->bd->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { @@ -625,7 +625,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return 'CONCAT(' . $s1 . ',' . $s2 . ')'; //MySQL } - protected function sqlListEntriesWhere($alias = '', $filter = null, $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $firstId = '', $date_min = 0) { + protected function sqlListEntriesWhere($alias = '', $filters = null, $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $firstId = '', $date_min = 0) { $search = ' '; $values = array(); if ($state & FreshRSS_Entry::STATE_NOT_READ) { @@ -650,10 +650,6 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { default: throw new FreshRSS_EntriesGetter_Exception('Bad order in Entry->listByType: [' . $order . ']!'); } - /*if ($firstId === '' && parent::$sharedDbType === 'mysql') { - //MySQL optimization. TODO: check if this is needed again, after the filtering for old articles has been removed in 0.9-dev - $firstId = $order === 'DESC' ? '9000000000'. '000000' : '0'; - }*/ if ($firstId !== '') { $search .= 'AND ' . $alias . 'id ' . ($order === 'DESC' ? '<=' : '>=') . ' ? '; $values[] = $firstId; @@ -662,91 +658,111 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $search .= 'AND ' . $alias . 'id >= ? '; $values[] = $date_min . '000000'; } - if ($filter) { - if ($filter->getMinDate()) { - $search .= 'AND ' . $alias . 'id >= ? '; - $values[] = "{$filter->getMinDate()}000000"; - } - if ($filter->getMaxDate()) { - $search .= 'AND ' . $alias . 'id <= ? '; - $values[] = "{$filter->getMaxDate()}000000"; - } - if ($filter->getMinPubdate()) { - $search .= 'AND ' . $alias . 'date >= ? '; - $values[] = $filter->getMinPubdate(); - } - if ($filter->getMaxPubdate()) { - $search .= 'AND ' . $alias . 'date <= ? '; - $values[] = $filter->getMaxPubdate(); - } + if ($filters && count($filters->searches()) > 0) { + $isOpen = false; + foreach ($filters->searches() as $filter) { + if ($filter == null) { + continue; + } + $sub_search = ''; + if ($filter->getMinDate()) { + $sub_search .= 'AND ' . $alias . 'id >= ? '; + $values[] = "{$filter->getMinDate()}000000"; + } + if ($filter->getMaxDate()) { + $sub_search .= 'AND ' . $alias . 'id <= ? '; + $values[] = "{$filter->getMaxDate()}000000"; + } + if ($filter->getMinPubdate()) { + $sub_search .= 'AND ' . $alias . 'date >= ? '; + $values[] = $filter->getMinPubdate(); + } + if ($filter->getMaxPubdate()) { + $sub_search .= 'AND ' . $alias . 'date <= ? '; + $values[] = $filter->getMaxPubdate(); + } - if ($filter->getAuthor()) { - foreach ($filter->getAuthor() as $author) { - $search .= 'AND ' . $alias . 'author LIKE ? '; - $values[] = "%{$author}%"; + if ($filter->getAuthor()) { + foreach ($filter->getAuthor() as $author) { + $sub_search .= 'AND ' . $alias . 'author LIKE ? '; + $values[] = "%{$author}%"; + } } - } - if ($filter->getIntitle()) { - foreach ($filter->getIntitle() as $title) { - $search .= 'AND ' . $alias . 'title LIKE ? '; - $values[] = "%{$title}%"; + if ($filter->getIntitle()) { + foreach ($filter->getIntitle() as $title) { + $sub_search .= 'AND ' . $alias . 'title LIKE ? '; + $values[] = "%{$title}%"; + } } - } - if ($filter->getTags()) { - foreach ($filter->getTags() as $tag) { - $search .= 'AND ' . $alias . 'tags LIKE ? '; - $values[] = "%{$tag}%"; + if ($filter->getTags()) { + foreach ($filter->getTags() as $tag) { + $sub_search .= 'AND ' . $alias . 'tags LIKE ? '; + $values[] = "%{$tag}%"; + } } - } - if ($filter->getInurl()) { - foreach ($filter->getInurl() as $url) { - $search .= 'AND CONCAT(' . $alias . 'link, ' . $alias . 'guid) LIKE ? '; - $values[] = "%{$url}%"; + if ($filter->getInurl()) { + foreach ($filter->getInurl() as $url) { + $sub_search .= 'AND CONCAT(' . $alias . 'link, ' . $alias . 'guid) LIKE ? '; + $values[] = "%{$url}%"; + } } - } - if ($filter->getNotAuthor()) { - foreach ($filter->getNotAuthor() as $author) { - $search .= 'AND (NOT ' . $alias . 'author LIKE ?) '; - $values[] = "%{$author}%"; + if ($filter->getNotAuthor()) { + foreach ($filter->getNotAuthor() as $author) { + $sub_search .= 'AND (NOT ' . $alias . 'author LIKE ?) '; + $values[] = "%{$author}%"; + } } - } - if ($filter->getNotIntitle()) { - foreach ($filter->getNotIntitle() as $title) { - $search .= 'AND (NOT ' . $alias . 'title LIKE ?) '; - $values[] = "%{$title}%"; + if ($filter->getNotIntitle()) { + foreach ($filter->getNotIntitle() as $title) { + $sub_search .= 'AND (NOT ' . $alias . 'title LIKE ?) '; + $values[] = "%{$title}%"; + } } - } - if ($filter->getNotTags()) { - foreach ($filter->getNotTags() as $tag) { - $search .= 'AND (NOT ' . $alias . 'tags LIKE ?) '; - $values[] = "%{$tag}%"; + if ($filter->getNotTags()) { + foreach ($filter->getNotTags() as $tag) { + $sub_search .= 'AND (NOT ' . $alias . 'tags LIKE ?) '; + $values[] = "%{$tag}%"; + } } - } - if ($filter->getNotInurl()) { - foreach ($filter->getNotInurl() as $url) { - $search .= 'AND (NOT CONCAT(' . $alias . 'link, ' . $alias . 'guid) LIKE ?) '; - $values[] = "%{$url}%"; + if ($filter->getNotInurl()) { + foreach ($filter->getNotInurl() as $url) { + $sub_search .= 'AND (NOT CONCAT(' . $alias . 'link, ' . $alias . 'guid) LIKE ?) '; + $values[] = "%{$url}%"; + } } - } - if ($filter->getSearch()) { - foreach ($filter->getSearch() as $search_value) { - $search .= 'AND ' . $this->sqlconcat($alias . 'title', $this->isCompressed() ? 'UNCOMPRESS(' . $alias . 'content_bin)' : '' . $alias . 'content') . ' LIKE ? '; - $values[] = "%{$search_value}%"; + if ($filter->getSearch()) { + foreach ($filter->getSearch() as $search_value) { + $sub_search .= 'AND ' . $this->sqlconcat($alias . 'title', $this->isCompressed() ? 'UNCOMPRESS(' . $alias . 'content_bin)' : '' . $alias . 'content') . ' LIKE ? '; + $values[] = "%{$search_value}%"; + } } - } - if ($filter->getNotSearch()) { - foreach ($filter->getNotSearch() as $search_value) { - $search .= 'AND (NOT ' . $this->sqlconcat($alias . 'title', $this->isCompressed() ? 'UNCOMPRESS(' . $alias . 'content_bin)' : '' . $alias . 'content') . ' LIKE ?) '; - $values[] = "%{$search_value}%"; + if ($filter->getNotSearch()) { + foreach ($filter->getNotSearch() as $search_value) { + $sub_search .= 'AND (NOT ' . $this->sqlconcat($alias . 'title', $this->isCompressed() ? 'UNCOMPRESS(' . $alias . 'content_bin)' : '' . $alias . 'content') . ' LIKE ?) '; + $values[] = "%{$search_value}%"; + } } + + if ($sub_search != '') { + if ($isOpen) { + $search .= 'OR '; + } else { + $search .= 'AND ('; + $isOpen = true; + } + $search .= '(' . substr($sub_search, 4) . ') '; + } + } + if ($isOpen) { + $search .= ') '; } } return array($values, $search); } - private function sqlListWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0) { + private function sqlListWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filters = null, $date_min = 0) { if (!$state) { $state = FreshRSS_Entry::STATE_ALL; } @@ -777,7 +793,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { throw new FreshRSS_EntriesGetter_Exception('Bad type in Entry->listByType: [' . $type . ']!'); } - list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filter, $state, $order, $firstId, $date_min); + list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filters, $state, $order, $firstId, $date_min); return array(array_merge($values, $searchValues), 'SELECT e.id FROM `' . $this->prefix . 'entry` e ' @@ -788,8 +804,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { . ($limit > 0 ? ' LIMIT ' . $limit : '')); //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ } - public function listWhereRaw($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0) { - list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min); + public function listWhereRaw($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filters = null, $date_min = 0) { + list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filters, $date_min); $sql = 'SELECT e0.id, e0.guid, e0.title, e0.author, ' . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') @@ -805,8 +821,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return $stm; } - public function listWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0) { - $stm = $this->listWhereRaw($type, $id, $state, $order, $limit, $firstId, $filter, $date_min); + public function listWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filters = null, $date_min = 0) { + $stm = $this->listWhereRaw($type, $id, $state, $order, $limit, $firstId, $filters, $date_min); return self::daoToEntries($stm->fetchAll(PDO::FETCH_ASSOC)); } @@ -827,8 +843,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return self::daoToEntries($stm->fetchAll(PDO::FETCH_ASSOC)); } - public function listIdsWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0) { //For API - list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min); + public function listIdsWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filters = null, $date_min = 0) { //For API + list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filters, $date_min); $stm = $this->bd->prepare($sql); $stm->execute($values); diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index 90aafb200..cca970e36 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -159,7 +159,7 @@ DROP TABLE IF EXISTS `tmp`; * @param integer $priorityMin * @return integer affected rows */ - public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0, $filter = null, $state = 0) { + public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0, $filters = null, $state = 0) { if ($idMax == 0) { $idMax = time() . '000000'; Minz_Log::debug('Calling markReadEntries(0) is deprecated!'); @@ -173,7 +173,7 @@ DROP TABLE IF EXISTS `tmp`; } $values = array($idMax); - list($searchValues, $search) = $this->sqlListEntriesWhere('', $filter, $state); + list($searchValues, $search) = $this->sqlListEntriesWhere('', $filters, $state); $stm = $this->bd->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { @@ -199,7 +199,7 @@ DROP TABLE IF EXISTS `tmp`; * @param integer $idMax fail safe article ID * @return integer affected rows */ - public function markReadCat($id, $idMax = 0, $filter = null, $state = 0) { + public function markReadCat($id, $idMax = 0, $filters = null, $state = 0) { if ($idMax == 0) { $idMax = time() . '000000'; Minz_Log::debug('Calling markReadCat(0) is deprecated!'); @@ -211,7 +211,7 @@ DROP TABLE IF EXISTS `tmp`; . 'id_feed IN (SELECT f.id FROM `' . $this->prefix . 'feed` f WHERE f.category=?)'; $values = array($idMax, $id); - list($searchValues, $search) = $this->sqlListEntriesWhere('', $filter, $state); + list($searchValues, $search) = $this->sqlListEntriesWhere('', $filters, $state); $stm = $this->bd->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { diff --git a/app/Models/UserQuery.php b/app/Models/UserQuery.php index 52747f538..ef94fdaf6 100644 --- a/app/Models/UserQuery.php +++ b/app/Models/UserQuery.php @@ -41,7 +41,7 @@ class FreshRSS_UserQuery { $query['search'] = ''; } // linked to deeply with the search object, need to use dependency injection - $this->search = new FreshRSS_Search($query['search']); + $this->search = new FreshRSS_BooleanSearch($query['search']); if (isset($query['state'])) { $this->state = $query['state']; } diff --git a/docs/en/users/03_Main_view.md b/docs/en/users/03_Main_view.md index adb62c7ec..57eab192d 100644 --- a/docs/en/users/03_Main_view.md +++ b/docs/en/users/03_Main_view.md @@ -181,3 +181,6 @@ Some operators can be used negatively, to exclude articles, with the same syntax `-author:name`, `-intitle:keyword`, `-inurl:keyword`, `-#tag`, `!keyword`. It is also possible to combine operators to have a very sharp filter, and it is allowed to have multiple instances of `author:`, `intitle:`, `inurl:`, `#`, and free-text. + +Combining several search criteria implies a logical *and*, but the keyword ` OR ` can be used to combine several search criteria with a logical *or* instead: +`author:Dupont OR author:Dupond` diff --git a/docs/fr/users/03_Main_view.md b/docs/fr/users/03_Main_view.md index ebf782136..af3a5a1db 100644 --- a/docs/fr/users/03_Main_view.md +++ b/docs/fr/users/03_Main_view.md @@ -180,4 +180,7 @@ Attention à ne pas introduire d’espace entre l’opérateur et la valeur rech Certains opérateurs peuvent être utilisé négativement, pour exclure des articles, avec la même syntaxe que ci-dessus, mais préfixé par `!` ou `-` : `-author:nom`, `-intitle:mot`, `-inurl:mot`, `-#tag`, `!mot`. -Il est également possible de combiner les mots-clefs pour faire un filtrage encore plus précis, and et il est autorisé d’avoir plusieurs instances de : `author:`, `intitle:`, `inurl:`, `#`, et texte libre. +Il est également possible de combiner les mots-clefs pour faire un filtrage encore plus précis, et il est autorisé d’avoir plusieurs instances de : `author:`, `intitle:`, `inurl:`, `#`, et texte libre. + +Combiner plusieurs critères implique un *et* logique, mais le mot clef ` OR ` peut être utiliser pour combiner plusieurs critères avec un *ou* logique : +`author:Dupont OR author:Dupond` diff --git a/p/api/greader.php b/p/api/greader.php index 9778aecf5..2a32ead4e 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -535,7 +535,7 @@ function streamContents($path, $include_target, $start_time, $count, $order, $ex } $entryDAO = FreshRSS_Factory::createEntryDao(); - $entries = $entryDAO->listWhere($type, $include_target, $state, $order === 'o' ? 'ASC' : 'DESC', $count, $continuation, new FreshRSS_Search(''), $start_time); + $entries = $entryDAO->listWhere($type, $include_target, $state, $order === 'o' ? 'ASC' : 'DESC', $count, $continuation, new FreshRSS_BooleanSearch(''), $start_time); $items = entriesToArray($entries); @@ -595,7 +595,7 @@ function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude } $entryDAO = FreshRSS_Factory::createEntryDao(); - $ids = $entryDAO->listIdsWhere($type, $id, $state, $order === 'o' ? 'ASC' : 'DESC', $count, $continuation, new FreshRSS_Search(''), $start_time); + $ids = $entryDAO->listIdsWhere($type, $id, $state, $order === 'o' ? 'ASC' : 'DESC', $count, $continuation, new FreshRSS_BooleanSearch(''), $start_time); if ($continuation != '') { array_shift($ids); //Discard first element that was already sent in the previous response -- cgit v1.2.3 From a0a5ec8daf3888b3e317f002fdd0281299a64cf4 Mon Sep 17 00:00:00 2001 From: Henry Date: Sun, 18 Mar 2018 10:06:02 +0100 Subject: Selinux FAQ Command to run on SELinux system enabled --- docs/en/users/07_Frequently_Asked_Questions.md | 22 +++++++++++++++++++++- docs/fr/users/07_Frequently_Asked_Questions.md | 18 ++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/docs/en/users/07_Frequently_Asked_Questions.md b/docs/en/users/07_Frequently_Asked_Questions.md index 132b2e7ec..08148ef98 100644 --- a/docs/en/users/07_Frequently_Asked_Questions.md +++ b/docs/en/users/07_Frequently_Asked_Questions.md @@ -43,4 +43,24 @@ Since [1.8.0](https://github.com/FreshRSS/FreshRSS/releases/tag/1.8.0) release, ```sh ./cli/update_user.php --user --password ``` -For more information on that matter, there is a [dedicated documentation](../../cli/README.md). \ No newline at end of file +For more information on that matter, there is a [dedicated documentation](../../cli/README.md). + +## Permissions under SELinux + +Some Linux distribution like Fedora or RedHat Enterprise Linux have SELinux system enabled. This acts like a firewall application, so all applications cannot write/modify files under certain conditions. While installing FreshRSS, step 2 can fail if the httpd process cannot write to the following directories : + + - FreshRSS/data + - FreshRSS/data/cache + - FreshRSS/data/favicons + - FreshRSS/data/users + +The following commands should be executed as root : + +```sh +semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data +semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/cache' +semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/users' +semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/favicons' + +restorecon -Rv /usr/share/FreshRSS/data +``` \ No newline at end of file diff --git a/docs/fr/users/07_Frequently_Asked_Questions.md b/docs/fr/users/07_Frequently_Asked_Questions.md index f27c92579..b86d01e4f 100644 --- a/docs/fr/users/07_Frequently_Asked_Questions.md +++ b/docs/fr/users/07_Frequently_Asked_Questions.md @@ -44,3 +44,21 @@ Depuis la version [1.8.0](https://github.com/FreshRSS/FreshRSS/releases/tag/1.8. ./cli/update_user.php --user --password ``` Pour plus d'information à ce sujet, il existe la [documentation dédiée](../../cli/README.md). + +## Gérer les permissions sous SELinux + +Certaines distributions Linux comme Fedora ou RedHat Enterprise Linux (RHEL) activent par défaut le système SELinux. Celui-ci permet de gérer des permissions au niveau des processus. Lors de l'installation de FreshRSS, l'étape 2 procède à la vérification des droits sur certains répertoires: + + - FreshRSS/data + - FreshRSS/data/cache + - FreshRSS/data/favicons + - FreshRSS/data/users + +Il faut donc exécuter les commandes suivantes en tant que root : +```sh +semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data +semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/cache' +semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/users' +semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/favicons' + +restorecon -Rv /usr/share/FreshRSS/data -- cgit v1.2.3 From 6bac71b2e8c2bc73659189ba752cbf7d158e89b3 Mon Sep 17 00:00:00 2001 From: Henry Date: Sun, 18 Mar 2018 11:24:50 +0100 Subject: Fix typo Missing quotes at the end of file --- docs/fr/users/07_Frequently_Asked_Questions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/fr/users/07_Frequently_Asked_Questions.md b/docs/fr/users/07_Frequently_Asked_Questions.md index b86d01e4f..dd0a64998 100644 --- a/docs/fr/users/07_Frequently_Asked_Questions.md +++ b/docs/fr/users/07_Frequently_Asked_Questions.md @@ -62,3 +62,4 @@ semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/users' semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/favicons' restorecon -Rv /usr/share/FreshRSS/data +``` \ No newline at end of file -- cgit v1.2.3 From 67c608d44f92b426dc100db87e4052a22585d691 Mon Sep 17 00:00:00 2001 From: Henry Date: Sun, 18 Mar 2018 12:46:55 +0100 Subject: Typo fix typo + apply command to sub dir --- docs/en/users/07_Frequently_Asked_Questions.md | 9 ++++++++- docs/fr/users/07_Frequently_Asked_Questions.md | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/en/users/07_Frequently_Asked_Questions.md b/docs/en/users/07_Frequently_Asked_Questions.md index 08148ef98..252e9c461 100644 --- a/docs/en/users/07_Frequently_Asked_Questions.md +++ b/docs/en/users/07_Frequently_Asked_Questions.md @@ -57,10 +57,17 @@ Some Linux distribution like Fedora or RedHat Enterprise Linux have SELinux syst The following commands should be executed as root : ```sh -semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data +semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data' semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/cache' semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/users' semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/favicons' +restorecon -Rv /usr/share/FreshRSS/data +``` + +If for some reasons right should be granted to the whole data directory (itself and sub-directories), execute the following commands: + +```sh +semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data(/.*)?' restorecon -Rv /usr/share/FreshRSS/data ``` \ No newline at end of file diff --git a/docs/fr/users/07_Frequently_Asked_Questions.md b/docs/fr/users/07_Frequently_Asked_Questions.md index dd0a64998..94992b154 100644 --- a/docs/fr/users/07_Frequently_Asked_Questions.md +++ b/docs/fr/users/07_Frequently_Asked_Questions.md @@ -56,10 +56,16 @@ Certaines distributions Linux comme Fedora ou RedHat Enterprise Linux (RHEL) act Il faut donc exécuter les commandes suivantes en tant que root : ```sh -semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data +semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data' semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/cache' semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/users' semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/favicons' +restorecon -Rv /usr/share/FreshRSS/data +``` + +Si les droits doivent s'appliquer à tous les sous-répertoires de data et data lui-même, exécuter les commandes suivantes: +```sh +semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data(/.*)?' restorecon -Rv /usr/share/FreshRSS/data ``` \ No newline at end of file -- cgit v1.2.3 From 16d4d5185ed653c7f4b2dadf7a143eecc42352a0 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 25 Mar 2018 10:34:46 +0200 Subject: Fix mute feeds https://github.com/FreshRSS/FreshRSS/issues/1844 --- app/Controllers/feedController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index af732951f..59c22b777 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -284,10 +284,10 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } $mtime = 0; - $ttl = $feed->ttl(); - if ($ttl < FreshRSS_Feed::TTL_DEFAULT) { + if ($feed->mute()) { continue; //Feed refresh is disabled } + $ttl = $feed->ttl(); if ((!$simplePiePush) && (!$feed_id) && ($feed->lastUpdate() + 10 >= time() - ($ttl == FreshRSS_Feed::TTL_DEFAULT ? FreshRSS_Context::$user_conf->ttl_default : $ttl))) { //Too early to refresh from source, but check whether the feed was updated by another user -- cgit v1.2.3 From 7c39c8024dc91f6a89e5601d0a7a10fc302fa106 Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Sun, 1 Apr 2018 15:40:04 +0200 Subject: [i18n] latest german translations (#1856) --- app/i18n/de/admin.php | 6 +++--- app/i18n/de/conf.php | 22 +++++++++++----------- app/i18n/de/feedback.php | 4 ++-- app/i18n/de/gen.php | 2 +- app/i18n/de/sub.php | 26 +++++++++++++------------- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/app/i18n/de/admin.php b/app/i18n/de/admin.php index 45bf2de68..f03e6cdaf 100644 --- a/app/i18n/de/admin.php +++ b/app/i18n/de/admin.php @@ -175,15 +175,15 @@ return array( 'user' => array( 'articles_and_size' => '%s Artikel (%s)', 'create' => 'Neuen Benutzer erstellen', - 'delete_users' => 'Delete user', // TODO + 'delete_users' => 'Lösche Benutzer', 'language' => 'Sprache', 'number' => 'Es wurde bis jetzt %d Account erstellt', 'numbers' => 'Es wurden bis jetzt %d Accounts erstellt', 'password_form' => 'Passwort
(für die Anmeldemethode per Webformular)', 'password_format' => 'mindestens 7 Zeichen', - 'selected' => 'Selected user', // TODO + 'selected' => 'Ausgewählter Benutzer', 'title' => 'Benutzer verwalten', - 'update_users' => 'Update user', // TODO + 'update_users' => 'Aktualisiere Benutzer', 'user_list' => 'Liste der Benutzer', 'username' => 'Nutzername', 'users' => 'Benutzer', diff --git a/app/i18n/de/conf.php b/app/i18n/de/conf.php index 93b2323a7..78f3b4510 100644 --- a/app/i18n/de/conf.php +++ b/app/i18n/de/conf.php @@ -37,12 +37,12 @@ return array( 'no_limit' => 'Keine Begrenzung', 'thin' => 'Klein', ), - 'show_nav_buttons' => 'Show the navigation buttons', //TODO + 'show_nav_buttons' => 'Zeige Navigations-Buttons', ), 'query' => array( '_' => 'Benutzerabfragen', 'deprecated' => 'Diese Abfrage ist nicht länger gültig. Die referenzierte Kategorie oder der Feed ist gelöscht worden.', - 'display' => 'Display user query results', // TODO + 'display' => 'Zeige Abfrage Ergebnisse', 'filter' => 'Angewendeter Filter:', 'get_all' => 'Alle Artikel anzeigen', 'get_category' => 'Kategorie "%s" anzeigen', @@ -53,7 +53,7 @@ return array( 'number' => 'Abfrage Nr. %d', 'order_asc' => 'Älteste Artikel zuerst anzeigen', 'order_desc' => 'Neueste Artikel zuerst anzeigen', - 'remove' => 'Remove user query', // TODO + 'remove' => 'Lösche Abfrage', 'search' => 'Suche nach "%s"', 'state_0' => 'Alle Artikel anzeigen', 'state_1' => 'Gelesene Artikel anzeigen', @@ -96,7 +96,7 @@ return array( 'display_categories_unfolded' => 'Kategorien standardmäßig eingeklappt zeigen', 'hide_read_feeds' => 'Kategorien & Feeds ohne ungelesene Artikel verstecken (funktioniert nicht mit der Einstellung „Alle Artikel zeigen“)', 'img_with_lazyload' => 'Verwende die "träges Laden"-Methode zum Laden von Bildern', - 'sides_close_article' => 'Clicking outside of article text area closes the article', //TODO + 'sides_close_article' => 'Klick außerhalb des Artikel-Textes schließt den Artikel', 'jump_next' => 'springe zum nächsten ungelesenen Geschwisterelement (Feed oder Kategorie)', 'number_divided_when_reader' => 'Geteilt durch 2 in der Lese-Ansicht.', 'read' => array( @@ -128,7 +128,7 @@ return array( ), 'sharing' => array( '_' => 'Teilen', - 'add' => 'Add a sharing method', // TODO + 'add' => 'Füge eine Teilen-Dienst hinzu', 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', 'email' => 'E-Mail', @@ -136,7 +136,7 @@ return array( 'g+' => 'Google+', 'more_information' => 'Weitere Informationen', 'print' => 'Drucken', - 'remove' => 'Remove sharing method', // TODO + 'remove' => 'Entferne Teilen-Dienst', 'shaarli' => 'Shaarli', 'share_name' => 'Anzuzeigender Teilen-Name', 'share_url' => 'Zu verwendende Teilen-URL', @@ -153,7 +153,7 @@ return array( 'collapse_article' => 'Einklappen', 'first_article' => 'Zum ersten Artikel springen', 'focus_search' => 'Auf das Suchfeld zugreifen', - 'global_view' => 'Switch to global view', // TODO + 'global_view' => 'Wechsle zur globalen Ansicht', 'help' => 'Dokumentation anzeigen', 'javascript' => 'JavaScript muss aktiviert sein, um Tastaturkürzel benutzen zu können', 'last_article' => 'Zum letzten Artikel springen', @@ -163,17 +163,17 @@ return array( 'navigation' => 'Navigation', 'navigation_help' => 'Mit der "Umschalttaste" finden die Tastenkombination auf Feeds Anwendung.
Mit der "Alt-Taste" finden die Tastenkombination auf Kategorien Anwendung.', 'next_article' => 'Zum nächsten Artikel springen', - 'normal_view' => 'Switch to normal view', // TODO + 'normal_view' => 'Wechsle zur normalen Ansicht', 'other_action' => 'Andere Aktionen', 'previous_article' => 'Zum vorherigen Artikel springen', - 'reading_view' => 'Switch to reading view', // TODO - 'rss_view' => 'Open RSS view in a new tab', // TODO + 'reading_view' => 'Wechsle zur Lese-Ansicht', + 'rss_view' => 'Öffne RSS Ansicht in neuem Tab', 'see_on_website' => 'Auf der Original-Webseite ansehen', 'shift_for_all_read' => '+ Umschalttaste, um alle Artikel als gelesen zu markieren.', 'title' => 'Tastenkombination', 'user_filter' => 'Auf Benutzerfilter zugreifen', 'user_filter_help' => 'Wenn es nur einen Benutzerfilter gibt, wird dieser verwendet. Ansonsten sind die Filter über ihre Nummer erreichbar.', - 'views' => 'Views', // TODO + 'views' => 'Ansichten', ), 'user' => array( 'articles_and_size' => '%s Artikel (%s)', diff --git a/app/i18n/de/feedback.php b/app/i18n/de/feedback.php index 0ac6efefc..6348bca32 100644 --- a/app/i18n/de/feedback.php +++ b/app/i18n/de/feedback.php @@ -102,8 +102,8 @@ return array( 'error' => 'Der Benutzer %s kann nicht gelöscht werden', ), 'updated' => array( - '_' => 'User %s has been updated', // TODO - 'error' => 'User %s has not been updated', // TODO + '_' => 'Benutzer %s wurde aktualisiert', + 'error' => 'Benutzer %s wurde nicht aktualisiert', ), ), 'profile' => array( diff --git a/app/i18n/de/gen.php b/app/i18n/de/gen.php index 8df0b1f07..6313c8b72 100644 --- a/app/i18n/de/gen.php +++ b/app/i18n/de/gen.php @@ -19,7 +19,7 @@ return array( 'see_website' => 'Webseite ansehen', 'submit' => 'Abschicken', 'truncate' => 'Alle Artikel löschen', - 'update' => 'Update', // TODO + 'update' => 'Aktualisieren', ), 'auth' => array( 'email' => 'E-Mail-Adresse', diff --git a/app/i18n/de/sub.php b/app/i18n/de/sub.php index 6d41d0e5a..7f74c275e 100644 --- a/app/i18n/de/sub.php +++ b/app/i18n/de/sub.php @@ -2,13 +2,13 @@ return array( 'api' => array( - 'documentation' => 'Copy the following URL to use it within an external tool.',// TODO - 'title' => 'API',// TODO + 'documentation' => 'Kopieren Sie die folgende URL, um sie in einem externen Tool zu verwenden.', + 'title' => 'API', ), 'bookmarklet' => array( - 'documentation' => 'Drag this button to your bookmarks toolbar or right-click it and choose "Bookmark This Link". Then click "Subscribe" button in any page you want to subscribe to.',// TODO - 'label' => 'Subscribe',// TODO - 'title' => 'Bookmarklet',// TODO + 'documentation' => 'Ziehen Sie diese Schaltfläche auf Ihre Lesezeichen-Symbolleiste oder klicken Sie mit der rechten Maustaste darauf und wählen Sie "Als Lesezeichen hinzufügen". Klicken Sie dann auf einer beliebigen Seite, die Sie abonnieren möchten, auf die Schaltfläche "Abonnieren".', + 'label' => 'Abonnieren', + 'title' => 'Bookmarklet', ), 'category' => array( '_' => 'Kategorie', @@ -35,14 +35,14 @@ return array( 'informations' => 'Information', 'keep_history' => 'Minimale Anzahl an Artikeln, die behalten wird', 'moved_category_deleted' => 'Wenn Sie eine Kategorie entfernen, werden deren Feeds automatisch in die Kategorie %s eingefügt.', - 'mute' => 'mute', // TODO + 'mute' => 'Stumm schalten', 'no_selected' => 'Kein Feed ausgewählt.', 'number_entries' => '%d Artikel', 'priority' => array( - '_' => 'Visibility', // TODO - 'archived' => 'Do not show (archived)', // TODO + '_' => 'Sichtbarkeit', + 'archived' => 'Nicht anzeigen (archiviert)', 'main_stream' => 'In Haupt-Feeds zeigen', - 'normal' => 'Show in its category', // TODO + 'normal' => 'Zeige in eigener Kategorie', ), 'stats' => 'Statistiken', 'think_to_add' => 'Sie können Feeds hinzufügen.', @@ -55,8 +55,8 @@ return array( 'pubsubhubbub' => 'Sofortbenachrichtigung mit PubSubHubbub', ), 'firefox' => array( - 'documentation' => 'Follow the steps described here to add FreshRSS to Firefox feed reader list.',// TODO - 'title' => 'Firefox feed reader',// TODO + 'documentation' => 'Folge den hier beschriebenen Schritten um FreshRSS zu Deiner Firefox RSS-Reader Liste hinzuzufügen.', + 'title' => 'Firefox RSS-Reader', ), 'import_export' => array( 'export' => 'Exportieren', @@ -73,11 +73,11 @@ return array( 'bookmark' => 'Abonnieren (FreshRSS-Lesezeichen)', 'import_export' => 'Importieren / Exportieren', 'subscription_management' => 'Abonnementverwaltung', - 'subscription_tools' => 'Subscription tools',// TODO + 'subscription_tools' => 'Abonnement-Tools', ), 'title' => array( '_' => 'Abonnementverwaltung', 'feed_management' => 'Verwaltung der RSS-Feeds', - 'subscription_tools' => 'Subscription tools',// TODO + 'subscription_tools' => 'Abonnement-Tools', ), ); -- cgit v1.2.3 From 6b9be92263cd9034ae8a84e6032b555591c948a1 Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Fri, 6 Apr 2018 20:26:08 +0200 Subject: [UI] fix active check in subscription menu (#1858) --- app/layout/aside_subscription.phtml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/layout/aside_subscription.phtml b/app/layout/aside_subscription.phtml index 6d2a5ac8f..e6a378837 100644 --- a/app/layout/aside_subscription.phtml +++ b/app/layout/aside_subscription.phtml @@ -1,15 +1,15 @@ -- cgit v1.2.3 From 6cda39a2f1fad096f850075564dd6e5790bf9925 Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Fri, 6 Apr 2018 20:52:33 +0200 Subject: fix update user for empty username (#1857) --- app/Controllers/userController.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index 2dad6a3f0..4b47b365e 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -46,6 +46,10 @@ class FreshRSS_user_Controller extends Minz_ActionController { public static function updateUser($user, $passwordPlain, $apiPasswordPlain, $userConfigUpdated = array()) { $userConfig = get_user_configuration($user); + if ($userConfig === null) { + return false; + } + if ($passwordPlain != '') { $passwordHash = self::hashPassword($passwordPlain); $userConfig->passwordHash = $passwordHash; @@ -78,8 +82,8 @@ class FreshRSS_user_Controller extends Minz_ActionController { $username = Minz_Request::param('username'); $ok = self::updateUser($username, $passwordPlain, $apiPasswordPlain, array( - 'token' => Minz_Request::param('token', null), - )); + 'token' => Minz_Request::param('token', null), + )); if ($ok) { Minz_Request::good(_t('feedback.user.updated', $username), -- cgit v1.2.3 From f62d69e1f4133c9bd8832c617fe5677f499d8a95 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 26 Apr 2018 07:46:15 +0200 Subject: Selinux FAQ Add PubSubHubbub sub directory --- docs/en/users/07_Frequently_Asked_Questions.md | 1 + docs/fr/users/07_Frequently_Asked_Questions.md | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/en/users/07_Frequently_Asked_Questions.md b/docs/en/users/07_Frequently_Asked_Questions.md index 252e9c461..8187b8b6a 100644 --- a/docs/en/users/07_Frequently_Asked_Questions.md +++ b/docs/en/users/07_Frequently_Asked_Questions.md @@ -61,6 +61,7 @@ semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data' semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/cache' semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/users' semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/favicons' +semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/PubSubHubbub' restorecon -Rv /usr/share/FreshRSS/data ``` diff --git a/docs/fr/users/07_Frequently_Asked_Questions.md b/docs/fr/users/07_Frequently_Asked_Questions.md index 94992b154..b1906147a 100644 --- a/docs/fr/users/07_Frequently_Asked_Questions.md +++ b/docs/fr/users/07_Frequently_Asked_Questions.md @@ -60,6 +60,7 @@ semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data' semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/cache' semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/users' semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/favicons' +semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/PubSubHubbub' restorecon -Rv /usr/share/FreshRSS/data ``` -- cgit v1.2.3 From 28403cd6aac6e6245ea7d189e34219b0af879603 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 26 Apr 2018 08:32:07 +0200 Subject: SeLinux context Prefer apply rights to the whole data instead of individual subdir --- docs/en/users/07_Frequently_Asked_Questions.md | 24 ++---------------------- docs/fr/users/07_Frequently_Asked_Questions.md | 22 ++-------------------- 2 files changed, 4 insertions(+), 42 deletions(-) diff --git a/docs/en/users/07_Frequently_Asked_Questions.md b/docs/en/users/07_Frequently_Asked_Questions.md index 8187b8b6a..42156b1a9 100644 --- a/docs/en/users/07_Frequently_Asked_Questions.md +++ b/docs/en/users/07_Frequently_Asked_Questions.md @@ -47,28 +47,8 @@ For more information on that matter, there is a [dedicated documentation](../../ ## Permissions under SELinux -Some Linux distribution like Fedora or RedHat Enterprise Linux have SELinux system enabled. This acts like a firewall application, so all applications cannot write/modify files under certain conditions. While installing FreshRSS, step 2 can fail if the httpd process cannot write to the following directories : - - - FreshRSS/data - - FreshRSS/data/cache - - FreshRSS/data/favicons - - FreshRSS/data/users - -The following commands should be executed as root : - -```sh -semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data' -semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/cache' -semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/users' -semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/favicons' -semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/PubSubHubbub' - -restorecon -Rv /usr/share/FreshRSS/data -``` - -If for some reasons right should be granted to the whole data directory (itself and sub-directories), execute the following commands: - +Some Linux distribution like Fedora or RedHat Enterprise Linux have SELinux system enabled. This acts like a firewall application, so all applications cannot write/modify files under certain conditions. While installing FreshRSS, step 2 can fail if the httpd process cannot write to some data sub-directories, the following command should be executed as root : ```sh semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data(/.*)?' restorecon -Rv /usr/share/FreshRSS/data -``` \ No newline at end of file +``` diff --git a/docs/fr/users/07_Frequently_Asked_Questions.md b/docs/fr/users/07_Frequently_Asked_Questions.md index b1906147a..2dc2cae97 100644 --- a/docs/fr/users/07_Frequently_Asked_Questions.md +++ b/docs/fr/users/07_Frequently_Asked_Questions.md @@ -47,26 +47,8 @@ Pour plus d'information à ce sujet, il existe la [documentation dédiée](../.. ## Gérer les permissions sous SELinux -Certaines distributions Linux comme Fedora ou RedHat Enterprise Linux (RHEL) activent par défaut le système SELinux. Celui-ci permet de gérer des permissions au niveau des processus. Lors de l'installation de FreshRSS, l'étape 2 procède à la vérification des droits sur certains répertoires: - - - FreshRSS/data - - FreshRSS/data/cache - - FreshRSS/data/favicons - - FreshRSS/data/users - -Il faut donc exécuter les commandes suivantes en tant que root : -```sh -semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data' -semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/cache' -semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/users' -semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/favicons' -semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data/PubSubHubbub' - -restorecon -Rv /usr/share/FreshRSS/data -``` - -Si les droits doivent s'appliquer à tous les sous-répertoires de data et data lui-même, exécuter les commandes suivantes: +Certaines distributions Linux comme Fedora ou RedHat Enterprise Linux (RHEL) activent par défaut le système SELinux. Celui-ci permet de gérer des permissions au niveau des processus. Lors de l'installation de FreshRSS, l'étape 2 procède à la vérification des droits sur certains répertoires, il faut donc exécuter la commande suivante en tant que root: ```sh semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/FreshRSS/data(/.*)?' restorecon -Rv /usr/share/FreshRSS/data -``` \ No newline at end of file +``` -- cgit v1.2.3 From 4381117a197efca11ce896791ef35c6e52c7130f Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 26 Apr 2018 12:00:14 +0200 Subject: cron in Docker image (#1871) * cron in Docker image https://github.com/FreshRSS/FreshRSS/issues/1869 * Fix cron CMD * Minor readme * Docker run d instead of dit There should not be a need for STDIN or TTY * Minor sed param --- Docker/Dockerfile | 10 +++++++--- Docker/README.md | 54 ++++++++++++++++++++++++++++++++++++++++++++-------- Docker/entrypoint.sh | 12 ++++++++++++ 3 files changed, 65 insertions(+), 11 deletions(-) create mode 100755 Docker/entrypoint.sh diff --git a/Docker/Dockerfile b/Docker/Dockerfile index 9b7336e3b..78a0f0e8f 100644 --- a/Docker/Dockerfile +++ b/Docker/Dockerfile @@ -15,8 +15,12 @@ WORKDIR ${FRESHRSS_ROOT} COPY . ${FRESHRSS_ROOT} COPY ./Docker/*.Apache.conf /etc/apache2/conf.d/ +RUN echo "17,37 * * * * php ${FRESHRSS_ROOT}/app/actualize_script.php 2>&1 | tee /tmp/FreshRSS.log" >> \ + /var/spool/cron/crontabs/root + +ENV CRON_MIN '' +ENTRYPOINT ["./Docker/entrypoint.sh"] + EXPOSE 80 -CMD php -f ./cli/prepare.php > /dev/null && \ - chown -R :www-data ${FRESHRSS_ROOT} && \ - chmod -R g+r ${FRESHRSS_ROOT} && chmod -R g+w ${FRESHRSS_ROOT}/data/ && \ +CMD ([ -z "$CRON_MIN" ] || crond -d 6) && \ exec httpd -D FOREGROUND diff --git a/Docker/README.md b/Docker/README.md index ccf4ab3f0..5cfe4cf38 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -26,7 +26,7 @@ sudo docker build --tag freshrss/freshrss -f Docker/Dockerfile . ## Run FreshRSS -Example using SQLite, and exposing FreshRSS on port 8080. You may have to adapt the network parameters to fit your needs. +Example using SQLite, built-in cron, and exposing FreshRSS on port 8080. You may have to adapt the parameters to fit your needs. ```sh # You can optionally run from the directory containing the FreshRSS source code: @@ -35,8 +35,9 @@ cd ./FreshRSS/ # The data will be saved on the host in `./data/` mkdir -p ./data/ -sudo docker run -dit --restart unless-stopped --log-opt max-size=10m \ +sudo docker run -d --restart unless-stopped --log-opt max-size=10m \ -v $(pwd)/data:/var/www/FreshRSS/data \ + -e 'CRON_MIN=5,35' \ -p 8080:80 \ --name freshrss freshrss/freshrss ``` @@ -50,8 +51,9 @@ See https://hub.docker.com/_/mysql/ ```sh sudo docker run -d -v /path/to/mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=rootpass -e MYSQL_DATABASE=freshrss -e MYSQL_USER=freshrss -e MYSQL_PASSWORD=pass --name mysql mysql -sudo docker run -dit --restart unless-stopped --log-opt max-size=10m \ +sudo docker run -d --restart unless-stopped --log-opt max-size=10m \ -v $(pwd)/data:/var/www/FreshRSS/data \ + -e 'CRON_MIN=17,47' \ --link mysql -p 8080:80 \ --name freshrss freshrss/freshrss ``` @@ -61,8 +63,9 @@ See https://hub.docker.com/_/postgres/ ```sh sudo docker run -d -v /path/to/pgsql-data:/var/lib/postgresql/data -e POSTGRES_DB=freshrss -e POSTGRES_USER=freshrss -e POSTGRES_PASSWORD=pass --name postgres postgres -sudo docker run -dit --restart unless-stopped --log-opt max-size=10m \ +sudo docker run -d --restart unless-stopped --log-opt max-size=10m \ -v $(pwd)/data:/var/www/FreshRSS/data \ + -e 'CRON_MIN=23,53' \ --link postgres -p 8080:80 \ --name freshrss freshrss/freshrss ``` @@ -89,16 +92,50 @@ sudo docker exec --user apache -it freshrss php ./cli/list-users.php See the [CLI documentation](../cli/) for all the other commands. -### Cron job to refresh feeds +## Cron job to automatically refresh feeds +We recommend a refresh rate of about twice per hour (see *WebSub* / *PubSubHubbub* for real-time updates). +There is no less than 3 options. Pick a single one. + +### Option 1) Cron inside the FreshRSS Docker image +Easiest, built-in solution, also used in the examples above +(but your Docker instance will have a second process in the background, without monitoring). +Just pass the environment variable `CRON_MIN` to your `docker run` command, +containing a valid cron minute definition such as `'13,43'` (recommended) or `'*/20'`. +Not passing the `CRON_MIN` environment variable – or setting it to empty string – will disable the cron daemon. + +```sh +sudo docker run -d --restart unless-stopped --log-opt max-size=10m \ + -v $(pwd)/data:/var/www/FreshRSS/data \ + -e 'CRON_MIN=13,43' \ + -p 8080:80 \ + --name freshrss freshrss/freshrss +``` + +### Option 2) Cron on the host machine +Traditional solution. Set a cron job up on your host machine, calling the `actualize_script.php` inside the FreshRSS Docker instance. +Remember not pass the `CRON_MIN` environment variable to your Docker run, to avoid running the built-in cron daemon of option 1. -#### Example on Debian / Ubuntu -Create `/etc/cron.d/FreshRSS` with: +Example on Debian / Ubuntu: Create `/etc/cron.d/FreshRSS` with: ``` 7,37 * * * * root docker exec --user apache -it freshrss php ./app/actualize_script.php > /tmp/FreshRSS.log 2>&1 ``` +### Option 3) Cron as another instance of the same FreshRSS Docker image +For advanced users. Offers good logging and monitoring with auto-restart on failure. +Watch out to use the same run parameters than in your main FreshRSS instance, for database, networking, and file system. +See cron option 1 for customising the cron schedule. + +```sh +sudo docker run -d --restart unless-stopped --log-opt max-size=10m \ + -v $(pwd)/data:/var/www/FreshRSS/data \ + -e 'CRON_MIN=17,37' \ + --name freshrss_cron freshrss/freshrss \ + crond -f -d 6 +``` + + ## Debugging ```sh @@ -115,5 +152,6 @@ ls /var/www/FreshRSS/ ## Deployment in production -Use a reverse proxy on your host server, such as [Træfik](https://traefik.io/) or [nginx](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/), +Use a reverse proxy on your host server, such as [Træfik](https://traefik.io/) +or [nginx](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/), with HTTPS, for instance using [Let’s Encrypt](https://letsencrypt.org/). diff --git a/Docker/entrypoint.sh b/Docker/entrypoint.sh new file mode 100755 index 000000000..5b643da93 --- /dev/null +++ b/Docker/entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +php -f ./cli/prepare.php > /dev/null + +chown -R :www-data . +chmod -R g+r . && chmod -R g+w ./data/ + +if [ -n "$CRON_MIN" ]; then + sed -r -i "/FreshRSS/s/^[^ ]+ /$CRON_MIN /" /var/spool/cron/crontabs/root +fi + +exec "$@" -- cgit v1.2.3 From 2b8da263ca53537ed919dbdf95a60891b420dc0a Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 26 Apr 2018 12:30:06 +0200 Subject: Changelog 1871 etc. https://github.com/FreshRSS/FreshRSS/issues/879 https://github.com/FreshRSS/FreshRSS/pull/1828 https://github.com/FreshRSS/FreshRSS/pull/1829 https://github.com/FreshRSS/FreshRSS/issues/1844 https://github.com/FreshRSS/FreshRSS/pull/1845 https://github.com/FreshRSS/FreshRSS/pull/1856 https://github.com/FreshRSS/FreshRSS/pull/1857 https://github.com/FreshRSS/FreshRSS/pull/1858 https://github.com/FreshRSS/FreshRSS/issues/1869 https://github.com/FreshRSS/FreshRSS/pull/1871 --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6638c8e8e..db8476c11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,22 @@ ## 2018-0X-XX FreshRSS 1.10.3-dev +* Features + * Light Boolean search implementation [879](https://github.com/FreshRSS/FreshRSS/issues/879) + * All parts are implicitly `AND` (which must not be written), except if `OR` is stated. + * No use of parentheses. Support for quotes to disable the Boolean search, like `"This or that"`. + * Example: `Hello intitle:World OR date:P1D example OR author:Else intitle:"This or that"` +* Deployment + * Includes an optional cron daemon in Docker to refresh feeds automatically [#1869](https://github.com/FreshRSS/FreshRSS/issues/1869) +* Bug fixing + * Fix muted feeds that were not actually muted [#1844](https://github.com/FreshRSS/FreshRSS/issues/1844) + * Fix errors in case of empty/wrong username when updating user settings [#1857](https://github.com/FreshRSS/FreshRSS/pull/1857) + * Fixes in subscription menu [#1858](https://github.com/FreshRSS/FreshRSS/pull/1858) * UI * Add tooltips on user queries [#1823](https://github.com/FreshRSS/FreshRSS/pull/1823) +* I18n + * Improve i18n tools [#1829](https://github.com/FreshRSS/FreshRSS/pull/1829) + * Updated German [#1856](https://github.com/FreshRSS/FreshRSS/pull/1856) * Misc. * Add error log information when SQLite has not enough temp space [#1816](https://github.com/FreshRSS/FreshRSS/issues/1816) -- cgit v1.2.3 From 90998c24370ea14b2b78d1e2a55bfe33947b2aa7 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 26 Apr 2018 13:52:44 +0200 Subject: Apache overrides were not enough (#1873) It looks like overrides of CustomLog, ErrorLog, Listen did not do the job fully. Updated to comment out those lines in httpd.conf. Fixes the fact that logs were kept in /var/log/apache2/ (instead of being only given via STDOUT to Docker logs) and the "internal dummy connection" bug over IPv6 (Docker is typically only IPv4). --- Docker/Dockerfile | 5 +++-- Docker/FreshRSS.Apache.conf | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Docker/Dockerfile b/Docker/Dockerfile index 78a0f0e8f..189d7175a 100644 --- a/Docker/Dockerfile +++ b/Docker/Dockerfile @@ -15,8 +15,9 @@ WORKDIR ${FRESHRSS_ROOT} COPY . ${FRESHRSS_ROOT} COPY ./Docker/*.Apache.conf /etc/apache2/conf.d/ -RUN echo "17,37 * * * * php ${FRESHRSS_ROOT}/app/actualize_script.php 2>&1 | tee /tmp/FreshRSS.log" >> \ - /var/spool/cron/crontabs/root +RUN sed -r -i "/^[ ]*(CustomLog|ErrorLog|Listen) /s/^/#/" /etc/apache2/httpd.conf && \ + echo "17,37 * * * * php ${FRESHRSS_ROOT}/app/actualize_script.php 2>&1 | tee /tmp/FreshRSS.log" >> \ + /var/spool/cron/crontabs/root ENV CRON_MIN '' ENTRYPOINT ["./Docker/entrypoint.sh"] diff --git a/Docker/FreshRSS.Apache.conf b/Docker/FreshRSS.Apache.conf index 59151d749..adfc804c6 100644 --- a/Docker/FreshRSS.Apache.conf +++ b/Docker/FreshRSS.Apache.conf @@ -17,8 +17,8 @@ ServerName freshrss.localhost Listen 0.0.0.0:80 DocumentRoot /var/www/FreshRSS/p/ +CustomLog /dev/stdout combined ErrorLog /dev/stderr -TransferLog /dev/stdout AllowEncodedSlashes On -- cgit v1.2.3 From db133f9bb3b1ebcc5aceabbb7569bf79b342f129 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 26 Apr 2018 13:55:58 +0200 Subject: Changelog 1873 https://github.com/FreshRSS/FreshRSS/pull/1873 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index db8476c11..38292e83f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * Deployment * Includes an optional cron daemon in Docker to refresh feeds automatically [#1869](https://github.com/FreshRSS/FreshRSS/issues/1869) * Bug fixing + * Fix Docker bug affecting Apache `CustomLog` (unwanted local copy of access logs), `ErrorLog`, `Listen` (IPv6 bug) [#1873](https://github.com/FreshRSS/FreshRSS/pull/1873) * Fix muted feeds that were not actually muted [#1844](https://github.com/FreshRSS/FreshRSS/issues/1844) * Fix errors in case of empty/wrong username when updating user settings [#1857](https://github.com/FreshRSS/FreshRSS/pull/1857) * Fixes in subscription menu [#1858](https://github.com/FreshRSS/FreshRSS/pull/1858) -- cgit v1.2.3 From 1b8ab5199b4ff36e73696500a678386cf39c7f24 Mon Sep 17 00:00:00 2001 From: ColonelMoutarde <4697568+ColonelMoutarde@users.noreply.github.com> Date: Thu, 26 Apr 2018 16:34:10 +0200 Subject: strict comparaision for null (#1874) With booleans and null, only strict comparison (with === operator) should be used to lower bug risks and to improve performances. --- app/views/helpers/export/articles.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/helpers/export/articles.phtml b/app/views/helpers/export/articles.phtml index 49c370023..75651483a 100644 --- a/app/views/helpers/export/articles.phtml +++ b/app/views/helpers/export/articles.phtml @@ -23,7 +23,7 @@ foreach ($this->entriesRaw as $entryRaw) { $entry = FreshRSS_EntryDAO::daoToEntry($entryRaw); if (!isset($this->feed)) { $feed = FreshRSS_CategoryDAO::findFeed($this->categories, $entry->feed()); - if ($feed == null) { + if ($feed === null) { $feed = $entry->feed(true); } } else { -- cgit v1.2.3 From 404ca869e9aafa40931914812b8552e4b9973694 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 26 Apr 2018 17:13:03 +0200 Subject: Tab to spaces in multiline shell Copy pasting the commands was not working due to tabs, in e.g. Ubuntu terminal. --- Docker/README.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Docker/README.md b/Docker/README.md index 5cfe4cf38..1767ae85d 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -36,10 +36,10 @@ cd ./FreshRSS/ mkdir -p ./data/ sudo docker run -d --restart unless-stopped --log-opt max-size=10m \ - -v $(pwd)/data:/var/www/FreshRSS/data \ - -e 'CRON_MIN=5,35' \ - -p 8080:80 \ - --name freshrss freshrss/freshrss + -v $(pwd)/data:/var/www/FreshRSS/data \ + -e 'CRON_MIN=5,35' \ + -p 8080:80 \ + --name freshrss freshrss/freshrss ``` ### Examples with external databases @@ -52,10 +52,10 @@ See https://hub.docker.com/_/mysql/ ```sh sudo docker run -d -v /path/to/mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=rootpass -e MYSQL_DATABASE=freshrss -e MYSQL_USER=freshrss -e MYSQL_PASSWORD=pass --name mysql mysql sudo docker run -d --restart unless-stopped --log-opt max-size=10m \ - -v $(pwd)/data:/var/www/FreshRSS/data \ - -e 'CRON_MIN=17,47' \ - --link mysql -p 8080:80 \ - --name freshrss freshrss/freshrss + -v $(pwd)/data:/var/www/FreshRSS/data \ + -e 'CRON_MIN=17,47' \ + --link mysql -p 8080:80 \ + --name freshrss freshrss/freshrss ``` #### PostgreSQL @@ -64,10 +64,10 @@ See https://hub.docker.com/_/postgres/ ```sh sudo docker run -d -v /path/to/pgsql-data:/var/lib/postgresql/data -e POSTGRES_DB=freshrss -e POSTGRES_USER=freshrss -e POSTGRES_PASSWORD=pass --name postgres postgres sudo docker run -d --restart unless-stopped --log-opt max-size=10m \ - -v $(pwd)/data:/var/www/FreshRSS/data \ - -e 'CRON_MIN=23,53' \ - --link postgres -p 8080:80 \ - --name freshrss freshrss/freshrss + -v $(pwd)/data:/var/www/FreshRSS/data \ + -e 'CRON_MIN=23,53' \ + --link postgres -p 8080:80 \ + --name freshrss freshrss/freshrss ``` ## Update @@ -105,10 +105,10 @@ Not passing the `CRON_MIN` environment variable – or setting it to empty strin ```sh sudo docker run -d --restart unless-stopped --log-opt max-size=10m \ - -v $(pwd)/data:/var/www/FreshRSS/data \ - -e 'CRON_MIN=13,43' \ - -p 8080:80 \ - --name freshrss freshrss/freshrss + -v $(pwd)/data:/var/www/FreshRSS/data \ + -e 'CRON_MIN=13,43' \ + -p 8080:80 \ + --name freshrss freshrss/freshrss ``` ### Option 2) Cron on the host machine @@ -129,10 +129,10 @@ See cron option 1 for customising the cron schedule. ```sh sudo docker run -d --restart unless-stopped --log-opt max-size=10m \ - -v $(pwd)/data:/var/www/FreshRSS/data \ - -e 'CRON_MIN=17,37' \ - --name freshrss_cron freshrss/freshrss \ - crond -f -d 6 + -v $(pwd)/data:/var/www/FreshRSS/data \ + -e 'CRON_MIN=17,37' \ + --name freshrss_cron freshrss/freshrss \ + crond -f -d 6 ``` -- cgit v1.2.3 From b552abb3327f09baa1c0f4e821dc9f6bd6ef738e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 1 May 2018 17:02:11 +0200 Subject: JSON column for feeds (#1838) * Draft of JSON column for feeds https://github.com/FreshRSS/FreshRSS/issues/1654 * Add some per-feed options * Feed cURL timeout * Mark updated articles as read https://github.com/FreshRSS/FreshRSS/issues/891 * Mark as read upon reception https://github.com/FreshRSS/FreshRSS/issues/1702 * Ignore SSL (unsafe) https://github.com/FreshRSS/FreshRSS/issues/1811 * Try PHPCS workaround While waiting for a better syntax support --- app/Controllers/feedController.php | 15 ++++-- app/Controllers/subscriptionController.php | 19 ++++++-- app/Models/DatabaseDAO.php | 2 +- app/Models/Factory.php | 8 +++- app/Models/Feed.php | 26 ++++++++++- app/Models/FeedDAO.php | 73 ++++++++++++++++++++++++++---- app/Models/FeedDAOSQLite.php | 17 +++++++ app/SQL/install.sql.mysql.php | 1 + app/SQL/install.sql.pgsql.php | 1 + app/SQL/install.sql.sqlite.php | 1 + app/i18n/cz/sub.php | 2 + app/i18n/de/sub.php | 2 + app/i18n/en/sub.php | 2 + app/i18n/es/sub.php | 2 + app/i18n/fr/sub.php | 2 + app/i18n/he/sub.php | 2 + app/i18n/it/sub.php | 2 + app/i18n/kr/sub.php | 2 + app/i18n/nl/sub.php | 2 + app/i18n/pt-br/sub.php | 2 + app/i18n/ru/sub.php | 2 + app/i18n/tr/sub.php | 2 + app/i18n/zh-cn/sub.php | 2 + app/views/helpers/feed/update.phtml | 47 +++++++++++++++++++ lib/Minz/ModelPdo.php | 4 +- lib/Minz/Request.php | 13 ++++++ lib/lib_rss.php | 15 ++++-- 27 files changed, 242 insertions(+), 26 deletions(-) create mode 100644 app/Models/FeedDAOSQLite.php diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 59c22b777..ca85e7cb8 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -84,6 +84,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { 'description' => $feed->description(), 'lastUpdate' => time(), 'httpAuth' => $feed->httpAuth(), + 'attributes' => array(), ); $id = $feedDAO->addFeed($values); @@ -271,7 +272,6 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $updated_feeds = 0; $nb_new_articles = 0; - $is_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0; foreach ($feeds as $feed) { $url = $feed->url(); //For detection of HTTP 301 @@ -353,8 +353,10 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } else { //This entry already exists but has been updated //Minz_Log::debug('Entry with GUID `' . $entry->guid() . '` updated in feed ' . $feed->id() . //', old hash ' . $existingHash . ', new hash ' . $entry->hash()); - //TODO: Make an updated/is_read policy by feed, in addition to the global one. - $needFeedCacheRefresh = FreshRSS_Context::$user_conf->mark_updated_article_unread; + $mark_updated_article_unread = $feed->attributes('mark_updated_article_unread') !== null ? ( + $feed->attributes('mark_updated_article_unread') + ) : FreshRSS_Context::$user_conf->mark_updated_article_unread; + $needFeedCacheRefresh = $mark_updated_article_unread; $entry->_isRead(FreshRSS_Context::$user_conf->mark_updated_article_unread ? false : null); //Change is_read according to policy. if (!$entryDAO->inTransaction()) { $entryDAO->beginTransaction(); @@ -365,15 +367,18 @@ class FreshRSS_feed_Controller extends Minz_ActionController { // This entry should not be added considering configuration and date. $oldGuids[] = $entry->guid(); } else { + $read_upon_reception = $feed->attributes('read_upon_reception') !== null ? ( + $feed->attributes('read_upon_reception') + ) : FreshRSS_Context::$user_conf->mark_when['reception']; if ($isNewFeed) { $id = min(time(), $entry_date) . uSecString(); - $entry->_isRead($is_read); + $entry->_isRead($read_upon_reception); } elseif ($entry_date < $date_min) { $id = min(time(), $entry_date) . uSecString(); $entry->_isRead(true); //Old article that was not in database. Probably an error, so mark as read } else { $id = uTimeString(); - $entry->_isRead($is_read); + $entry->_isRead($read_upon_reception); } $entry->_id($id); diff --git a/app/Controllers/subscriptionController.php b/app/Controllers/subscriptionController.php index 37efd3b57..860cd912f 100644 --- a/app/Controllers/subscriptionController.php +++ b/app/Controllers/subscriptionController.php @@ -15,7 +15,7 @@ class FreshRSS_subscription_Controller extends Minz_ActionController { } $catDAO = new FreshRSS_CategoryDAO(); - $feedDAO = new FreshRSS_FeedDAO(); + $feedDAO = FreshRSS_Factory::createFeedDao(); $catDAO->checkDefault(); $feedDAO->updateTTL(); @@ -74,9 +74,10 @@ class FreshRSS_subscription_Controller extends Minz_ActionController { return; } - $this->view->feed = $this->view->feeds[$id]; + $feed = $this->view->feeds[$id]; + $this->view->feed = $feed; - Minz_View::prependTitle(_t('sub.title.feed_management') . ' · ' . $this->view->feed->name() . ' · '); + Minz_View::prependTitle(_t('sub.title.feed_management') . ' · ' . $feed->name() . ' · '); if (Minz_Request::isPost()) { $user = trim(Minz_Request::param('http_user_feed' . $id, '')); @@ -95,6 +96,13 @@ class FreshRSS_subscription_Controller extends Minz_ActionController { $ttl = FreshRSS_Context::$user_conf->ttl_default; } + $feed->_attributes('mark_updated_article_unread', Minz_Request::paramTernary('mark_updated_article_unread')); + $feed->_attributes('read_upon_reception', Minz_Request::paramTernary('read_upon_reception')); + $feed->_attributes('ssl_verify', Minz_Request::paramTernary('ssl_verify')); + + $timeout = intval(Minz_Request::param('timeout', 0)); + $feed->_attributes('timeout', $timeout > 0 ? $timeout : null); + $values = array( 'name' => Minz_Request::param('name', ''), 'description' => sanitizeHTML(Minz_Request::param('description', '', true)), @@ -106,14 +114,15 @@ class FreshRSS_subscription_Controller extends Minz_ActionController { 'httpAuth' => $httpAuth, 'keep_history' => intval(Minz_Request::param('keep_history', FreshRSS_Feed::KEEP_HISTORY_DEFAULT)), 'ttl' => $ttl * ($mute ? -1 : 1), + 'attributes' => $feed->attributes() ); invalidateHttpCache(); $url_redirect = array('c' => 'subscription', 'params' => array('id' => $id)); if ($feedDAO->updateFeed($id, $values) !== false) { - $this->view->feed->_category($cat); - $this->view->feed->faviconPrepare(); + $feed->_category($cat); + $feed->faviconPrepare(); Minz_Request::good(_t('feedback.sub.feed.updated'), $url_redirect); } else { diff --git a/app/Models/DatabaseDAO.php b/app/Models/DatabaseDAO.php index f5469f2b7..b8e5577e4 100644 --- a/app/Models/DatabaseDAO.php +++ b/app/Models/DatabaseDAO.php @@ -50,7 +50,7 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo { public function feedIsCorrect() { return $this->checkTable('feed', array( 'id', 'url', 'category', 'name', 'website', 'description', 'lastUpdate', - 'priority', 'pathEntries', 'httpAuth', 'error', 'keep_history', 'ttl', + 'priority', 'pathEntries', 'httpAuth', 'error', 'keep_history', 'ttl', 'attributes', 'cache_nbEntries', 'cache_nbUnreads' )); } diff --git a/app/Models/Factory.php b/app/Models/Factory.php index dfccc883e..764987c46 100644 --- a/app/Models/Factory.php +++ b/app/Models/Factory.php @@ -3,7 +3,13 @@ class FreshRSS_Factory { public static function createFeedDao($username = null) { - return new FreshRSS_FeedDAO($username); + $conf = Minz_Configuration::get('system'); + switch ($conf->db['type']) { + case 'sqlite': + return new FreshRSS_FeedDAOSQLite($username); + default: + return new FreshRSS_FeedDAO($username); + } } public static function createEntryDao($username = null) { diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 196d94931..04101c10d 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -26,6 +26,7 @@ class FreshRSS_Feed extends Minz_Model { private $error = false; private $keep_history = self::KEEP_HISTORY_DEFAULT; private $ttl = self::TTL_DEFAULT; + private $attributes = array(); private $mute = false; private $hash = null; private $lockPath = ''; @@ -114,6 +115,13 @@ class FreshRSS_Feed extends Minz_Model { public function ttl() { return $this->ttl; } + public function attributes($key = '') { + if ($key == '') { + return $this->attributes; + } else { + return isset($this->attributes[$key]) ? $this->attributes[$key] : null; + } + } public function mute() { return $this->mute; } @@ -234,6 +242,22 @@ class FreshRSS_Feed extends Minz_Model { $this->ttl = abs($value); $this->mute = $value < self::TTL_DEFAULT; } + + public function _attributes($key, $value) { + if ($key == '') { + if (is_string($value)) { + $value = json_decode($value, true); + } + if (is_array($value)) { + $this->attributes = $value; + } + } elseif ($value === null) { + unset($this->attributes[$key]); + } else { + $this->attributes[$key] = $value; + } + } + public function _nbNotRead($value) { $this->nbNotRead = intval($value); } @@ -253,7 +277,7 @@ class FreshRSS_Feed extends Minz_Model { if ($this->httpAuth != '') { $url = preg_replace('#((.+)://)(.+)#', '${1}' . $this->httpAuth . '@${3}', $url); } - $feed = customSimplePie(); + $feed = customSimplePie($this->attributes()); if (substr($url, -11) === '#force_feed') { $feed->force_feed(true); $url = substr($url, 0, -11); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 5c6e613d3..f968ae98b 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -1,6 +1,33 @@ bd->prepare('ALTER TABLE `' . $this->prefix . 'feed` ADD COLUMN attributes TEXT'); + return $stm && $stm->execute(); + } + } catch (Exception $e) { + Minz_Log::error('FreshRSS_FeedDAO::addColumn error: ' . $e->getMessage()); + } + return false; + } + + protected function autoUpdateDb($errorInfo) { + if (isset($errorInfo[0])) { + if ($errorInfo[0] === '42S22' || $errorInfo[0] === '42703') { //ER_BAD_FIELD_ERROR (Mysql), undefined_column (PostgreSQL) + foreach (array('attributes') as $column) { + if (stripos($errorInfo[2], $column) !== false) { + return $this->addColumn($column); + } + } + } + } + return false; + } + public function addFeed($valuesTmp) { $sql = ' INSERT INTO `' . $this->prefix . 'feed` @@ -15,10 +42,11 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { `httpAuth`, error, keep_history, - ttl + ttl, + attributes ) VALUES - (?, ?, ?, ?, ?, ?, 10, ?, 0, ?, ?)'; + (?, ?, ?, ?, ?, ?, 10, ?, 0, ?, ?, ?)'; $stm = $this->bd->prepare($sql); $valuesTmp['url'] = safe_ascii($valuesTmp['url']); @@ -34,12 +62,16 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { base64_encode($valuesTmp['httpAuth']), FreshRSS_Feed::KEEP_HISTORY_DEFAULT, FreshRSS_Feed::TTL_DEFAULT, + isset($valuesTmp['attributes']) ? json_encode($valuesTmp['attributes']) : '', ); if ($stm && $stm->execute($values)) { return $this->bd->lastInsertId('"' . $this->prefix . 'feed_id_seq"'); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + if ($this->autoUpdateDb($info)) { + return $this->addFeed($valuesTmp); + } Minz_Log::error('SQL error addFeed: ' . $info[2]); return false; } @@ -60,7 +92,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { 'website' => $feed->website(), 'description' => $feed->description(), 'lastUpdate' => 0, - 'httpAuth' => $feed->httpAuth() + 'httpAuth' => $feed->httpAuth(), + 'attributes' => $feed->attributes(), ); $id = $this->addFeed($values); @@ -87,8 +120,10 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { foreach ($valuesTmp as $key => $v) { $set .= '`' . $key . '`=?, '; - if ($key == 'httpAuth') { + if ($key === 'httpAuth') { $valuesTmp[$key] = base64_encode($v); + } elseif ($key === 'attributes') { + $valuesTmp[$key] = json_encode($v); } } $set = substr($set, 0, -2); @@ -105,11 +140,25 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return $stm->rowCount(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + if ($this->autoUpdateDb($info)) { + return $this->updateFeed($id, $valuesTmp); + } Minz_Log::error('SQL error updateFeed: ' . $info[2] . ' for feed ' . $id); return false; } } + public function updateFeedAttribute($feed, $key, $value) { + if ($feed instanceof FreshRSS_Feed) { + $feed->_attributes($key, $value); + return $this->updateFeed( + $feed->id(), + array('attributes' => $feed->attributes()) + ); + } + return false; + } + public function updateLastUpdate($id, $inError = false, $mtime = 0) { //See also updateCachedValue() $sql = 'UPDATE `' . $this->prefix . 'feed` ' . 'SET `lastUpdate`=?, error=? ' @@ -252,15 +301,22 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { */ public function listFeedsOrderUpdate($defaultCacheDuration = 3600) { $this->updateTTL(); - $sql = 'SELECT id, url, name, website, `lastUpdate`, `pathEntries`, `httpAuth`, keep_history, ttl ' + $sql = 'SELECT id, url, name, website, `lastUpdate`, `pathEntries`, `httpAuth`, keep_history, ttl, attributes ' . 'FROM `' . $this->prefix . 'feed` ' . ($defaultCacheDuration < 0 ? '' : 'WHERE ttl >= ' . FreshRSS_Feed::TTL_DEFAULT . ' AND `lastUpdate` < (' . (time() + 60) . '-(CASE WHEN ttl=' . FreshRSS_Feed::TTL_DEFAULT . ' THEN ' . intval($defaultCacheDuration) . ' ELSE ttl END)) ') . 'ORDER BY `lastUpdate`'; $stm = $this->bd->prepare($sql); - $stm->execute(); - - return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC)); + if ($stm && $stm->execute()) { + return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC)); + } else { + $info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo(); + if ($this->autoUpdateDb($info)) { + return $this->listFeedsOrderUpdate($defaultCacheDuration); + } + Minz_Log::error('SQL error listFeedsOrderUpdate: ' . $info[2]); + return array(); + } } public function listByCategory($cat) { @@ -385,6 +441,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $myFeed->_error(isset($dao['error']) ? $dao['error'] : 0); $myFeed->_keepHistory(isset($dao['keep_history']) ? $dao['keep_history'] : FreshRSS_Feed::KEEP_HISTORY_DEFAULT); $myFeed->_ttl(isset($dao['ttl']) ? $dao['ttl'] : FreshRSS_Feed::TTL_DEFAULT); + $myFeed->_attributes('', isset($dao['attributes']) ? $dao['attributes'] : ''); $myFeed->_nbNotRead(isset($dao['cache_nbUnreads']) ? $dao['cache_nbUnreads'] : 0); $myFeed->_nbEntries(isset($dao['cache_nbEntries']) ? $dao['cache_nbEntries'] : 0); if (isset($dao['id'])) { diff --git a/app/Models/FeedDAOSQLite.php b/app/Models/FeedDAOSQLite.php new file mode 100644 index 000000000..3c203b378 --- /dev/null +++ b/app/Models/FeedDAOSQLite.php @@ -0,0 +1,17 @@ +bd->query("PRAGMA table_info('feed')")) { + $columns = $tableInfo->fetchAll(PDO::FETCH_COLUMN, 1); + foreach (array('attributes') as $column) { + if (!in_array($column, $columns)) { + return $this->addColumn($column); + } + } + } + return false; + } + +} diff --git a/app/SQL/install.sql.mysql.php b/app/SQL/install.sql.mysql.php index b94a24298..747a0a6b3 100644 --- a/app/SQL/install.sql.mysql.php +++ b/app/SQL/install.sql.mysql.php @@ -24,6 +24,7 @@ CREATE TABLE IF NOT EXISTS `%1$sfeed` ( `error` boolean DEFAULT 0, `keep_history` MEDIUMINT NOT NULL DEFAULT -2, -- v0.7 `ttl` INT NOT NULL DEFAULT 0, -- v0.7.3 + `attributes` TEXT, -- v1.11.0 `cache_nbEntries` int DEFAULT 0, -- v0.7 `cache_nbUnreads` int DEFAULT 0, -- v0.7 PRIMARY KEY (`id`), diff --git a/app/SQL/install.sql.pgsql.php b/app/SQL/install.sql.pgsql.php index 23afdb783..99f5a05d3 100644 --- a/app/SQL/install.sql.pgsql.php +++ b/app/SQL/install.sql.pgsql.php @@ -22,6 +22,7 @@ $SQL_CREATE_TABLES = array( "error" smallint DEFAULT 0, "keep_history" INT NOT NULL DEFAULT -2, "ttl" INT NOT NULL DEFAULT 0, + "attributes" TEXT, -- v1.11.0 "cache_nbEntries" INT DEFAULT 0, "cache_nbUnreads" INT DEFAULT 0, FOREIGN KEY ("category") REFERENCES "%1$scategory" ("id") ON DELETE SET NULL ON UPDATE CASCADE diff --git a/app/SQL/install.sql.sqlite.php b/app/SQL/install.sql.sqlite.php index d8e670bc8..cbfb719e5 100644 --- a/app/SQL/install.sql.sqlite.php +++ b/app/SQL/install.sql.sqlite.php @@ -21,6 +21,7 @@ $SQL_CREATE_TABLES = array( `error` boolean DEFAULT 0, `keep_history` MEDIUMINT NOT NULL DEFAULT -2, `ttl` INT NOT NULL DEFAULT 0, + `attributes` TEXT, -- v1.11.0 `cache_nbEntries` int DEFAULT 0, `cache_nbUnreads` int DEFAULT 0, FOREIGN KEY (`category`) REFERENCES `category`(`id`) ON DELETE SET NULL ON UPDATE CASCADE, diff --git a/app/i18n/cz/sub.php b/app/i18n/cz/sub.php index ec77be317..5caf9acbe 100644 --- a/app/i18n/cz/sub.php +++ b/app/i18n/cz/sub.php @@ -44,8 +44,10 @@ return array( 'main_stream' => 'Zobrazit ve “Všechny kanály”', 'normal' => 'Show in its category', // TODO ), + 'ssl_verify' => 'Verify SSL security', //TODO 'stats' => 'Statistika', 'think_to_add' => 'Můžete přidat kanály.', + 'timeout' => 'Timeout in seconds', //TODO 'title' => 'Název', 'title_add' => 'Přidat RSS kanál', 'ttl' => 'Neobnovovat častěji než', diff --git a/app/i18n/de/sub.php b/app/i18n/de/sub.php index 7f74c275e..0ba818c69 100644 --- a/app/i18n/de/sub.php +++ b/app/i18n/de/sub.php @@ -44,8 +44,10 @@ return array( 'main_stream' => 'In Haupt-Feeds zeigen', 'normal' => 'Zeige in eigener Kategorie', ), + 'ssl_verify' => 'Verify SSL security', //TODO 'stats' => 'Statistiken', 'think_to_add' => 'Sie können Feeds hinzufügen.', + 'timeout' => 'Timeout in seconds', //TODO 'title' => 'Titel', 'title_add' => 'Einen RSS-Feed hinzufügen', 'ttl' => 'Aktualisiere automatisch nicht öfter als', diff --git a/app/i18n/en/sub.php b/app/i18n/en/sub.php index b9bae7955..5ff41a4b3 100644 --- a/app/i18n/en/sub.php +++ b/app/i18n/en/sub.php @@ -44,8 +44,10 @@ return array( 'main_stream' => 'Show in main stream', 'normal' => 'Show in its category', ), + 'ssl_verify' => 'Verify SSL security', 'stats' => 'Statistics', 'think_to_add' => 'You may add some feeds.', + 'timeout' => 'Timeout in seconds', 'title' => 'Title', 'title_add' => 'Add a RSS feed', 'ttl' => 'Do not automatically refresh more often than', diff --git a/app/i18n/es/sub.php b/app/i18n/es/sub.php index 091c1e3e3..3abc85578 100755 --- a/app/i18n/es/sub.php +++ b/app/i18n/es/sub.php @@ -39,8 +39,10 @@ return array( 'main_stream' => 'Mostrar en salida principal', 'normal' => 'Show in its category', // TODO ), + 'ssl_verify' => 'Verify SSL security', //TODO 'stats' => 'Estadísticas', 'think_to_add' => 'Puedes añadir fuentes.', + 'timeout' => 'Timeout in seconds', //TODO 'title' => 'Título', 'title_add' => 'Añadir fuente RSS', 'ttl' => 'No actualizar de forma automática con una frecuencia mayor a', diff --git a/app/i18n/fr/sub.php b/app/i18n/fr/sub.php index 04be55aa5..c6af2fb90 100644 --- a/app/i18n/fr/sub.php +++ b/app/i18n/fr/sub.php @@ -44,8 +44,10 @@ return array( 'main_stream' => 'Afficher dans le flux principal', 'normal' => 'Afficher dans sa catégorie', ), + 'ssl_verify' => 'Vérification sécurité SSL', 'stats' => 'Statistiques', 'think_to_add' => 'Vous pouvez ajouter des flux.', + 'timeout' => 'Délai d’attente en secondes', 'title' => 'Titre', 'title_add' => 'Ajouter un flux RSS', 'ttl' => 'Ne pas automatiquement rafraîchir plus souvent que', diff --git a/app/i18n/he/sub.php b/app/i18n/he/sub.php index 849a1d5bd..a263cd728 100644 --- a/app/i18n/he/sub.php +++ b/app/i18n/he/sub.php @@ -44,8 +44,10 @@ return array( 'main_stream' => 'הצגה בזרם המרכזי', 'normal' => 'Show in its category', // TODO ), + 'ssl_verify' => 'Verify SSL security', //TODO 'stats' => 'סטטיסטיקות', 'think_to_add' => 'ניתן להוסיף הזנות חדשות.', + 'timeout' => 'Timeout in seconds', //TODO 'title' => 'כותרת', 'title_add' => 'הוספת הזנה', 'ttl' => 'אין לרענן אוטומטית יותר מ', diff --git a/app/i18n/it/sub.php b/app/i18n/it/sub.php index 698e64481..22d58a27f 100644 --- a/app/i18n/it/sub.php +++ b/app/i18n/it/sub.php @@ -44,8 +44,10 @@ return array( 'main_stream' => 'Mostra in homepage', // TODO 'normal' => 'Show in its category', // TODO ), + 'ssl_verify' => 'Verify SSL security', //TODO 'stats' => 'Statistiche', 'think_to_add' => 'Aggiungi feed.', + 'timeout' => 'Timeout in seconds', //TODO 'title' => 'Titolo', 'title_add' => 'Aggiungi RSS feed', 'ttl' => 'Non aggiornare automaticamente piu di', diff --git a/app/i18n/kr/sub.php b/app/i18n/kr/sub.php index e11d4588f..464b64f70 100644 --- a/app/i18n/kr/sub.php +++ b/app/i18n/kr/sub.php @@ -44,8 +44,10 @@ return array( 'main_stream' => '메인 스트림에 표시하기', 'normal' => '피드가 속한 카테고리에만 표시하기', ), + 'ssl_verify' => 'Verify SSL security', //TODO 'stats' => '통계', 'think_to_add' => '피드를 추가할 수 있습니다.', + 'timeout' => 'Timeout in seconds', //TODO 'title' => '제목', 'title_add' => 'RSS 피드 추가', 'ttl' => '다음 시간이 지나기 전에 새로고침 금지', diff --git a/app/i18n/nl/sub.php b/app/i18n/nl/sub.php index 6b1ac268b..067e226aa 100644 --- a/app/i18n/nl/sub.php +++ b/app/i18n/nl/sub.php @@ -45,8 +45,10 @@ return array( 'normal' => 'Show in its category', // TODO ), 'pubsubhubbub' => 'Directe notificaties met PubSubHubbub', + 'ssl_verify' => 'Verify SSL security', //TODO 'stats' => 'Statistieken', 'think_to_add' => 'Voeg wat feeds toe.', + 'timeout' => 'Timeout in seconds', //TODO 'title' => 'Titel', 'title_add' => 'Voeg een RSS feed toe', 'ttl' => 'Vernieuw automatisch niet vaker dan', diff --git a/app/i18n/pt-br/sub.php b/app/i18n/pt-br/sub.php index 09dde718f..1b084f08f 100644 --- a/app/i18n/pt-br/sub.php +++ b/app/i18n/pt-br/sub.php @@ -44,8 +44,10 @@ return array( 'main_stream' => 'Mostrar na tela principal', 'normal' => 'Show in its category', // TODO ), + 'ssl_verify' => 'Verify SSL security', //TODO 'stats' => 'Estatísticas', 'think_to_add' => 'Você deve adicionar alguns feeds.', + 'timeout' => 'Timeout in seconds', //TODO 'title' => 'Título', 'title_add' => 'Adicionar o RSS feed', 'ttl' => 'Não atualize automáticamente mais que', diff --git a/app/i18n/ru/sub.php b/app/i18n/ru/sub.php index 9e360630a..bef49623f 100644 --- a/app/i18n/ru/sub.php +++ b/app/i18n/ru/sub.php @@ -44,8 +44,10 @@ return array( 'main_stream' => 'Show in main stream', // TODO 'normal' => 'Show in its category', // TODO ), + 'ssl_verify' => 'Verify SSL security', //TODO 'stats' => 'Statistics',// TODO 'think_to_add' => 'You may add some feeds.',// TODO + 'timeout' => 'Timeout in seconds', //TODO 'title' => 'Title',// TODO 'title_add' => 'Add a RSS feed',// TODO 'ttl' => 'Do not automatically refresh more often than',// TODO diff --git a/app/i18n/tr/sub.php b/app/i18n/tr/sub.php index 871731158..e8cd15d0d 100644 --- a/app/i18n/tr/sub.php +++ b/app/i18n/tr/sub.php @@ -44,8 +44,10 @@ return array( 'main_stream' => 'Ana akışda göster', 'normal' => 'Show in its category', // TODO ), + 'ssl_verify' => 'Verify SSL security', //TODO 'stats' => 'İstatistikler', 'think_to_add' => 'Akış ekleyebilirsiniz.', + 'timeout' => 'Timeout in seconds', //TODO 'title' => 'Başlık', 'title_add' => 'RSS akışı ekle', 'ttl' => 'Şu kadar süreden fazla otomatik yenileme yapma', diff --git a/app/i18n/zh-cn/sub.php b/app/i18n/zh-cn/sub.php index bf73f82c4..034f8a9d9 100644 --- a/app/i18n/zh-cn/sub.php +++ b/app/i18n/zh-cn/sub.php @@ -44,8 +44,10 @@ return array( 'main_stream' => '在首页中显示', 'normal' => '在分类中显示', ), + 'ssl_verify' => 'Verify SSL security', //TODO 'stats' => '统计', 'think_to_add' => '你可以添加一些 RSS 源。', + 'timeout' => 'Timeout in seconds', //TODO 'title' => '标题', 'title_add' => '添加 RSS 源', 'ttl' => '最小自动更新时间', diff --git a/app/views/helpers/feed/update.phtml b/app/views/helpers/feed/update.phtml index d379c5df8..01c90369c 100644 --- a/app/views/helpers/feed/update.phtml +++ b/app/views/helpers/feed/update.phtml @@ -178,6 +178,53 @@ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+
diff --git a/lib/Minz/ModelPdo.php b/lib/Minz/ModelPdo.php index d769e0ff4..6928a2857 100644 --- a/lib/Minz/ModelPdo.php +++ b/lib/Minz/ModelPdo.php @@ -69,7 +69,7 @@ class Minz_ModelPdo { case 'sqlite': $string = 'sqlite:' . join_path(DATA_PATH, 'users', $currentUser, 'db.sqlite'); $this->prefix = ''; - $this->bd = new MinzPDOMSQLite($string, $db['user'], $db['password'], $driver_options); + $this->bd = new MinzPDOSQLite($string, $db['user'], $db['password'], $driver_options); $this->bd->exec('PRAGMA foreign_keys = ON;'); break; case 'pgsql': @@ -160,7 +160,7 @@ class MinzPDOMySql extends MinzPDO { } } -class MinzPDOMSQLite extends MinzPDO { +class MinzPDOSQLite extends MinzPDO { public function lastInsertId($name = null) { return parent::lastInsertId(); //We discard the name, only used by PostgreSQL } diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index a43509ded..e21697e42 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -39,6 +39,19 @@ class Minz_Request { return $default; } } + public static function paramTernary($key) { + if (isset(self::$params[$key])) { + $p = self::$params[$key]; + $tp = trim($p); + if ($p === null || $tp === '' || $tp === 'null') { + return null; + } elseif ($p == false || $tp == '0' || $tp === 'false' || $tp === 'no') { + return false; + } + return true; + } + return null; + } public static function defaultControllerName() { return self::$default_controller_name; } diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 215c4c362..9dfca385d 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -175,7 +175,7 @@ function html_only_entity_decode($text) { return strtr($text, $htmlEntitiesOnly); } -function customSimplePie() { +function customSimplePie($attributes = array()) { $system_conf = Minz_Configuration::get('system'); $limits = $system_conf->limits; $simplePie = new SimplePie(); @@ -183,8 +183,17 @@ function customSimplePie() { $simplePie->set_syslog($system_conf->simplepie_syslog_enabled); $simplePie->set_cache_location(CACHE_PATH); $simplePie->set_cache_duration($limits['cache_duration']); - $simplePie->set_timeout($limits['timeout']); - $simplePie->set_curl_options($system_conf->curl_options); + + $feed_timeout = empty($attributes['timeout']) ? 0 : intval($attributes['timeout']); + $simplePie->set_timeout($feed_timeout > 0 ? $feed_timeout : $limits['timeout']); + + $curl_options = $system_conf->curl_options; + if (isset($attributes['ssl_verify'])) { + $curl_options[CURLOPT_SSL_VERIFYHOST] = $attributes['ssl_verify'] ? 2 : 0; + $curl_options[CURLOPT_SSL_VERIFYPEER] = $attributes['ssl_verify'] ? true : false; + } + $simplePie->set_curl_options($curl_options); + $simplePie->strip_htmltags(array( 'base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', -- cgit v1.2.3 From e1b30f0da8fc1ae593fc2ab661869bde4c9959a4 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 1 May 2018 17:09:11 +0200 Subject: Changelog 1838 https://github.com/FreshRSS/FreshRSS/pull/1838 https://github.com/FreshRSS/FreshRSS/issues/891 https://github.com/FreshRSS/FreshRSS/issues/1702 https://github.com/FreshRSS/FreshRSS/issues/1811 --- CHANGELOG.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38292e83f..3d52363bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,14 @@ # FreshRSS changelog -## 2018-0X-XX FreshRSS 1.10.3-dev +## 2018-05-XX FreshRSS 1.10.3-dev * Features - * Light Boolean search implementation [879](https://github.com/FreshRSS/FreshRSS/issues/879) + * Several per-feed options (implemented in JSON) [#1838](https://github.com/FreshRSS/FreshRSS/pull/1838) + * Mark updated articles as read [#891](https://github.com/FreshRSS/FreshRSS/issues/891) + * Mark as read upon reception [#1702](https://github.com/FreshRSS/FreshRSS/issues/1702) + * Feed cURL timeout + * Ignore SSL (unsafe) [#1811](https://github.com/FreshRSS/FreshRSS/issues/1811) + * Light Boolean search implementation [#879](https://github.com/FreshRSS/FreshRSS/issues/879) * All parts are implicitly `AND` (which must not be written), except if `OR` is stated. * No use of parentheses. Support for quotes to disable the Boolean search, like `"This or that"`. * Example: `Hello intitle:World OR date:P1D example OR author:Else intitle:"This or that"` -- cgit v1.2.3 From 0513ba8d9f431cac17152f9366f8433906a7846f Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 1 May 2018 21:50:45 +0200 Subject: Use HTML autocomplete new-password (#1877) * Use HTML autocomplete new-password https://github.com/FreshRSS/FreshRSS/issues/1841#issuecomment-376551901 autocomplete="new-password" for user management pages https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion * autocomplete username https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-autocomplete-username --- app/install.php | 2 +- app/views/auth/formLogin.phtml | 2 +- app/views/auth/register.phtml | 2 +- app/views/feed/add.phtml | 2 +- app/views/helpers/feed/update.phtml | 2 +- app/views/subscription/index.phtml | 2 +- app/views/user/manage.phtml | 2 +- app/views/user/profile.phtml | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/install.php b/app/install.php index 870c93908..c30f8d583 100644 --- a/app/install.php +++ b/app/install.php @@ -537,7 +537,7 @@ function printStep2() {
- +
diff --git a/app/views/auth/formLogin.phtml b/app/views/auth/formLogin.phtml index 99be6059c..01d1d4736 100644 --- a/app/views/auth/formLogin.phtml +++ b/app/views/auth/formLogin.phtml @@ -9,7 +9,7 @@
- +
diff --git a/app/views/auth/register.phtml b/app/views/auth/register.phtml index 23bda25ce..19e11ef76 100644 --- a/app/views/auth/register.phtml +++ b/app/views/auth/register.phtml @@ -11,7 +11,7 @@
- +
diff --git a/app/views/feed/add.phtml b/app/views/feed/add.phtml index 5cd59d298..823cf1b2f 100644 --- a/app/views/feed/add.phtml +++ b/app/views/feed/add.phtml @@ -73,7 +73,7 @@
- +
diff --git a/app/views/helpers/feed/update.phtml b/app/views/helpers/feed/update.phtml index 01c90369c..88a38ea58 100644 --- a/app/views/helpers/feed/update.phtml +++ b/app/views/helpers/feed/update.phtml @@ -158,7 +158,7 @@
- +
diff --git a/app/views/subscription/index.phtml b/app/views/subscription/index.phtml index 26af0bd7c..41dd8a7df 100644 --- a/app/views/subscription/index.phtml +++ b/app/views/subscription/index.phtml @@ -40,7 +40,7 @@
  • - +
  • diff --git a/app/views/user/manage.phtml b/app/views/user/manage.phtml index 26838fcc1..9d457f7a5 100644 --- a/app/views/user/manage.phtml +++ b/app/views/user/manage.phtml @@ -66,7 +66,7 @@
    - /> + />
    diff --git a/app/views/user/profile.phtml b/app/views/user/profile.phtml index 7a63c0941..83140376d 100644 --- a/app/views/user/profile.phtml +++ b/app/views/user/profile.phtml @@ -22,7 +22,7 @@
    - /> + />
    @@ -35,7 +35,7 @@
    - /> + />
    -- cgit v1.2.3 From bde96ec8ea32d153d1d3d6263299f6b073f34f9e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 1 May 2018 21:57:30 +0200 Subject: Changelog 1877 https://github.com/FreshRSS/FreshRSS/pull/1877 https://github.com/FreshRSS/FreshRSS/issues/1841#issuecomment-376551901 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d52363bf..660845ec2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ * Fix muted feeds that were not actually muted [#1844](https://github.com/FreshRSS/FreshRSS/issues/1844) * Fix errors in case of empty/wrong username when updating user settings [#1857](https://github.com/FreshRSS/FreshRSS/pull/1857) * Fixes in subscription menu [#1858](https://github.com/FreshRSS/FreshRSS/pull/1858) +* Security + * Use `autocomplete="new-password"` to prevent form autocomplete in user management pages (fix bug with e.g. Firefox) [#1877](https://github.com/FreshRSS/FreshRSS/pull/1877) * UI * Add tooltips on user queries [#1823](https://github.com/FreshRSS/FreshRSS/pull/1823) * I18n -- cgit v1.2.3 From 0ccda74571fd314864ce0223ed1efaae26a11ca8 Mon Sep 17 00:00:00 2001 From: Upils Date: Wed, 9 May 2018 09:57:46 +0200 Subject: Add a docker compose example. (#1882) * Add a docker compose example. Using postgresql and with traefik specific labels. * Added docker-compose specific documentation. * Move docker-compose section at the end of the README.md. --- Docker/README.md | 17 ++++++++++++++++- Docker/docker-compose.yml | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 Docker/docker-compose.yml diff --git a/Docker/README.md b/Docker/README.md index 1767ae85d..c333329d4 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -75,7 +75,7 @@ sudo docker run -d --restart unless-stopped --log-opt max-size=10m \ ```sh # Rebuild an image (see build section above) or get a new online version: sudo docker pull freshrss/freshrss -# And then +# And then sudo docker stop freshrss sudo docker rename freshrss freshrss_old # See the run section above for the full command @@ -155,3 +155,18 @@ ls /var/www/FreshRSS/ Use a reverse proxy on your host server, such as [Træfik](https://traefik.io/) or [nginx](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/), with HTTPS, for instance using [Let’s Encrypt](https://letsencrypt.org/). + +### Example with [docker-compose](https://docs.docker.com/compose/) + +A `docker-compose.yml` file is given as an example, using PostgreSQL. In order to use it, you have to adapt: +- In the `postgresql` service: + * the `volumes` section; + * the `POSTGRES_PASSWORD` in the `environment` section; +- In the `freshrss` service: + * the `volumes` section; + * options under the `labels` section are specific to [Træfik](https://traefik.io/), a reverse proxy. If you are not using it, feel free to delete this section. If you are using it, adapt accordingly to your config, especially the `traefik.frontend.rule` option. + +You can then launch the stack (postgres + freshrss) with: +```sh +docker-compose up -d +``` diff --git a/Docker/docker-compose.yml b/Docker/docker-compose.yml new file mode 100644 index 000000000..a57f214da --- /dev/null +++ b/Docker/docker-compose.yml @@ -0,0 +1,36 @@ +version: '2.3' + +services: + postgresql: + image: postgres:latest + restart: unless-stopped + volumes: + - '/path/to/pgsql-data:/var/lib/postgresql/data:Z' + environment: + - POSTGRES_USER=freshrss + - POSTGRES_PASSWORD=password + - POSTGRES_DB=freshrss + + freshrss: + image: freshrss/freshrss:latest + restart: unless-stopped + depends_on: + - postgresql + networks: + - web + - default + volumes: + - '/your/local/directory/data:/var/www/FreshRSS/data:Z' + labels: + - "traefik.backend=freshrss" + - "traefik.docker.network=web" + - "traefik.frontend.rule=Host:rss.example.com" + - "traefik.enable=true" + - "traefik.default.protocol=http" + - "traefik.frontend.entryPoints=http,https" + - "traefik.port=80" + +networks: + web: + external: true + -- cgit v1.2.3 From a8ffde626313b01b80bd688345d2e005b3371b36 Mon Sep 17 00:00:00 2001 From: ColonelMoutarde <4697568+ColonelMoutarde@users.noreply.github.com> Date: Sat, 19 May 2018 13:16:51 +0200 Subject: add visibility "public" (#1849) --- app/Models/Feed.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 04101c10d..5ed9b1834 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -395,11 +395,11 @@ class FreshRSS_Feed extends Minz_Model { $this->entries = $entries; } - function cacheModifiedTime() { + public function cacheModifiedTime() { return @filemtime(CACHE_PATH . '/' . md5($this->url) . '.spc'); } - function lock() { + public function lock() { $this->lockPath = TMP_PATH . '/' . $this->hash() . '.freshrss.lock'; if (file_exists($this->lockPath) && ((time() - @filemtime($this->lockPath)) > 3600)) { @unlink($this->lockPath); @@ -412,13 +412,13 @@ class FreshRSS_Feed extends Minz_Model { return true; } - function unlock() { + public function unlock() { @unlink($this->lockPath); } // - function pubSubHubbubEnabled() { + public function pubSubHubbubEnabled() { $url = $this->selfUrl ? $this->selfUrl : $this->url; $hubFilename = PSHB_PATH . '/feeds/' . base64url_encode($url) . '/!hub.json'; if ($hubFile = @file_get_contents($hubFilename)) { @@ -431,7 +431,7 @@ class FreshRSS_Feed extends Minz_Model { return false; } - function pubSubHubbubError($error = true) { + public function pubSubHubbubError($error = true) { $url = $this->selfUrl ? $this->selfUrl : $this->url; $hubFilename = PSHB_PATH . '/feeds/' . base64url_encode($url) . '/!hub.json'; $hubFile = @file_get_contents($hubFilename); @@ -444,7 +444,7 @@ class FreshRSS_Feed extends Minz_Model { return false; } - function pubSubHubbubPrepare() { + public function pubSubHubbubPrepare() { $key = ''; if (FreshRSS_Context::$system_conf->base_url && $this->hubUrl && $this->selfUrl && @is_dir(PSHB_PATH)) { $path = PSHB_PATH . '/feeds/' . base64url_encode($this->selfUrl); @@ -491,7 +491,7 @@ class FreshRSS_Feed extends Minz_Model { } //Parameter true to subscribe, false to unsubscribe. - function pubSubHubbubSubscribe($state) { + public function pubSubHubbubSubscribe($state) { $url = $this->selfUrl ? $this->selfUrl : $this->url; if (FreshRSS_Context::$system_conf->base_url && $url) { $hubFilename = PSHB_PATH . '/feeds/' . base64url_encode($url) . '/!hub.json'; -- cgit v1.2.3 From 967dc041a7da1f4ed42cd1b32719641bcbea3e0d Mon Sep 17 00:00:00 2001 From: Frans de Jonge Date: Sun, 20 May 2018 11:57:46 +0200 Subject: [i18n] Add hint about adding #force_feed to URL (#1879) Fixes #1878 --- app/i18n/cz/feedback.php | 2 +- app/i18n/de/feedback.php | 2 +- app/i18n/en/feedback.php | 2 +- app/i18n/es/feedback.php | 2 +- app/i18n/fr/feedback.php | 2 +- app/i18n/he/feedback.php | 2 +- app/i18n/it/feedback.php | 2 +- app/i18n/kr/feedback.php | 2 +- app/i18n/nl/feedback.php | 6 +++--- app/i18n/pt-br/feedback.php | 2 +- app/i18n/ru/feedback.php | 2 +- app/i18n/tr/feedback.php | 2 +- app/i18n/zh-cn/feedback.php | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/i18n/cz/feedback.php b/app/i18n/cz/feedback.php index 22eaf77f7..693efcb8e 100644 --- a/app/i18n/cz/feedback.php +++ b/app/i18n/cz/feedback.php @@ -72,7 +72,7 @@ return array( 'already_subscribed' => 'Již jste přihlášen k odběru %s', 'deleted' => 'Kanál byl smazán', 'error' => 'Kanál nelze aktualizovat', - 'internal_problem' => 'RSS kanál nelze přidat. Pro detaily zkontrolujte logy FressRSS.', + 'internal_problem' => 'RSS kanál nelze přidat. Pro detaily zkontrolujte logy FressRSS.', // @todo 'invalid_url' => 'URL %s není platné', 'marked_read' => 'Kanály byly označeny jako přečtené', 'n_actualized' => '%d kanálů bylo aktualizováno', diff --git a/app/i18n/de/feedback.php b/app/i18n/de/feedback.php index 6348bca32..75e92538a 100644 --- a/app/i18n/de/feedback.php +++ b/app/i18n/de/feedback.php @@ -72,7 +72,7 @@ return array( 'already_subscribed' => 'Sie haben %s bereits abonniert', 'deleted' => 'Der Feed ist gelöscht worden', 'error' => 'Der Feed kann nicht aktualisiert werden', - 'internal_problem' => 'Der RSS-Feed konnte nicht hinzugefügt werden. Für Details prüfen Sie die FressRSS-Protokolle.', + 'internal_problem' => 'Der RSS-Feed konnte nicht hinzugefügt werden. Für Details prüfen Sie die FressRSS-Protokolle.', // @todo 'invalid_url' => 'Die URL %s ist ungültig', 'marked_read' => 'Die Feeds sind als gelesen markiert worden', 'n_actualized' => 'Die %d Feeds sind aktualisiert worden', diff --git a/app/i18n/en/feedback.php b/app/i18n/en/feedback.php index ad7f87fd9..79fa2c347 100644 --- a/app/i18n/en/feedback.php +++ b/app/i18n/en/feedback.php @@ -72,7 +72,7 @@ return array( 'already_subscribed' => 'You have already subscribed to %s', 'deleted' => 'Feed has been deleted', 'error' => 'Feed cannot be updated', - 'internal_problem' => 'The RSS feed could not be added. Check FressRSS logs for details.', + 'internal_problem' => 'The newsfeed could not be added. Check FressRSS logs for details. You can try force adding by appending #force_feed to the URL.', 'invalid_url' => 'URL %s is invalid', 'marked_read' => 'Feeds have been marked as read', 'n_actualized' => '%d feeds have been updated', diff --git a/app/i18n/es/feedback.php b/app/i18n/es/feedback.php index 7b23f1a8d..ce5d5e75f 100755 --- a/app/i18n/es/feedback.php +++ b/app/i18n/es/feedback.php @@ -72,7 +72,7 @@ return array( 'already_subscribed' => 'Ya estás suscrito a %s', 'deleted' => 'Fuente eliminada', 'error' => 'No es posible actualizar la fuente', - 'internal_problem' => 'No ha sido posible agregar la fuente RSS. Revisa el registro de FressRSS para más información.', + 'internal_problem' => 'No ha sido posible agregar la fuente RSS. Revisa el registro de FressRSS para más información.', // @todo 'invalid_url' => 'La URL %s es inválida', 'marked_read' => 'Fuentes marcadas como leídas', 'n_actualized' => 'Se han actualiado %d fuentes', diff --git a/app/i18n/fr/feedback.php b/app/i18n/fr/feedback.php index 1abf1b518..2443ad30a 100644 --- a/app/i18n/fr/feedback.php +++ b/app/i18n/fr/feedback.php @@ -72,7 +72,7 @@ return array( 'already_subscribed' => 'Vous êtes déjà abonné à %s', 'deleted' => 'Le flux a été supprimé.', 'error' => 'Une erreur est survenue', - 'internal_problem' => 'Le flux ne peut pas être ajouté. Consulter les logs de FreshRSS pour plus de détails.', + 'internal_problem' => 'Le flux ne peut pas être ajouté. Consulter les logs de FreshRSS pour plus de détails. Vous pouvez essayer de forcer l’ajout par addition de #force_feed à l’URL.', 'invalid_url' => 'L’url %s est invalide.', 'marked_read' => 'Les flux ont été marqués comme lus.', 'n_actualized' => '%d flux ont été mis à jour.', diff --git a/app/i18n/he/feedback.php b/app/i18n/he/feedback.php index f773ff270..f86b37244 100644 --- a/app/i18n/he/feedback.php +++ b/app/i18n/he/feedback.php @@ -73,7 +73,7 @@ return array( 'already_subscribed' => 'אתה כבר רשום ל %s', 'deleted' => 'ההזנה נמחקה', 'error' => 'Feed cannot be updated', // @todo - 'internal_problem' => 'אין אפשרות להוסיף את ההזנה. בדקו את הלוגים לפרטים.', + 'internal_problem' => 'אין אפשרות להוסיף את ההזנה. בדקו את הלוגים לפרטים.', // @todo 'invalid_url' => 'URL %s אינו תקין', 'marked_read' => 'הזנות סומנו כנקראו', 'n_actualized' => '%d הזנות עודכנו', diff --git a/app/i18n/it/feedback.php b/app/i18n/it/feedback.php index d702eb448..934666aa5 100644 --- a/app/i18n/it/feedback.php +++ b/app/i18n/it/feedback.php @@ -72,7 +72,7 @@ return array( 'already_subscribed' => 'Hai già sottoscritto %s', 'deleted' => 'Feed cancellato', 'error' => 'Feed non aggiornato', - 'internal_problem' => 'RSS feed non aggiunto. Verifica i logs per dettagli.', + 'internal_problem' => 'RSS feed non aggiunto. Verifica i logs per dettagli.', // @todo 'invalid_url' => 'URL %s non valido', 'marked_read' => 'Feeds segnati come letti', 'n_actualized' => '%d feeds aggiornati', diff --git a/app/i18n/kr/feedback.php b/app/i18n/kr/feedback.php index bccf1aac0..4fd7c899f 100644 --- a/app/i18n/kr/feedback.php +++ b/app/i18n/kr/feedback.php @@ -72,7 +72,7 @@ return array( 'already_subscribed' => '이미 %s 피드를 구독 중입니다', 'deleted' => '피드가 삭제되었습니다', 'error' => '피드를 변경할 수 없습니다', - 'internal_problem' => 'RSS 피드를 추가할 수 없습니다. 자세한 내용은 FressRSS 로그를 참고하세요.', + 'internal_problem' => 'RSS 피드를 추가할 수 없습니다. 자세한 내용은 FressRSS 로그를 참고하세요.', // @todo 'invalid_url' => 'URL (%s)이 유효하지 않습니다', 'marked_read' => '피드가 읽음으로 표시되었습니다', 'n_actualized' => '%d 개의 피드에서 새 글을 가져왔습니다', diff --git a/app/i18n/nl/feedback.php b/app/i18n/nl/feedback.php index 3fabc97b8..54636ab4b 100644 --- a/app/i18n/nl/feedback.php +++ b/app/i18n/nl/feedback.php @@ -72,7 +72,7 @@ return array( 'already_subscribed' => 'U bent al geabonneerd op %s', 'deleted' => 'Feed is verwijderd', 'error' => 'Feed kan niet worden vernieuwd', - 'internal_problem' => 'De RSS feed kon niet worden toegevoegd. Controleer FressRSS log bestanden voor details.', + 'internal_problem' => 'De feed kon niet worden toegevoegd. Controleer de FressRSS-logbestanden voor details. Toevoegen forceren kan worden geprobeerd door #force_feed aan de URL toe te voegen.', 'invalid_url' => 'URL %s is ongeldig', 'marked_read' => 'Feeds zijn gemarkeerd als gelezen', 'n_actualized' => '%d feeds zijn vernieuwd', @@ -102,8 +102,8 @@ return array( 'error' => 'Gebruiker %s kan niet worden verwijderd', ), 'updated' => array( - '_' => 'User %s has been updated', // TODO - 'error' => 'User %s has not been updated', // TODO + '_' => 'Gebruiker %s is bijgewerkt', + 'error' => 'Gebruiker %s kan niet worden bijgewerkt', ), 'set_registration' => 'Het maximale aantal accounts is vernieuwd.', ), diff --git a/app/i18n/pt-br/feedback.php b/app/i18n/pt-br/feedback.php index 932bb72b1..0e115f2cf 100644 --- a/app/i18n/pt-br/feedback.php +++ b/app/i18n/pt-br/feedback.php @@ -72,7 +72,7 @@ return array( 'already_subscribed' => 'Você já está inscrito no %s', 'deleted' => 'o Feed foi deletado', 'error' => 'O feed não pode ser atualizado', - 'internal_problem' => 'O RSS feed não pôde ser adicionado. Verifique os FressRSS logs para detalhes.', + 'internal_problem' => 'O RSS feed não pôde ser adicionado. Verifique os FressRSS logs para detalhes.', // @todo 'invalid_url' => 'URL %s é inválida', 'marked_read' => 'Feeds foram marcados como lidos', 'n_actualized' => '%d feeds foram atualizados', diff --git a/app/i18n/ru/feedback.php b/app/i18n/ru/feedback.php index 9416ed878..ad4ff958f 100644 --- a/app/i18n/ru/feedback.php +++ b/app/i18n/ru/feedback.php @@ -72,7 +72,7 @@ return array( 'already_subscribed' => 'You have already subscribed to %s', //TODO 'deleted' => 'Feed has been deleted', //TODO 'error' => 'Feed cannot be updated', //TODO - 'internal_problem' => 'The RSS feed could not be added. Check FressRSS logs for details.', //TODO + 'internal_problem' => 'The newsfeed could not be added. Check FressRSS logs for details. You can try force adding by appending #force_feed to the URL.', //TODO 'invalid_url' => 'URL %s is invalid', //TODO 'marked_read' => 'Feeds have been marked as read', //TODO 'n_actualized' => '%d feeds have been updated', //TODO diff --git a/app/i18n/tr/feedback.php b/app/i18n/tr/feedback.php index 8f40e7d85..7e9fdd229 100644 --- a/app/i18n/tr/feedback.php +++ b/app/i18n/tr/feedback.php @@ -72,7 +72,7 @@ return array( 'already_subscribed' => '%s için zaten aboneliğiniz bulunmakta', 'deleted' => 'Akış silindi', 'error' => 'Akış güncellenemiyor', - 'internal_problem' => 'RSS akışı eklenemiyor. Detaylar için FressRSS log kayıtlarını kontrol edin.', + 'internal_problem' => 'RSS akışı eklenemiyor. Detaylar için FressRSS log kayıtlarını kontrol edin.', // @todo 'invalid_url' => 'URL %s geçersiz', 'marked_read' => 'Akışlar okundu olarak işaretlendi', 'n_actualized' => '%d akışları güncellendi', diff --git a/app/i18n/zh-cn/feedback.php b/app/i18n/zh-cn/feedback.php index a005de0ce..0021308bd 100644 --- a/app/i18n/zh-cn/feedback.php +++ b/app/i18n/zh-cn/feedback.php @@ -72,7 +72,7 @@ return array( 'already_subscribed' => '你已订阅 %s', 'deleted' => 'RSS 源已删除', 'error' => 'RSS 源更新失败', - 'internal_problem' => 'RSS 源添加失败。检查 FressRSS 日志 查看详情。', + 'internal_problem' => 'RSS 源添加失败。检查 FressRSS 日志 查看详情。', // @todo 'invalid_url' => 'URL %s 无效', 'marked_read' => 'RSS 源已被设为已读', 'n_actualized' => '%d 个 RSS 源已更新', -- cgit v1.2.3 From fd7b0c1e7854b00f222cad96c376ef8a7d547d7d Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 20 May 2018 12:04:12 +0200 Subject: Changelog 1882 https://github.com/FreshRSS/FreshRSS/pull/1882 --- CHANGELOG.md | 1 + Docker/README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 660845ec2..09f389205 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ * Example: `Hello intitle:World OR date:P1D example OR author:Else intitle:"This or that"` * Deployment * Includes an optional cron daemon in Docker to refresh feeds automatically [#1869](https://github.com/FreshRSS/FreshRSS/issues/1869) + * Docker Compose example [#1882](https://github.com/FreshRSS/FreshRSS/pull/1882) * Bug fixing * Fix Docker bug affecting Apache `CustomLog` (unwanted local copy of access logs), `ErrorLog`, `Listen` (IPv6 bug) [#1873](https://github.com/FreshRSS/FreshRSS/pull/1873) * Fix muted feeds that were not actually muted [#1844](https://github.com/FreshRSS/FreshRSS/issues/1844) diff --git a/Docker/README.md b/Docker/README.md index c333329d4..381adab38 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -158,7 +158,7 @@ with HTTPS, for instance using [Let’s Encrypt](https://letsencrypt.org/). ### Example with [docker-compose](https://docs.docker.com/compose/) -A `docker-compose.yml` file is given as an example, using PostgreSQL. In order to use it, you have to adapt: +A [docker-compose.yml](docker-compose.yml) file is given as an example, using PostgreSQL. In order to use it, you have to adapt: - In the `postgresql` service: * the `volumes` section; * the `POSTGRES_PASSWORD` in the `environment` section; -- cgit v1.2.3 From 6881d362b1ff4e153580ee7bf5cdf80a4428516d Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 20 May 2018 12:17:08 +0200 Subject: Detection of non-unique GUIDs (#1887) * Detection of non-unique GUIDs Some feeds are using GUIDs, but fail to make them unique. Example: https://www.kbh-sprogcenter.dk/en/category/danish-break/feed/ This patch detects non-unique GUIDs, and disable GUIDs in that specific case. * Add state and log --- app/Models/Feed.php | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 5ed9b1834..7eb079f15 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -335,6 +335,8 @@ class FreshRSS_Feed extends Minz_Model { public function loadEntries($feed) { $entries = array(); + $guids = array(); + $hasUniqueGuids = true; foreach ($feed->get_items() as $item) { $title = html_only_entity_decode(strip_tags($item->get_title())); @@ -375,9 +377,13 @@ class FreshRSS_Feed extends Minz_Model { } } + $guid = $item->get_id(false, false); + $hasUniqueGuids &= empty($guids['_' . $guid]); + $guids['_' . $guid] = true; + $entry = new FreshRSS_Entry( $this->id(), - $item->get_id(false, false), + $guid, $title === null ? '' : $title, $author === null ? '' : html_only_entity_decode(strip_tags($author->name == null ? $author->email : $author->name)), $content === null ? '' : $content, @@ -392,6 +398,23 @@ class FreshRSS_Feed extends Minz_Model { unset($item); } + $hasBadGuids = $this->attributes('hasBadGuids'); + if ($hasBadGuids != !$hasUniqueGuids) { + $hasBadGuids = !$hasUniqueGuids; + if ($hasBadGuids) { + Minz_Log::warning('Feed has invalid GUIDs: ' . $this->url); + } else { + Minz_Log::warning('Feed has valid GUIDs again: ' . $this->url); + } + $feedDAO = FreshRSS_Factory::createFeedDao(); + $feedDAO->updateFeedAttribute($this, 'hasBadGuids', $hasBadGuids); + } + if (!$hasUniqueGuids) { + foreach ($entries as $entry) { + $entry->_guid(''); + } + } + $this->entries = $entries; } -- cgit v1.2.3 From 9e393445bb9ca2a0c05eb08ec8c30dc95396b1ab Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 20 May 2018 12:25:52 +0200 Subject: Changelog 1887 https://github.com/FreshRSS/FreshRSS/pull/1887 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09f389205..50fb5d5fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ * Fixes in subscription menu [#1858](https://github.com/FreshRSS/FreshRSS/pull/1858) * Security * Use `autocomplete="new-password"` to prevent form autocomplete in user management pages (fix bug with e.g. Firefox) [#1877](https://github.com/FreshRSS/FreshRSS/pull/1877) +* SimplePie + * Work-around for feeds with invalid non-unique GUIDs [#1887](https://github.com/FreshRSS/FreshRSS/pull/1887) * UI * Add tooltips on user queries [#1823](https://github.com/FreshRSS/FreshRSS/pull/1823) * I18n -- cgit v1.2.3 From 7e9a29b71936c701474c9fe79cb3f2cc4aafda4f Mon Sep 17 00:00:00 2001 From: David souza Date: Thu, 17 May 2018 11:09:10 -0300 Subject: add shebang to actualize script (#1886) * add shebang to actualize script * Update actualize_script.php --- app/actualize_script.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/actualize_script.php b/app/actualize_script.php index 6f48220a6..ba9660a14 100755 --- a/app/actualize_script.php +++ b/app/actualize_script.php @@ -1,3 +1,4 @@ +#!/usr/bin/php Date: Sun, 20 May 2018 23:23:48 +0200 Subject: Strip credentials from HTTP Referer (#1891) https://github.com/simplepie/simplepie/blob/0edf39018709c0d66cfa6d9b972907bb92461efb/library/SimplePie/File.php#L109 Candidate for pull-request upstream to SimplePie --- lib/SimplePie/SimplePie/File.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SimplePie/SimplePie/File.php b/lib/SimplePie/SimplePie/File.php index 8be38f145..b8a595571 100644 --- a/lib/SimplePie/SimplePie/File.php +++ b/lib/SimplePie/SimplePie/File.php @@ -110,7 +110,7 @@ class SimplePie_File curl_setopt($fp, CURLOPT_FAILONERROR, 1); curl_setopt($fp, CURLOPT_TIMEOUT, $timeout); curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout); - curl_setopt($fp, CURLOPT_REFERER, $url); + curl_setopt($fp, CURLOPT_REFERER, SimplePie_Misc::url_remove_credentials($url)); curl_setopt($fp, CURLOPT_USERAGENT, $useragent); curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2); if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>=')) -- cgit v1.2.3 From 8afce0753a9d96dd38ecfc7f15d63c0f4160671a Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 20 May 2018 23:27:16 +0200 Subject: Changelog 1891 https://github.com/FreshRSS/FreshRSS/pull/1891 https://framateam.org/freshrss/pl/q8mqryc4abbaixjxxditzrxgsh --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50fb5d5fd..a3821d66a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ * Fix errors in case of empty/wrong username when updating user settings [#1857](https://github.com/FreshRSS/FreshRSS/pull/1857) * Fixes in subscription menu [#1858](https://github.com/FreshRSS/FreshRSS/pull/1858) * Security + * Strip HTTP credentials from HTTP Referer in SimplePie [#1891](https://github.com/FreshRSS/FreshRSS/pull/1891) * Use `autocomplete="new-password"` to prevent form autocomplete in user management pages (fix bug with e.g. Firefox) [#1877](https://github.com/FreshRSS/FreshRSS/pull/1877) * SimplePie * Work-around for feeds with invalid non-unique GUIDs [#1887](https://github.com/FreshRSS/FreshRSS/pull/1887) -- cgit v1.2.3 From c96b5ff34a1c9f2bd1697980d967429e3cd7d220 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 22 May 2018 08:49:11 +0200 Subject: SimplePie fix for Atom feeds using namespace for type (#1893) https://github.com/FreshRSS/FreshRSS/issues/1892 --- lib/SimplePie/SimplePie/Misc.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/SimplePie/SimplePie/Misc.php b/lib/SimplePie/SimplePie/Misc.php index 40477c01e..1f338623c 100644 --- a/lib/SimplePie/SimplePie/Misc.php +++ b/lib/SimplePie/SimplePie/Misc.php @@ -1928,9 +1928,18 @@ class SimplePie_Misc public static function atom_10_content_construct_type($attribs) { + $type = ''; if (isset($attribs['']['type'])) { - $type = strtolower(trim($attribs['']['type'])); + $type = trim($attribs['']['type']); + } + elseif (isset($attribs[SIMPLEPIE_NAMESPACE_ATOM_10]['type'])) + {//FreshRSS + $type = trim($attribs[SIMPLEPIE_NAMESPACE_ATOM_10]['type']); + } + if ($type != '') + { + $type = strtolower($type); switch ($type) { case 'text': -- cgit v1.2.3 From 6c0daa03557ffcdd64a8e4ef99548b49e19aa93e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 22 May 2018 08:55:49 +0200 Subject: Changelog 1892 https://github.com/FreshRSS/FreshRSS/issues/1892 https://github.com/FreshRSS/FreshRSS/pull/1893 --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3821d66a..cc32e6163 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,11 +20,12 @@ * Fix muted feeds that were not actually muted [#1844](https://github.com/FreshRSS/FreshRSS/issues/1844) * Fix errors in case of empty/wrong username when updating user settings [#1857](https://github.com/FreshRSS/FreshRSS/pull/1857) * Fixes in subscription menu [#1858](https://github.com/FreshRSS/FreshRSS/pull/1858) +* SimplePie + * Work-around for feeds with invalid non-unique GUIDs [#1887](https://github.com/FreshRSS/FreshRSS/pull/1887) + * Fix for Atom feeds using a namespace for type [#1892](https://github.com/FreshRSS/FreshRSS/issues/1892) * Security * Strip HTTP credentials from HTTP Referer in SimplePie [#1891](https://github.com/FreshRSS/FreshRSS/pull/1891) * Use `autocomplete="new-password"` to prevent form autocomplete in user management pages (fix bug with e.g. Firefox) [#1877](https://github.com/FreshRSS/FreshRSS/pull/1877) -* SimplePie - * Work-around for feeds with invalid non-unique GUIDs [#1887](https://github.com/FreshRSS/FreshRSS/pull/1887) * UI * Add tooltips on user queries [#1823](https://github.com/FreshRSS/FreshRSS/pull/1823) * I18n -- cgit v1.2.3 From 8f1bad60d0b7bd0d0a05bcdcf3c6834e39c0c6eb Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Thu, 24 May 2018 21:53:47 +0200 Subject: Add Fever API and user documentation (#1836) * added fever api and documentation * spaces to tabs * fixed code format * added links * added utf8 to header * removed XML support * removed before check, as we have to convert it afterwards * added sandboxed setting (currently disabled) added support for extensions using entry_before_display * listFeedsOrderUpdate LIMIT https://github.com/FreshRSS/FreshRSS/pull/1836/files#r175287881 * removed custom sql by using FreshRSS_FeedDAO::listFeedsOrderUpdate() * fixed mark all as read * replaced custom sql for getUnread() and getStarred() with dao functions * removed sanitization functions * Rework fever login * Fix config bug Plus documentation * Fix array syntax For compatibility with PHP 5.3 * Disable cookies and session for API * Fix currentUser * added response header and error log * adjusted phpdoc to match new authentication * Mechanism to delete old keys * replace PHP_INT_MAX with zero to disable limit * replace method_exists with check for explicit methods * removed Press support and smaller refactoring + updated docu * Rewrite bindParamArray Avoid one of the SQL injection risks * Docs and readme * Fix API link * Simplify reverse key check Using userConfig --- README.fr.md | 49 ++- README.md | 49 ++- app/Controllers/userController.php | 19 ++ app/Models/EntryDAO.php | 2 +- app/Models/FeedDAO.php | 5 +- cli/prepare.php | 1 + config-user.default.php | 2 + data/fever/.gitignore | 1 + data/fever/index.html | 13 + docs/en/users/06_Fever_API.md | 110 +++++++ docs/fr/users/06_Fever_API.md | 17 + p/api/fever.php | 634 +++++++++++++++++++++++++++++++++++++ p/api/greader.php | 2 + p/api/index.php | 11 + 14 files changed, 888 insertions(+), 27 deletions(-) create mode 100644 data/fever/.gitignore create mode 100644 data/fever/index.html create mode 100644 docs/en/users/06_Fever_API.md create mode 100644 docs/fr/users/06_Fever_API.md create mode 100644 p/api/fever.php diff --git a/README.fr.md b/README.fr.md index 97ffafb56..265030546 100644 --- a/README.fr.md +++ b/README.fr.md @@ -1,3 +1,6 @@ +[![Build Status][travis-badge]][travis-link] + +* Lire ce document sur [github.com/FreshRSS/FreshRSS/](https://github.com/FreshRSS/FreshRSS/blob/master/README.md) pour avoir les images et liens corrects. * [English version](README.md) # FreshRSS @@ -54,6 +57,8 @@ Nous sommes une communauté amicale. 6. Des paramètres de configuration avancée peuvent être vues dans [config.default.php](config.default.php) et modifiées dans `data/config.php`. 7. Avec Apache, activer [`AllowEncodedSlashes`](https://httpd.apache.org/docs/trunk/mod/core.html#allowencodedslashes) pour une meilleure compatibilité avec les clients mobiles. +Plus d’informations sur l’installation et la configuration serveur peuvent être trouvées dans [notre documentation](https://freshrss.github.io/FreshRSS/fr/users/01_Installation.md). + ## Installation automatisée * [Docker](./Docker/) * [![Cloudron](https://cloudron.io/img/button.svg)](https://cloudron.io/button.html?app=org.freshrss.cloudronapp) @@ -107,6 +112,8 @@ sudo git pull sudo chown -R :www-data . && sudo chmod -R g+r . && sudo chmod -R g+w ./data/ ``` +Voir la [documentation de la ligne de commande](cli/README.md) pour plus de détails. + ## Contrôle d’accès Il est requis pour le mode multi-utilisateur, et recommandé dans tous les cas, de limiter l’accès à votre FreshRSS. Au choix : * En utilisant l’identification par formulaire (requiert JavaScript, et PHP 5.5+ recommandé) @@ -150,11 +157,42 @@ mysqldump --skip-comments --disable-keys --user= --password --host --password --host feverKey)) { + return @unlink(DATA_PATH . '/fever/.key-' . sha1(FreshRSS_Context::$system_conf->salt) . '-' . $userConfig->feverKey . '.txt'); + } + return false; + } + public static function updateUser($user, $passwordPlain, $apiPasswordPlain, $userConfigUpdated = array()) { $userConfig = get_user_configuration($user); if ($userConfig === null) { @@ -58,6 +66,16 @@ class FreshRSS_user_Controller extends Minz_ActionController { if ($apiPasswordPlain != '') { $apiPasswordHash = self::hashPassword($apiPasswordPlain); $userConfig->apiPasswordHash = $apiPasswordHash; + + @mkdir(DATA_PATH . '/fever/', 0770, true); + self::deleteFeverKey($user); + $userConfig->feverKey = strtolower(md5($user . ':' . $apiPasswordPlain)); + $ok = file_put_contents(DATA_PATH . '/fever/.key-' . sha1(FreshRSS_Context::$system_conf->salt) . '-' . $userConfig->feverKey . '.txt', $user) !== false; + + if (!$ok) { + Minz_Log::warning('Could not save API credentials for fever API', ADMIN_LOG); + return $ok; + } } if (is_array($userConfigUpdated)) { @@ -258,6 +276,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { $ok &= $userDAO->deleteUser($username); $ok &= recursive_unlink($user_data); array_map('unlink', glob(PSHB_PATH . '/feeds/*/' . $username . '.txt')); + self::deleteFeverKey(); } return $ok; } diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 516aad3b8..a3bca3727 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -801,7 +801,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { . 'WHERE ' . $where . $search . 'ORDER BY e.id ' . $order - . ($limit > 0 ? ' LIMIT ' . $limit : '')); //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ + . ($limit > 0 ? ' LIMIT ' . intval($limit) : '')); //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ } public function listWhereRaw($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filters = null, $date_min = 0) { diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index f968ae98b..9d980c139 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -299,13 +299,14 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { /** * Use $defaultCacheDuration == -1 to return all feeds, without filtering them by TTL. */ - public function listFeedsOrderUpdate($defaultCacheDuration = 3600) { + public function listFeedsOrderUpdate($defaultCacheDuration = 3600, $limit = 0) { $this->updateTTL(); $sql = 'SELECT id, url, name, website, `lastUpdate`, `pathEntries`, `httpAuth`, keep_history, ttl, attributes ' . 'FROM `' . $this->prefix . 'feed` ' . ($defaultCacheDuration < 0 ? '' : 'WHERE ttl >= ' . FreshRSS_Feed::TTL_DEFAULT . ' AND `lastUpdate` < (' . (time() + 60) . '-(CASE WHEN ttl=' . FreshRSS_Feed::TTL_DEFAULT . ' THEN ' . intval($defaultCacheDuration) . ' ELSE ttl END)) ') - . 'ORDER BY `lastUpdate`'; + . 'ORDER BY `lastUpdate` ' + . ($limit < 1 ? '' : 'LIMIT ' . intval($limit)); $stm = $this->bd->prepare($sql); if ($stm && $stm->execute()) { return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC)); diff --git a/cli/prepare.php b/cli/prepare.php index 2db2da555..81fb53f85 100755 --- a/cli/prepare.php +++ b/cli/prepare.php @@ -7,6 +7,7 @@ $dirs = array( '/cache', '/extensions-data', '/favicons', + '/fever', '/PubSubHubbub', '/PubSubHubbub/feeds', '/PubSubHubbub/keys', diff --git a/config-user.default.php b/config-user.default.php index 5e67d8d9b..6aef0dc49 100644 --- a/config-user.default.php +++ b/config-user.default.php @@ -9,6 +9,8 @@ return array ( 'token' => '', 'passwordHash' => '', 'apiPasswordHash' => '', + //feverKey is md5($user . ':' . $apiPasswordPlain) + 'feverKey' => '', 'posts_per_page' => 20, 'since_hours_posts_per_rss' => 168, 'min_posts_per_rss' => 2, diff --git a/data/fever/.gitignore b/data/fever/.gitignore new file mode 100644 index 000000000..2211df63d --- /dev/null +++ b/data/fever/.gitignore @@ -0,0 +1 @@ +*.txt diff --git a/data/fever/index.html b/data/fever/index.html new file mode 100644 index 000000000..85faaa37e --- /dev/null +++ b/data/fever/index.html @@ -0,0 +1,13 @@ + + + + + +Redirection + + + + +

    Redirection

    + + diff --git a/docs/en/users/06_Fever_API.md b/docs/en/users/06_Fever_API.md new file mode 100644 index 000000000..58e986a64 --- /dev/null +++ b/docs/en/users/06_Fever_API.md @@ -0,0 +1,110 @@ +# FreshRSS - Fever API implementation + +## RSS clients + +There are many RSS clients existing supporting Fever APIs but they seem to understand the Fever API a bit differently. +If your favourite client does not work properly with this API, create an issue and we will have a look. +But we can **only** do that for free clients. + +### Usage & Authentication + +Before you can start to use this API, you have to enable and setup API access, which is [documented here](https://freshrss.github.io/FreshRSS/en/users/06_Mobile_access.html), +and then re-set the user’s API password. + +Then point your mobile application to the URL of `fever.php` (e.g. `https://freshrss.example.net/api/fever.php`). + +## Compatibility + +Tested with: + +- iOS + - [Fiery Feeds](https://itunes.apple.com/app/fiery-feeds-rss-reader/id1158763303) + - [Unread](https://itunes.apple.com/app/unread-rss-reader/id1252376153) + +- MacOS + - [Readkit](https://itunes.apple.com/app/readkit/id588726889) + +- Android + -Until now, we don't know about compatible Android clients. Please leave your feedback, if you tested the Fever API with Android apps. + - Please note, that *Press* is NOT compatible: it was a popular RSS client with Fever support, but its development stopped a while ago. It uses the Fever API in a wrong way, which we don't support. + +## Features + +Following features are implemented: + +- fetching categories +- fetching feeds +- fetching RSS items (new, favorites, unread, by_id, by_feed, by_category, since) +- fetching favicons +- setting read marker for item(s) +- setting starred marker for item(s) +- setting read marker for feed +- setting read marker for category +- supports FreshRSS extensions, which use th `entry_before_display` hook + +Following features are not supported: +- **Hot Links** aka **hot** as there is nothing in FreshRSS yet that is similar or could be used to simulate it + +## Testing and error search + +If this API does not work as expected in your RSS reader, you can test it manually with a tool like [Postman](https://www.getpostman.com/). + +Configure a POST request to the URL https://freshrss.example.net/api/fever.php?api which should give you the result: +```json +{ + "api_version": 3, + "auth": 0 +} +``` +Great, the base setup seems to work! + +Now lets try an authenticated call. Fever uses an `api_key`, which is the MD5 hash of `"$username:$apiPassword"`. +Assuming the user is `kevin` and the password `freshrss`, here is a command-line example to compute the resulting `api_key` + +```sh +api_key=`echo -n "kevin:freshrss" | md5sum | cut -d' ' -f1` +``` + +Add a body to your POST request encoded as `form-data` and one key named `api_key` with the value `your-password-hash`: + +```sh +curl -s -F "api_key=$api_key" 'https://freshrss.example.net/api/fever.php?api' +``` + +This shoud give: +```json +{ + "api_version": 3, + "auth": 1, + "last_refreshed_on_time": "1520013061" +} +``` +Perfect, you are authenticated and can now start testing the more advanced features. Therefor change the URL and append the possible API actions to your request parameters. Check the [original Fever documentation](https://feedafever.com/api) for more infos. + +Some basic calls are: + +- https://freshrss.example.net/api/fever.php?api&items +- https://freshrss.example.net/api/fever.php?api&feeds +- https://freshrss.example.net/api/fever.php?api&groups +- https://freshrss.example.net/api/fever.php?api&unread_item_ids +- https://freshrss.example.net/api/fever.php?api&saved_item_ids +- https://freshrss.example.net/api/fever.php?api&items&since_id=some_id +- https://freshrss.example.net/api/fever.php?api&items&max_id=some_id +- https://freshrss.example.net/api/fever.php?api&mark=item&as=read&id=some_id +- https://freshrss.example.net/api/fever.php?api&mark=item&as=unread&id=some_id + +Replace `some_id` with a real ID from your `freshrss_username_entry` database. + +### Debugging + +If nothing helps and your clients still misbehaves, add these lines to the start of `fever.api`: + +```php +file_put_contents(__DIR__ . '/fever.log', $_SERVER['HTTP_USER_AGENT'] . ': ' . json_encode($_REQUEST) . PHP_EOL, FILE_APPEND); +``` + +Then use your RSS client to query the API and afterwards check the file `fever.log`. + +## Credits + +This plugin was inspired by the [tinytinyrss-fever-plugin](https://github.com/dasmurphy/tinytinyrss-fever-plugin). diff --git a/docs/fr/users/06_Fever_API.md b/docs/fr/users/06_Fever_API.md new file mode 100644 index 000000000..f9dcd5d73 --- /dev/null +++ b/docs/fr/users/06_Fever_API.md @@ -0,0 +1,17 @@ +# FreshRSS - API compatible Fever + + +## Compatibilité + +Testé avec: + +- iOS + - [Fiery Feeds](https://itunes.apple.com/app/fiery-feeds-rss-reader/id1158763303) + - [Unread](https://itunes.apple.com/app/unread-rss-reader/id1252376153) + +- MacOS + - [Readkit](https://itunes.apple.com/app/readkit/id588726889) + +## TODO + +Voir [la page en anglais](../../en/users/06_Fever_API.md). diff --git a/p/api/fever.php b/p/api/fever.php new file mode 100644 index 000000000..749116183 --- /dev/null +++ b/p/api/fever.php @@ -0,0 +1,634 @@ +api_enabled) { + Minz_Log::warning('serviceUnavailable() ' . debugInfo(), API_LOG); + header('HTTP/1.1 503 Service Unavailable'); + header('Content-Type: text/plain; charset=UTF-8'); + die('Service Unavailable!'); +} + +ini_set('session.use_cookies', '0'); +register_shutdown_function('session_destroy'); +Minz_Session::init('FreshRSS'); +// ================================================================================================ + + +class FeverAPI_EntryDAO extends FreshRSS_EntryDAO +{ + /** + * @return array + */ + public function countFever() + { + $values = array( + 'total' => 0, + 'min' => 0, + 'max' => 0, + ); + $sql = 'SELECT COUNT(id) as `total`, MIN(id) as `min`, MAX(id) as `max` FROM `' . $this->prefix . 'entry`'; + $stm = $this->bd->prepare($sql); + $stm->execute(); + $result = $stm->fetchAll(PDO::FETCH_ASSOC); + + if (!empty($result[0])) { + $values = $result[0]; + } + + return $values; + } + + /** + * @param string $prefix + * @param array $values + * @param array $bindArray + * @return string + */ + protected function bindParamArray($prefix, $values, &$bindArray) + { + $str = ''; + for ($i = 0; $i < count($values); $i++) { + $str .= ':' . $prefix . $i . ','; + $bindArray[$prefix . $i] = $values[$i]; + } + return rtrim($str, ','); + } + + /** + * @param array $feed_ids + * @param array $entry_ids + * @param int|null $max_id + * @param int|null $since_id + * @return FreshRSS_Entry[] + */ + public function findEntries(array $feed_ids, array $entry_ids, $max_id, $since_id) + { + $values = array(); + $order = ''; + + $sql = 'SELECT id, guid, title, author, ' + . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') + . ', link, date, is_read, is_favorite, id_feed, tags ' + . 'FROM `' . $this->prefix . 'entry` WHERE'; + + if (!empty($entry_ids)) { + $bindEntryIds = $this->bindParamArray("id", $entry_ids, $values); + $sql .= " id IN($bindEntryIds)"; + } else if (!empty($max_id)) { + $sql .= ' id < :id'; + $values[':id'] = $max_id; + $order = ' ORDER BY id DESC'; + } else { + $sql .= ' id > :id'; + $values[':id'] = $since_id; + $order = ' ORDER BY id ASC'; + } + + if (!empty($feed_ids)) { + $bindFeedIds = $this->bindParamArray("feed", $feed_ids, $values); + $sql .= " AND id_feed IN($bindFeedIds)"; + } + + $sql .= $order; + $sql .= ' LIMIT 50'; + + $stm = $this->bd->prepare($sql); + $stm->execute($values); + $result = $stm->fetchAll(PDO::FETCH_ASSOC); + + $entries = array(); + foreach ($result as $dao) { + $entries[] = self::daoToEntry($dao); + } + + return $entries; + } +} + +/** + * Class FeverAPI + */ +class FeverAPI +{ + const API_LEVEL = 3; + const STATUS_OK = 1; + const STATUS_ERR = 0; + + /** + * Authenticate the user + * + * API Password sent from client is the result of the md5 sum of + * your FreshRSS "username:your-api-password" combination + */ + private function authenticate() + { + FreshRSS_Context::$user_conf = null; + Minz_Session::_param('currentUser'); + $feverKey = empty($_POST['api_key']) ? '' : substr(trim($_POST['api_key']), 0, 128); + if (ctype_xdigit($feverKey)) { + $feverKey = strtolower($feverKey); + $username = @file_get_contents(DATA_PATH . '/fever/.key-' . sha1(FreshRSS_Context::$system_conf->salt) . '-' . $feverKey . '.txt', false); + if ($username != false) { + $username = trim($username); + $user_conf = get_user_configuration($username); + if ($user_conf != null && $feverKey === $user_conf->feverKey) { + FreshRSS_Context::$user_conf = $user_conf; + Minz_Session::_param('currentUser', $username); + } + } + } + } + + /** + * @return bool + */ + public function isAuthenticatedApiUser() + { + $this->authenticate(); + + if (FreshRSS_Context::$user_conf !== null) { + return true; + } + + return false; + } + + /** + * @return FreshRSS_FeedDAO + */ + protected function getDaoForFeeds() + { + return new FreshRSS_FeedDAO(); + } + + /** + * @return FreshRSS_CategoryDAO + */ + protected function getDaoForCategories() + { + return new FreshRSS_CategoryDAO(); + } + + /** + * @return FeverAPI_EntryDAO + */ + protected function getDaoForEntries() + { + return new FeverAPI_EntryDAO(); + } + + /** + * This does all the processing, since the fever api does not have a specific variable that specifies the operation + * + * @return array + * @throws Exception + */ + public function process() + { + $response_arr = array(); + + if (!$this->isAuthenticatedApiUser()) { + throw new Exception('No user given or user is not allowed to access API'); + } + + if (isset($_REQUEST["groups"])) { + $response_arr["groups"] = $this->getGroups(); + $response_arr["feeds_groups"] = $this->getFeedsGroup(); + } + + if (isset($_REQUEST["feeds"])) { + $response_arr["feeds"] = $this->getFeeds(); + $response_arr["feeds_groups"] = $this->getFeedsGroup(); + } + + if (isset($_REQUEST["favicons"])) { + $response_arr["favicons"] = $this->getFavicons(); + } + + if (isset($_REQUEST["items"])) { + $response_arr["total_items"] = $this->getTotalItems(); + $response_arr["items"] = $this->getItems(); + } + + if (isset($_REQUEST["links"])) { + $response_arr["links"] = $this->getLinks(); + } + + if (isset($_REQUEST["unread_item_ids"])) { + $response_arr["unread_item_ids"] = $this->getUnreadItemIds(); + } + + if (isset($_REQUEST["saved_item_ids"])) { + $response_arr["saved_item_ids"] = $this->getSavedItemIds(); + } + + if (isset($_REQUEST["mark"], $_REQUEST["as"], $_REQUEST["id"]) && is_numeric($_REQUEST["id"])) { + $method_name = "set" . ucfirst($_REQUEST["mark"]) . "As" . ucfirst($_REQUEST["as"]); + $allowedMethods = array( + 'setFeedAsRead', 'setGroupAsRead', 'setItemAsRead', + 'setItemAsSaved', 'setItemAsUnread', 'setItemAsUnsaved' + ); + if (in_array($method_name, $allowedMethods)) { + $id = intval($_REQUEST["id"]); + switch (strtolower($_REQUEST["mark"])) { + case 'item': + $this->{$method_name}($id); + break; + case 'feed': + case 'group': + $before = (isset($_REQUEST["before"])) ? $_REQUEST["before"] : null; + $this->{$method_name}($id, $before); + break; + } + + switch ($_REQUEST["as"]) { + case "read": + case "unread": + $response_arr["unread_item_ids"] = $this->getUnreadItemIds(); + break; + + case 'saved': + case 'unsaved': + $response_arr["saved_item_ids"] = $this->getSavedItemIds(); + break; + } + } + } + + return $response_arr; + } + + /** + * Returns the complete JSON, with 'api_version' and status as 'auth'. + * + * @param int $status + * @param array $reply + * @return string + */ + public function wrap($status, array $reply = array()) + { + $arr = array('api_version' => self::API_LEVEL, 'auth' => $status); + + if ($status === self::STATUS_OK) { + $arr['last_refreshed_on_time'] = (string) $this->lastRefreshedOnTime(); + $arr = array_merge($arr, $reply); + } + + return json_encode($arr); + } + + /** + * every authenticated method includes last_refreshed_on_time + * + * @return int + */ + protected function lastRefreshedOnTime() + { + $lastUpdate = 0; + + $dao = $this->getDaoForFeeds(); + $entries = $dao->listFeedsOrderUpdate(-1, 1); + $feed = current($entries); + + if (!empty($feed)) { + $lastUpdate = $feed->lastUpdate(); + } + + return $lastUpdate; + } + + /** + * @return array + */ + protected function getFeeds() + { + $feeds = array(); + + $dao = $this->getDaoForFeeds(); + $myFeeds = $dao->listFeeds(); + + /** @var FreshRSS_Feed $feed */ + foreach ($myFeeds as $feed) { + $feeds[] = array( + "id" => $feed->id(), + "favicon_id" => $feed->id(), + "title" => $feed->name(), + "url" => $feed->url(), + "site_url" => $feed->website(), + "is_spark" => 0, // unsupported + "last_updated_on_time" => $feed->lastUpdate() + ); + } + + return $feeds; + } + + /** + * @return array + */ + protected function getGroups() + { + $groups = array(); + + $dao = $this->getDaoForCategories(); + $categories = $dao->listCategories(false, false); + + /** @var FreshRSS_Category $category */ + foreach ($categories as $category) { + $groups[] = array( + 'id' => $category->id(), + 'title' => $category->name() + ); + } + + return $groups; + } + + /** + * @return array + */ + protected function getFavicons() + { + $favicons = array(); + + $dao = $this->getDaoForFeeds(); + $myFeeds = $dao->listFeeds(); + + $salt = FreshRSS_Context::$system_conf->salt; + + /** @var FreshRSS_Feed $feed */ + foreach ($myFeeds as $feed) { + + $id = hash('crc32b', $salt . $feed->url()); + $filename = DATA_PATH . '/favicons/' . $id . '.ico'; + if (!file_exists($filename)) { + continue; + } + + $favicons[] = array( + "id" => $feed->id(), + "data" => image_type_to_mime_type(exif_imagetype($filename)) . ";base64," . base64_encode(file_get_contents($filename)) + ); + } + + return $favicons; + } + + /** + * @return int + */ + protected function getTotalItems() + { + $total_items = 0; + + $dao = $this->getDaoForEntries(); + $result = $dao->countFever(); + + if (!empty($result)) { + $total_items = $result['total']; + } + + return $total_items; + } + + /** + * @return array + */ + protected function getFeedsGroup() + { + $groups = array(); + $ids = array(); + + $dao = $this->getDaoForFeeds(); + $myFeeds = $dao->listFeeds(); + + /** @var FreshRSS_Feed $feed */ + foreach ($myFeeds as $feed) { + $ids[$feed->category()][] = $feed->id(); + } + + foreach($ids as $category => $feedIds) { + $groups[] = array( + 'group_id' => $category, + 'feed_ids' => implode(',', $feedIds) + ); + } + + return $groups; + } + + /** + * AFAIK there is no 'hot links' alternative in FreshRSS + * @return array + */ + protected function getLinks() + { + return array(); + } + + /** + * @param array $ids + * @return string + */ + protected function entriesToIdList($ids = array()) + { + return implode(',', array_values($ids)); + } + + /** + * @return string + */ + protected function getUnreadItemIds() + { + $dao = $this->getDaoForEntries(); + $entries = $dao->listIdsWhere('a', '', FreshRSS_Entry::STATE_NOT_READ, 'ASC', 0); + return $this->entriesToIdList($entries); + } + + /** + * @return string + */ + protected function getSavedItemIds() + { + $dao = $this->getDaoForEntries(); + $entries = $dao->listIdsWhere('a', '', FreshRSS_Entry::STATE_FAVORITE, 'ASC', 0); + return $this->entriesToIdList($entries); + } + + protected function setItemAsRead($id) + { + $dao = $this->getDaoForEntries(); + $dao->markRead($id, true); + } + + protected function setItemAsUnread($id) + { + $dao = $this->getDaoForEntries(); + $dao->markRead($id, false); + } + + protected function setItemAsSaved($id) + { + $dao = $this->getDaoForEntries(); + $dao->markFavorite($id, true); + } + + protected function setItemAsUnsaved($id) + { + $dao = $this->getDaoForEntries(); + $dao->markFavorite($id, false); + } + + /** + * @return array + */ + protected function getItems() + { + $feed_ids = array(); + $entry_ids = array(); + $max_id = null; + $since_id = null; + + if (isset($_REQUEST["feed_ids"]) || isset($_REQUEST["group_ids"])) { + if (isset($_REQUEST["feed_ids"])) { + $feed_ids = explode(",", $_REQUEST["feed_ids"]); + } + + $dao = $this->getDaoForCategories(); + if (isset($_REQUEST["group_ids"])) { + $group_ids = explode(",", $_REQUEST["group_ids"]); + foreach ($group_ids as $id) { + /** @var FreshRSS_Category $category */ + $category = $dao->searchById($id); + /** @var FreshRSS_Feed $feed */ + foreach ($category->feeds() as $feed) { + $feeds[] = $feed->id(); + } + } + + $feed_ids = array_unique($feeds); + } + } + + if (isset($_REQUEST["max_id"])) { + // use the max_id argument to request the previous $item_limit items + if (is_numeric($_REQUEST["max_id"])) { + $max = ($_REQUEST["max_id"] > 0) ? intval($_REQUEST["max_id"]) : 0; + if ($max) { + $max_id = $max; + } + } + } else if (isset($_REQUEST["with_ids"])) { + $entry_ids = explode(",", $_REQUEST["with_ids"]); + } else { + // use the since_id argument to request the next $item_limit items + $since_id = isset($_REQUEST["since_id"]) && is_numeric($_REQUEST["since_id"]) ? intval($_REQUEST["since_id"]) : 0; + } + + $items = array(); + + $dao = $this->getDaoForEntries(); + $entries = $dao->findEntries($feed_ids, $entry_ids, $max_id, $since_id); + + // Load list of extensions and enable the "system" ones. + Minz_ExtensionManager::init(); + + foreach($entries as $item) { + /** @var FreshRSS_Entry $entry */ + $entry = Minz_ExtensionManager::callHook('entry_before_display', $item); + if (is_null($entry)) { + continue; + } + $items[] = array( + "id" => $entry->id(), + "feed_id" => $entry->feed(false), + "title" => $entry->title(), + "author" => $entry->author(), + "html" => $entry->content(), + "url" => $entry->link(), + "is_saved" => $entry->isFavorite() ? 1 : 0, + "is_read" => $entry->isRead() ? 1 : 0, + "created_on_time" => $entry->date(true) + ); + } + + return $items; + } + + /** + * TODO replace by a dynamic fetch for id <= $before timestamp + * + * @param int $beforeTimestamp + * @return int + */ + protected function convertBeforeToId($beforeTimestamp) + { + // if before is zero, set it to now so feeds all items are read from before this point in time + if ($beforeTimestamp == 0) { + $before = time(); + } + $before = PHP_INT_MAX; + + return $before; + } + + protected function setFeedAsRead($id, $before) + { + $before = $this->convertBeforeToId($before); + $dao = $this->getDaoForEntries(); + return $dao->markReadFeed($id, $before); + } + + protected function setGroupAsRead($id, $before) + { + $dao = $this->getDaoForEntries(); + + // special case to mark all items as read + if ($id === 0) { + $result = $dao->countFever(); + + if (!empty($result)) { + return $dao->markReadEntries($result['max']); + } + } + + $before = $this->convertBeforeToId($before); + return $dao->markReadCat($id, $before); + } +} + +// ================================================================================================ +// refresh is not allowed yet, probably we find a way to support it later +if (isset($_REQUEST["refresh"])) { + Minz_Log::warning('Refresh items for fever API - notImplemented()', API_LOG); + header('HTTP/1.1 501 Not Implemented'); + header('Content-Type: text/plain; charset=UTF-8'); + die('Not Implemented!'); +} + +// Start the Fever API handling +$handler = new FeverAPI(); + +header("Content-Type: application/json; charset=UTF-8"); + +if (!$handler->isAuthenticatedApiUser()) { + echo $handler->wrap(FeverAPI::STATUS_ERR, array()); +} else { + echo $handler->wrap(FeverAPI::STATUS_OK, $handler->process()); +} diff --git a/p/api/greader.php b/p/api/greader.php index 2a32ead4e..5ab6c8115 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -745,6 +745,8 @@ if (!FreshRSS_Context::$system_conf->api_enabled) { serviceUnavailable(); } +ini_set('session.use_cookies', '0'); +register_shutdown_function('session_destroy'); Minz_Session::init('FreshRSS'); $user = authorizationToUser(); diff --git a/p/api/index.php b/p/api/index.php index 429b25225..108841819 100644 --- a/p/api/index.php +++ b/p/api/index.php @@ -26,5 +26,16 @@ echo Minz_Url::display('/api/greader.php', 'html', true); configuration (without %2F support) +

    Fever compatible API

    +
    +
    Your API address:
    +
    +
    + + -- cgit v1.2.3 From c4cb988ccf9b84e6c7c6580323e31d16bb9ed2c3 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 24 May 2018 22:16:45 +0200 Subject: Changelog 1836 https://github.com/FreshRSS/FreshRSS/issues/1406 https://github.com/FreshRSS/FreshRSS/pull/1836 https://github.com/FreshRSS/FreshRSS/issues/1861 --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc32e6163..d77eae44c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 2018-05-XX FreshRSS 1.10.3-dev +* API + * Add support for Fever compatible API, enabling more clients [#1406](https://github.com/FreshRSS/FreshRSS/pull/1406) + * iOS: [Fiery Feeds](https://itunes.apple.com/app/fiery-feeds-rss-reader/id1158763303), [Unread](https://itunes.apple.com/app/unread-rss-reader/id1252376153) + * MacOS: [Readkit](https://itunes.apple.com/app/readkit/id588726889) * Features * Several per-feed options (implemented in JSON) [#1838](https://github.com/FreshRSS/FreshRSS/pull/1838) * Mark updated articles as read [#891](https://github.com/FreshRSS/FreshRSS/issues/891) @@ -66,7 +70,7 @@ ## 2018-02-24 FreshRSS 1.10.0 * API - * Add compatibility with FeedMe 3.5.3+ on Android [#1774](https://github.com/FreshRSS/FreshRSS/pull/1774) + * Add compatibility with [FeedMe](https://play.google.com/store/apps/details?id=com.seazon.feedme) 3.5.3+ on Android [#1774](https://github.com/FreshRSS/FreshRSS/pull/1774) * Features * Ability to pause feeds, and to hide them from categories [#1750](https://github.com/FreshRSS/FreshRSS/pull/1750) * Ability for the admin to reset a user’s password [#960](https://github.com/FreshRSS/FreshRSS/issues/960) -- cgit v1.2.3 From defbc5d6a54c0c9c98e9ef8e451681ac39757a77 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 24 May 2018 22:29:37 +0200 Subject: Minor readme whitespace --- README.fr.md | 3 +-- README.md | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/README.fr.md b/README.fr.md index 265030546..2cdab269b 100644 --- a/README.fr.md +++ b/README.fr.md @@ -188,8 +188,7 @@ Tout client supportant une API de type Fever ; Sélection : * iOS * [Fiery Feeds](https://itunes.apple.com/app/fiery-feeds-rss-reader/id1158763303) * [Unread](https://itunes.apple.com/app/unread-rss-reader/id1252376153) - -* MacOS: +* MacOS * [Readkit](https://itunes.apple.com/app/readkit/id588726889) diff --git a/README.md b/README.md index 0bd4f59c0..75136e5e7 100644 --- a/README.md +++ b/README.md @@ -188,8 +188,7 @@ Supported clients are: * iOS * [Fiery Feeds](https://itunes.apple.com/app/fiery-feeds-rss-reader/id1158763303) * [Unread](https://itunes.apple.com/app/unread-rss-reader/id1252376153) - -* MacOS: +* MacOS * [Readkit](https://itunes.apple.com/app/readkit/id588726889) -- cgit v1.2.3 From 4ff6613989a8f1ee2959b4d31466205f00aaec52 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 26 May 2018 11:44:17 +0200 Subject: Fix CLI for creating/deleting user with Fever API (#1896) * Fix CLI for creating user with Fever API https://github.com/FreshRSS/FreshRSS/pull/1836 * Fix CLI for deleting user with Fever API https://github.com/FreshRSS/FreshRSS/pull/1836 --- app/Controllers/userController.php | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index e09572b7e..47f0ecc62 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -190,30 +190,16 @@ class FreshRSS_user_Controller extends Minz_ActionController { $configPath = join_path($homeDir, 'config.php'); $ok &= !file_exists($configPath); } - if ($ok) { - $passwordHash = ''; - if ($passwordPlain != '') { - $passwordHash = self::hashPassword($passwordPlain); - $ok &= ($passwordHash != ''); - } - - $apiPasswordHash = ''; - if ($apiPasswordPlain != '') { - $apiPasswordHash = self::hashPassword($apiPasswordPlain); - $ok &= ($apiPasswordHash != ''); - } - } if ($ok) { if (!is_dir($homeDir)) { mkdir($homeDir); } - $userConfig['passwordHash'] = $passwordHash; - $userConfig['apiPasswordHash'] = $apiPasswordHash; $ok &= (file_put_contents($configPath, "createUser($new_user_name, $userConfig['language'], $insertDefaultFeeds); + $ok &= self::updateUser($new_user_name, $passwordPlain, $apiPasswordPlain); } return $ok; } @@ -268,15 +254,13 @@ class FreshRSS_user_Controller extends Minz_ActionController { $ok &= (strcasecmp($username, $default_user) !== 0); //It is forbidden to delete the default user } $user_data = join_path(DATA_PATH, 'users', $username); + $ok &= is_dir($user_data); if ($ok) { - $ok &= is_dir($user_data); - } - if ($ok) { + self::deleteFeverKey($username); $userDAO = new FreshRSS_UserDAO(); $ok &= $userDAO->deleteUser($username); $ok &= recursive_unlink($user_data); array_map('unlink', glob(PSHB_PATH . '/feeds/*/' . $username . '.txt')); - self::deleteFeverKey(); } return $ok; } -- cgit v1.2.3 From 0ffc006a7edcf36853c7133c9adba3c095b923cf Mon Sep 17 00:00:00 2001 From: Bartłomiej Cieszkowski <11327694+bartlomiejcieszkowski@users.noreply.github.com> Date: Sat, 26 May 2018 23:13:14 +0200 Subject: 1888 postgresql unix socket workaround (#1889) * Workaround to enable postgres unix socket instead of tcp * Support Unix sockets for MySQL and PostgreSQL --- lib/Minz/ModelPdo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Minz/ModelPdo.php b/lib/Minz/ModelPdo.php index 6928a2857..733982c14 100644 --- a/lib/Minz/ModelPdo.php +++ b/lib/Minz/ModelPdo.php @@ -58,7 +58,7 @@ class Minz_ModelPdo { try { switch ($db['type']) { case 'mysql': - $string = 'mysql:host=' . $dbServer['host'] . ';dbname=' . $db['base'] . ';charset=utf8mb4'; + $string = 'mysql:host=' . (empty($dbServer['host']) ? $db['host'] : $dbServer['host']) . ';dbname=' . $db['base'] . ';charset=utf8mb4'; if (!empty($dbServer['port'])) { $string .= ';port=' . $dbServer['port']; } @@ -73,7 +73,7 @@ class Minz_ModelPdo { $this->bd->exec('PRAGMA foreign_keys = ON;'); break; case 'pgsql': - $string = 'pgsql:host=' . $dbServer['host'] . ';dbname=' . $db['base']; + $string = 'pgsql:host=' . (empty($dbServer['host']) ? $db['host'] : $dbServer['host']) . ';dbname=' . $db['base']; if (!empty($dbServer['port'])) { $string .= ';port=' . $dbServer['port']; } -- cgit v1.2.3 From ce75410315635ff8601d4702a6a378baee640238 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 26 May 2018 23:16:00 +0200 Subject: Changelog 1888 https://github.com/FreshRSS/FreshRSS/issues/1888 https://github.com/FreshRSS/FreshRSS/pull/1889 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d77eae44c..87308bddd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ * Fix muted feeds that were not actually muted [#1844](https://github.com/FreshRSS/FreshRSS/issues/1844) * Fix errors in case of empty/wrong username when updating user settings [#1857](https://github.com/FreshRSS/FreshRSS/pull/1857) * Fixes in subscription menu [#1858](https://github.com/FreshRSS/FreshRSS/pull/1858) + * Fix allowing Unix sockets for MySQL and PostgreSQL (#1888)[https://github.com/FreshRSS/FreshRSS/issues/1888] * SimplePie * Work-around for feeds with invalid non-unique GUIDs [#1887](https://github.com/FreshRSS/FreshRSS/pull/1887) * Fix for Atom feeds using a namespace for type [#1892](https://github.com/FreshRSS/FreshRSS/issues/1892) -- cgit v1.2.3 From 5a835f22ce097c1b5d0a4fe0f7113b5455c24cb6 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 26 May 2018 23:19:34 +0200 Subject: Fix changelog link --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87308bddd..dd6b38d54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,7 @@ * Fix muted feeds that were not actually muted [#1844](https://github.com/FreshRSS/FreshRSS/issues/1844) * Fix errors in case of empty/wrong username when updating user settings [#1857](https://github.com/FreshRSS/FreshRSS/pull/1857) * Fixes in subscription menu [#1858](https://github.com/FreshRSS/FreshRSS/pull/1858) - * Fix allowing Unix sockets for MySQL and PostgreSQL (#1888)[https://github.com/FreshRSS/FreshRSS/issues/1888] + * Fix allowing Unix sockets for MySQL and PostgreSQL [#1888](https://github.com/FreshRSS/FreshRSS/issues/1888) * SimplePie * Work-around for feeds with invalid non-unique GUIDs [#1887](https://github.com/FreshRSS/FreshRSS/pull/1887) * Fix for Atom feeds using a namespace for type [#1892](https://github.com/FreshRSS/FreshRSS/issues/1892) -- cgit v1.2.3 From 7ebc56e8d3c2f0126d70dc3d5f071f36631a294d Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 28 May 2018 20:55:44 +0200 Subject: Fix null exception in shares (#1898) https://github.com/FreshRSS/FreshRSS/issues/1824 https://github.com/FreshRSS/FreshRSS/issues/1884 --- CHANGELOG.md | 1 + app/views/helpers/index/normal/entry_bottom.phtml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd6b38d54..0b8da0b64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ * Bug fixing * Fix Docker bug affecting Apache `CustomLog` (unwanted local copy of access logs), `ErrorLog`, `Listen` (IPv6 bug) [#1873](https://github.com/FreshRSS/FreshRSS/pull/1873) * Fix muted feeds that were not actually muted [#1844](https://github.com/FreshRSS/FreshRSS/issues/1844) + * Fix null exception in shares, showing only the first article [#1824](https://github.com/FreshRSS/FreshRSS/issues/1824) * Fix errors in case of empty/wrong username when updating user settings [#1857](https://github.com/FreshRSS/FreshRSS/pull/1857) * Fixes in subscription menu [#1858](https://github.com/FreshRSS/FreshRSS/pull/1858) * Fix allowing Unix sockets for MySQL and PostgreSQL [#1888](https://github.com/FreshRSS/FreshRSS/issues/1888) diff --git a/app/views/helpers/index/normal/entry_bottom.phtml b/app/views/helpers/index/normal/entry_bottom.phtml index bc23938b0..793c644f9 100644 --- a/app/views/helpers/index/normal/entry_bottom.phtml +++ b/app/views/helpers/index/normal/entry_bottom.phtml @@ -48,6 +48,9 @@ $title = $this->entry->title() . ' · ' . $this->feed->name(); foreach (FreshRSS_Context::$user_conf->sharing as $share_options) { $share = FreshRSS_Share::get($share_options['type']); + if ($share === null) { + continue; + } $share_options['link'] = $link; $share_options['title'] = $title; $share->update($share_options); -- cgit v1.2.3 From d514dc50af4973fc78e9cd4fd4a93bf08a955ca4 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 28 May 2018 21:13:42 +0200 Subject: Add Pocket share (#1899) * Add Pocket share https://github.com/FreshRSS/FreshRSS/issues/1884#issuecomment-388988200 https://github.com/FreshRSS/FreshRSS/issues/1051 * Pocket i18n --- CHANGELOG.md | 1 + app/i18n/cz/gen.php | 1 + app/i18n/de/gen.php | 1 + app/i18n/en/gen.php | 1 + app/i18n/es/gen.php | 1 + app/i18n/fr/gen.php | 1 + app/i18n/he/gen.php | 1 + app/i18n/it/gen.php | 1 + app/i18n/kr/gen.php | 1 + app/i18n/nl/gen.php | 1 + app/i18n/pt-br/gen.php | 1 + app/i18n/ru/gen.php | 1 + app/i18n/tr/gen.php | 1 + app/i18n/zh-cn/gen.php | 1 + app/shares.php | 6 ++++++ 15 files changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b8da0b64..96ca33287 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * All parts are implicitly `AND` (which must not be written), except if `OR` is stated. * No use of parentheses. Support for quotes to disable the Boolean search, like `"This or that"`. * Example: `Hello intitle:World OR date:P1D example OR author:Else intitle:"This or that"` + * Share with Pocket [#1884](https://github.com/FreshRSS/FreshRSS/issues/1884) * Deployment * Includes an optional cron daemon in Docker to refresh feeds automatically [#1869](https://github.com/FreshRSS/FreshRSS/issues/1869) * Docker Compose example [#1882](https://github.com/FreshRSS/FreshRSS/pull/1882) diff --git a/app/i18n/cz/gen.php b/app/i18n/cz/gen.php index 288be61ec..09a8307ff 100644 --- a/app/i18n/cz/gen.php +++ b/app/i18n/cz/gen.php @@ -170,6 +170,7 @@ return array( 'jdh' => 'Journal du hacker', 'mastodon' => 'Mastodon', 'movim' => 'Movim', + 'pocket' => 'Pocket', 'print' => 'Tisk', 'shaarli' => 'Shaarli', 'twitter' => 'Twitter', diff --git a/app/i18n/de/gen.php b/app/i18n/de/gen.php index 6313c8b72..7dbaac384 100644 --- a/app/i18n/de/gen.php +++ b/app/i18n/de/gen.php @@ -169,6 +169,7 @@ return array( 'jdh' => 'Journal du hacker', 'mastodon' => 'Mastodon', 'movim' => 'Movim', + 'pocket' => 'Pocket', 'print' => 'Drucken', 'shaarli' => 'Shaarli', 'twitter' => 'Twitter', diff --git a/app/i18n/en/gen.php b/app/i18n/en/gen.php index 82cd4da72..ceee25105 100644 --- a/app/i18n/en/gen.php +++ b/app/i18n/en/gen.php @@ -170,6 +170,7 @@ return array( 'Known' => 'Known based sites', 'mastodon' => 'Mastodon', 'movim' => 'Movim', + 'pocket' => 'Pocket', 'print' => 'Print', 'shaarli' => 'Shaarli', 'twitter' => 'Twitter', diff --git a/app/i18n/es/gen.php b/app/i18n/es/gen.php index fe495a63f..a5957e12f 100755 --- a/app/i18n/es/gen.php +++ b/app/i18n/es/gen.php @@ -170,6 +170,7 @@ return array( 'jdh' => 'Journal du hacker', 'mastodon' => 'Mastodon', 'movim' => 'Movim', + 'pocket' => 'Pocket', 'print' => 'Print', 'shaarli' => 'Shaarli', 'twitter' => 'Twitter', diff --git a/app/i18n/fr/gen.php b/app/i18n/fr/gen.php index beb578543..24c4b82fb 100644 --- a/app/i18n/fr/gen.php +++ b/app/i18n/fr/gen.php @@ -170,6 +170,7 @@ return array( 'jdh' => 'Journal du hacker', 'mastodon' => 'Mastodon', 'movim' => 'Movim', + 'pocket' => 'Pocket', 'print' => 'Imprimer', 'shaarli' => 'Shaarli', 'twitter' => 'Twitter', diff --git a/app/i18n/he/gen.php b/app/i18n/he/gen.php index c4262ab66..401e13620 100644 --- a/app/i18n/he/gen.php +++ b/app/i18n/he/gen.php @@ -170,6 +170,7 @@ return array( 'Known' => 'Known based sites', 'mastodon' => 'Mastodon', 'movim' => 'Movim', + 'pocket' => 'Pocket', 'print' => 'הדפסה', 'shaarli' => 'Shaarli', 'twitter' => 'Twitter', diff --git a/app/i18n/it/gen.php b/app/i18n/it/gen.php index d4f83735a..b2e007bce 100644 --- a/app/i18n/it/gen.php +++ b/app/i18n/it/gen.php @@ -170,6 +170,7 @@ return array( 'jdh' => 'Journal du hacker', 'mastodon' => 'Mastodon', 'movim' => 'Movim', + 'pocket' => 'Pocket', 'print' => 'Stampa', 'shaarli' => 'Shaarli', 'twitter' => 'Twitter', diff --git a/app/i18n/kr/gen.php b/app/i18n/kr/gen.php index 4fb6c92ff..b40ab14c4 100644 --- a/app/i18n/kr/gen.php +++ b/app/i18n/kr/gen.php @@ -170,6 +170,7 @@ return array( 'jdh' => 'Journal du hacker', 'mastodon' => 'Mastodon', 'movim' => 'Movim', + 'pocket' => 'Pocket', 'print' => '인쇄', 'shaarli' => 'Shaarli', 'twitter' => 'Twitter', diff --git a/app/i18n/nl/gen.php b/app/i18n/nl/gen.php index ea3e720b4..1755197ba 100644 --- a/app/i18n/nl/gen.php +++ b/app/i18n/nl/gen.php @@ -170,6 +170,7 @@ return array( 'jdh' => 'Journal du hacker', 'mastodon' => 'Mastodon', 'movim' => 'Movim', + 'pocket' => 'Pocket', 'print' => 'Print', 'shaarli' => 'Shaarli', 'twitter' => 'Twitter', diff --git a/app/i18n/pt-br/gen.php b/app/i18n/pt-br/gen.php index fa1532787..932b6066d 100644 --- a/app/i18n/pt-br/gen.php +++ b/app/i18n/pt-br/gen.php @@ -169,6 +169,7 @@ return array( 'jdh' => 'Journal du hacker', 'mastodon' => 'Mastodon', 'movim' => 'Movim', + 'pocket' => 'Pocket', 'print' => 'Imprimir', 'shaarli' => 'Shaarli', 'twitter' => 'Twitter', diff --git a/app/i18n/ru/gen.php b/app/i18n/ru/gen.php index 6f9020695..ef9217d73 100644 --- a/app/i18n/ru/gen.php +++ b/app/i18n/ru/gen.php @@ -170,6 +170,7 @@ return array( 'jdh' => 'Journal du hacker', 'mastodon' => 'Mastodon', 'movim' => 'Movim', + 'pocket' => 'Pocket', 'print' => 'Print', 'shaarli' => 'Shaarli', 'twitter' => 'Twitter', diff --git a/app/i18n/tr/gen.php b/app/i18n/tr/gen.php index 293502e74..d99081396 100644 --- a/app/i18n/tr/gen.php +++ b/app/i18n/tr/gen.php @@ -170,6 +170,7 @@ return array( 'jdh' => 'Journal du hacker', 'mastodon' => 'Mastodon', 'movim' => 'Movim', + 'pocket' => 'Pocket', 'print' => 'Print', 'shaarli' => 'Shaarli', 'twitter' => 'Twitter', diff --git a/app/i18n/zh-cn/gen.php b/app/i18n/zh-cn/gen.php index 241fce13b..9289d8571 100644 --- a/app/i18n/zh-cn/gen.php +++ b/app/i18n/zh-cn/gen.php @@ -170,6 +170,7 @@ return array( 'jdh' => 'Journal du hacker', 'mastodon' => 'Mastodon', 'movim' => 'Movim', + 'pocket' => 'Pocket', 'print' => '打印', 'shaarli' => 'Shaarli', 'twitter' => 'Twitter', diff --git a/app/shares.php b/app/shares.php index 5403fd48c..cd09aa19c 100644 --- a/app/shares.php +++ b/app/shares.php @@ -126,4 +126,10 @@ return array( 'method' => 'POST', 'field' => 'status', ), + 'pocket' => array( + 'url' => 'https://getpocket.com/save?url=~LINK~&title=~TITLE~', + 'transform' => array('rawurlencode'), + 'form' => 'simple', + 'method' => 'GET', + ), ); -- cgit v1.2.3 From 58fb9ea5b32040fdb64ece910f0ede1aafb97329 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 28 May 2018 22:14:10 +0200 Subject: Fix CLI no_default_feeds (#1900) * Fix CLI no_default_feeds The option was not working anymore * Changelog 1900 https://github.com/FreshRSS/FreshRSS/pull/1900 --- CHANGELOG.md | 1 + cli/create-user.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96ca33287..80e2f05eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ * Fix errors in case of empty/wrong username when updating user settings [#1857](https://github.com/FreshRSS/FreshRSS/pull/1857) * Fixes in subscription menu [#1858](https://github.com/FreshRSS/FreshRSS/pull/1858) * Fix allowing Unix sockets for MySQL and PostgreSQL [#1888](https://github.com/FreshRSS/FreshRSS/issues/1888) + * Fix `create-user` CLI option `no_default_feeds` [#1900](https://github.com/FreshRSS/FreshRSS/pull/1900) * SimplePie * Work-around for feeds with invalid non-unique GUIDs [#1887](https://github.com/FreshRSS/FreshRSS/pull/1887) * Fix for Atom feeds using a namespace for type [#1892](https://github.com/FreshRSS/FreshRSS/issues/1892) diff --git a/cli/create-user.php b/cli/create-user.php index 5bc6c1e6c..29675fa53 100755 --- a/cli/create-user.php +++ b/cli/create-user.php @@ -20,7 +20,7 @@ $ok = FreshRSS_user_Controller::createUser($username, empty($options['password']) ? '' : $options['password'], empty($options['api_password']) ? '' : $options['api_password'], $values, - !isset($options['no-default-feeds'])); + !isset($options['no_default_feeds'])); if (!$ok) { fail('FreshRSS could not create user!'); -- cgit v1.2.3 From 1801f44bfca5ecc0f5709f03c9ee3434b78a9bf9 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 28 May 2018 23:33:48 +0200 Subject: Fix import: invalid variable scope (#1901) * Fix import: invalid variable scope https://github.com/FreshRSS/FreshRSS/issues/1890 https://github.com/FreshRSS/FreshRSS/pull/1315 * Changelog 1890 https://github.com/FreshRSS/FreshRSS/issues/1890 https://github.com/FreshRSS/FreshRSS/pull/1901 --- CHANGELOG.md | 1 + app/Controllers/importExportController.php | 21 +++++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80e2f05eb..88cdce03e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ * Fix Docker bug affecting Apache `CustomLog` (unwanted local copy of access logs), `ErrorLog`, `Listen` (IPv6 bug) [#1873](https://github.com/FreshRSS/FreshRSS/pull/1873) * Fix muted feeds that were not actually muted [#1844](https://github.com/FreshRSS/FreshRSS/issues/1844) * Fix null exception in shares, showing only the first article [#1824](https://github.com/FreshRSS/FreshRSS/issues/1824) + * Fix error during import[#1890](https://github.com/FreshRSS/FreshRSS/issues/1890) * Fix errors in case of empty/wrong username when updating user settings [#1857](https://github.com/FreshRSS/FreshRSS/pull/1857) * Fixes in subscription menu [#1858](https://github.com/FreshRSS/FreshRSS/pull/1858) * Fix allowing Unix sockets for MySQL and PostgreSQL [#1888](https://github.com/FreshRSS/FreshRSS/issues/1888) diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index a76dd9a2b..0fb5ba651 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -390,6 +390,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $article_to_feed = array(); $nb_feeds = count($this->feedDAO->listFeeds()); + $newFeedGuids = array(); $limits = FreshRSS_Context::$system_conf->limits; // First, we check feeds of articles are in DB (and add them if needed). @@ -417,21 +418,25 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if ($feed != null) { $article_to_feed[$item['id']] = $feed->id(); + if (!isset($newFeedGuids['f_' . $feed->id()])) { + $newFeedGuids['f_' . $feed->id()] = array(); + } + $newFeedGuids['f_' . $feed->id()][] = safe_ascii($item['id']); } } - $newGuids = array(); - foreach ($article_object['items'] as $item) { - $newGuids[] = safe_ascii($item['id']); + // For each feed, check existing GUIDs already in database. + $existingHashForGuids = array(); + foreach ($newFeedGuids as $feedId => $newGuids) { + $existingHashForGuids[$feedId] = $this->entryDAO->listHashForFeedGuids(substr($feedId, 2), $newGuids); } - // For this feed, check existing GUIDs already in database. - $existingHashForGuids = $this->entryDAO->listHashForFeedGuids($feed->id(), $newGuids); - $newGuids = array(); + unset($newFeedGuids); // Then, articles are imported. + $newGuids = array(); $this->entryDAO->beginTransaction(); foreach ($article_object['items'] as $item) { - if (!isset($article_to_feed[$item['id']])) { + if (empty($article_to_feed[$item['id']])) { // Related feed does not exist for this entry, do nothing. continue; } @@ -468,7 +473,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $values = $entry->toArray(); $ok = false; - if (isset($existingHashForGuids[$entry->guid()])) { + if (isset($existingHashForGuids['f_' . $feed_id][$entry->guid()])) { $ok = $this->entryDAO->updateEntry($values); } else { $ok = $this->entryDAO->addEntry($values); -- cgit v1.2.3 From 18443963b92827e750a5a2a8de71aa63af9d3f6a Mon Sep 17 00:00:00 2001 From: Frans de Jonge Date: Mon, 28 May 2018 23:54:16 +0200 Subject: [i18n] Finish Dutch translation (#1903) --- app/i18n/nl/admin.php | 6 +++--- app/i18n/nl/conf.php | 28 ++++++++++++++-------------- app/i18n/nl/gen.php | 2 +- app/i18n/nl/sub.php | 16 ++++++++-------- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/app/i18n/nl/admin.php b/app/i18n/nl/admin.php index cbdfd6978..a1d975305 100644 --- a/app/i18n/nl/admin.php +++ b/app/i18n/nl/admin.php @@ -175,7 +175,7 @@ return array( 'user' => array( 'articles_and_size' => '%s artikelen (%s)', 'create' => 'Creëer nieuwe gebruiker', - 'delete_users' => 'Delete user', // TODO + 'delete_users' => 'Verwijder gebruiker', 'language' => 'Taal', 'number' => 'Er is %d accounts gemaakt', 'numbers' => 'Er zijn %d accounts gemaakt', @@ -186,9 +186,9 @@ return array( 'help' => '0 betekent dat er geen accountlimiet is', 'number' => 'Max aantal accounts', ), - 'selected' => 'Selected user', // TODO + 'selected' => 'Geselecteerde gebruiker', 'title' => 'Beheer gebruikers', - 'update_users' => 'Update user', // TODO + 'update_users' => 'Gebruiker bijwerken', 'user_list' => 'Lijst van gebruikers ', 'username' => 'Gebruikersnaam', 'users' => 'Gebruikers', diff --git a/app/i18n/nl/conf.php b/app/i18n/nl/conf.php index 6d739a0b7..041b482b9 100644 --- a/app/i18n/nl/conf.php +++ b/app/i18n/nl/conf.php @@ -37,12 +37,12 @@ return array( 'no_limit' => 'Geen limiet', 'thin' => 'Smal', ), - 'show_nav_buttons' => 'Show the navigation buttons', //TODO + 'show_nav_buttons' => 'Toon navigatieknoppen', ), 'query' => array( - '_' => 'Gebruikers queries (informatie aanvragen)', + '_' => 'Gebruikersquery\'s (informatie aanvragen)', 'deprecated' => 'Deze query (informatie aanvraag) is niet langer geldig. De bedoelde categorie of feed is al verwijderd.', - 'display' => 'Display user query results', // TODO + 'display' => 'Queryresultaten weergeven', 'filter' => 'Filter toegepast:', 'get_all' => 'Toon alle artikelen', 'get_category' => 'Toon "%s" categorie', @@ -53,7 +53,7 @@ return array( 'number' => 'Query n°%d', 'order_asc' => 'Toon oudste artikelen eerst', 'order_desc' => 'Toon nieuwste artikelen eerst', - 'remove' => 'Remove user query', // TODO + 'remove' => 'Gebruikersquery verwijderen', 'search' => 'Zoek naar "%s"', 'state_0' => 'Toon alle artikelen', 'state_1' => 'Toon gelezen artikelen', @@ -71,7 +71,7 @@ return array( 'state_13' => 'Toon gelezen artikelen', 'state_14' => 'Toon ongelezen artikelen', 'state_15' => 'Toon alle artikelen', - 'title' => 'Gebruikers queries', + 'title' => 'Gebruikersquery\'s', ), 'profile' => array( '_' => 'Profiel beheer', @@ -128,7 +128,7 @@ return array( ), 'sharing' => array( '_' => 'Delen', - 'add' => 'Add a sharing method', // TODO + 'add' => 'Deelmethode toevoegen', 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', 'email' => 'Email', @@ -136,7 +136,7 @@ return array( 'g+' => 'Google+', 'more_information' => 'Meer informatie', 'print' => 'Afdrukken', - 'remove' => 'Remove sharing method', // TODO + 'remove' => 'Deelmethode verwijderen', 'shaarli' => 'Shaarli', 'share_name' => 'Gedeelde naam om weer te geven', 'share_url' => 'Deel URL voor gebruik', @@ -148,12 +148,12 @@ return array( '_' => 'Shortcuts', 'article_action' => 'Artikel acties', 'auto_share' => 'Delen', - 'auto_share_help' => 'Als er slechts één deel methode i, dan wordt deze gebruikt. Anders zijn ze toegankelijk met hun nummer.', + 'auto_share_help' => 'Als er slechts één deelmethode is, dan wordt die gebruikt. Anders zijn ze toegankelijk met hun nummer.', 'close_dropdown' => 'Sluit menu', 'collapse_article' => 'Inklappen', 'first_article' => 'Spring naar eerste artikel', 'focus_search' => 'Toegang zoek venster', - 'global_view' => 'Switch to global view', // TODO + 'global_view' => 'Schakel naar globaal aanzicht', 'help' => 'Toon documentatie', 'javascript' => 'JavaScript moet geactiveerd zijn om verwijzingen te gebruiken', 'last_article' => 'Spring naar laatste artikel', @@ -163,17 +163,17 @@ return array( 'navigation' => 'Navigatie', 'navigation_help' => 'Met de "Shift" toets, kunt u navigatie verwijzingen voor feeds gebruiken.
    Met de "Alt" toets, kunt u navigatie verwijzingen voor categoriën gebruiken.', 'next_article' => 'Spring naar volgende artikel', - 'normal_view' => 'Switch to normal view', // TODO + 'normal_view' => 'Schakel naar gewoon aanzicht', 'other_action' => 'Andere acties', 'previous_article' => 'Spring naar vorige artikel', - 'reading_view' => 'Switch to reading view', // TODO - 'rss_view' => 'Open RSS view in a new tab', // TODO + 'reading_view' => 'Schakel naar leesaanzicht', + 'rss_view' => 'Open RSS-aanzicht in een nieuwe tab', 'see_on_website' => 'Bekijk op originale website', 'shift_for_all_read' => '+ shift om alle artikelen als gelezen te markeren', 'title' => 'Verwijzingen', 'user_filter' => 'Toegang gebruikers filters', - 'user_filter_help' => 'Als er slechts één gebruikers filter s, dan wordt deze gebruikt. Anders zijn ze toegankelijk met hun nummer.', - 'views' => 'Views', // TODO + 'user_filter_help' => 'Als er slechts één gebruikersfilter is, dan wordt die gebruikt. Anders zijn ze toegankelijk met hun nummer.', + 'views' => 'Aanzichten', ), 'user' => array( 'articles_and_size' => '%s artikelen (%s)', diff --git a/app/i18n/nl/gen.php b/app/i18n/nl/gen.php index 1755197ba..683209cb9 100644 --- a/app/i18n/nl/gen.php +++ b/app/i18n/nl/gen.php @@ -19,7 +19,7 @@ return array( 'see_website' => 'Bekijk website', 'submit' => 'Opslaan', 'truncate' => 'Verwijder alle artikelen', - 'update' => 'Update', // TODO + 'update' => 'Updaten', ), 'auth' => array( 'email' => 'Email adres', diff --git a/app/i18n/nl/sub.php b/app/i18n/nl/sub.php index 067e226aa..4ce254ef5 100644 --- a/app/i18n/nl/sub.php +++ b/app/i18n/nl/sub.php @@ -35,20 +35,20 @@ return array( 'informations' => 'Informatie', 'keep_history' => 'Minimum aantal artikelen om te houden', 'moved_category_deleted' => 'Als u een categorie verwijderd, worden de feeds automatisch geclassificeerd onder %s.', - 'mute' => 'mute', // TODO + 'mute' => 'demp', 'no_selected' => 'Geen feed geselecteerd.', 'number_entries' => '%d artikelen', 'priority' => array( - '_' => 'Visibility', // TODO - 'archived' => 'Do not show (archived)', // TODO + '_' => 'Zichtbaarheid', + 'archived' => 'Niet weergeven (gearchiveerd)', 'main_stream' => 'Zichtbaar in het overzicht', - 'normal' => 'Show in its category', // TODO + 'normal' => 'Toon in categorie', ), 'pubsubhubbub' => 'Directe notificaties met PubSubHubbub', - 'ssl_verify' => 'Verify SSL security', //TODO + 'ssl_verify' => 'SSL-veiligheid controleren', 'stats' => 'Statistieken', 'think_to_add' => 'Voeg wat feeds toe.', - 'timeout' => 'Timeout in seconds', //TODO + 'timeout' => 'Time-out in seconden', 'title' => 'Titel', 'title_add' => 'Voeg een RSS feed toe', 'ttl' => 'Vernieuw automatisch niet vaker dan', @@ -75,11 +75,11 @@ return array( 'bookmark' => 'Abonneer (FreshRSS bladwijzer)', 'import_export' => 'Importeer / exporteer', 'subscription_management' => 'Abonnementenbeheer', - 'subscription_tools' => 'Subscription tools',// TODO + 'subscription_tools' => 'Hulpmiddelen voor abonnementen', ), 'title' => array( '_' => 'Abonnementenbeheer', 'feed_management' => 'RSS-feedbeheer', - 'subscription_tools' => 'Subscription tools',// TODO + 'subscription_tools' => 'Hulpmiddelen voor abonnementen', ), ); -- cgit v1.2.3 From 4ec1ebade400725266d65dcdd368b4cee238284e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 29 May 2018 19:11:28 +0200 Subject: Feed attributes only for admin (#1905) * Feed attributes only for admin https://github.com/FreshRSS/FreshRSS/pull/1838 * Changelog 1905 https://github.com/FreshRSS/FreshRSS/pull/1905 --- CHANGELOG.md | 5 +++-- app/Controllers/subscriptionController.php | 11 ++++++++--- app/views/helpers/feed/update.phtml | 2 ++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88cdce03e..759f5eaaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,9 @@ * Several per-feed options (implemented in JSON) [#1838](https://github.com/FreshRSS/FreshRSS/pull/1838) * Mark updated articles as read [#891](https://github.com/FreshRSS/FreshRSS/issues/891) * Mark as read upon reception [#1702](https://github.com/FreshRSS/FreshRSS/issues/1702) - * Feed cURL timeout - * Ignore SSL (unsafe) [#1811](https://github.com/FreshRSS/FreshRSS/issues/1811) + * Only for admin user [#1905](https://github.com/FreshRSS/FreshRSS/pull/1905) + * Feed cURL timeout + * Ignore SSL (unsafe) [#1811](https://github.com/FreshRSS/FreshRSS/issues/1811) * Light Boolean search implementation [#879](https://github.com/FreshRSS/FreshRSS/issues/879) * All parts are implicitly `AND` (which must not be written), except if `OR` is stated. * No use of parentheses. Support for quotes to disable the Boolean search, like `"This or that"`. diff --git a/app/Controllers/subscriptionController.php b/app/Controllers/subscriptionController.php index 860cd912f..701a588e0 100644 --- a/app/Controllers/subscriptionController.php +++ b/app/Controllers/subscriptionController.php @@ -98,10 +98,15 @@ class FreshRSS_subscription_Controller extends Minz_ActionController { $feed->_attributes('mark_updated_article_unread', Minz_Request::paramTernary('mark_updated_article_unread')); $feed->_attributes('read_upon_reception', Minz_Request::paramTernary('read_upon_reception')); - $feed->_attributes('ssl_verify', Minz_Request::paramTernary('ssl_verify')); - $timeout = intval(Minz_Request::param('timeout', 0)); - $feed->_attributes('timeout', $timeout > 0 ? $timeout : null); + if (FreshRSS_Auth::hasAccess('admin')) { + $feed->_attributes('ssl_verify', Minz_Request::paramTernary('ssl_verify')); + $timeout = intval(Minz_Request::param('timeout', 0)); + $feed->_attributes('timeout', $timeout > 0 ? $timeout : null); + } else { + $feed->_attributes('ssl_verify', null); + $feed->_attributes('timeout', null); + } $values = array( 'name' => Minz_Request::param('name', ''), diff --git a/app/views/helpers/feed/update.phtml b/app/views/helpers/feed/update.phtml index 88a38ea58..7144aab46 100644 --- a/app/views/helpers/feed/update.phtml +++ b/app/views/helpers/feed/update.phtml @@ -205,6 +205,7 @@
    +
    @@ -224,6 +225,7 @@
    +
    -- cgit v1.2.3 From 66ff16294a2f5dc6a731e4e7a82409d4e3e4dee0 Mon Sep 17 00:00:00 2001 From: Upils Date: Tue, 29 May 2018 22:28:30 +0200 Subject: Correct docker-compose.yml file and add a corresponding note in the doc. (#1906) * Add a docker compose example. Using postgresql and with traefik specific labels. * Added docker-compose specific documentation. * Move docker-compose section at the end of the README.md. * Correct docker-compose.yml file and add a corresponding note in the doc. * Typo in doc. --- Docker/README.md | 3 ++- Docker/docker-compose.yml | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Docker/README.md b/Docker/README.md index 381adab38..133e7c3b8 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -160,11 +160,12 @@ with HTTPS, for instance using [Let’s Encrypt](https://letsencrypt.org/). A [docker-compose.yml](docker-compose.yml) file is given as an example, using PostgreSQL. In order to use it, you have to adapt: - In the `postgresql` service: - * the `volumes` section; + * the `volumes` section. Be careful to keep the path `/var/lib/postgresql/data` for the container. If the path is wrong, you will not get any error but your db will be gone at the next run; * the `POSTGRES_PASSWORD` in the `environment` section; - In the `freshrss` service: * the `volumes` section; * options under the `labels` section are specific to [Træfik](https://traefik.io/), a reverse proxy. If you are not using it, feel free to delete this section. If you are using it, adapt accordingly to your config, especially the `traefik.frontend.rule` option. + * the `environment` section to adapt the strategy to update feeds. You can then launch the stack (postgres + freshrss) with: ```sh diff --git a/Docker/docker-compose.yml b/Docker/docker-compose.yml index a57f214da..8a8ad270b 100644 --- a/Docker/docker-compose.yml +++ b/Docker/docker-compose.yml @@ -5,7 +5,7 @@ services: image: postgres:latest restart: unless-stopped volumes: - - '/path/to/pgsql-data:/var/lib/postgresql/data:Z' + - '/path/to/pgsql-data:/var/lib/postgresql/data' environment: - POSTGRES_USER=freshrss - POSTGRES_PASSWORD=password @@ -20,7 +20,7 @@ services: - web - default volumes: - - '/your/local/directory/data:/var/www/FreshRSS/data:Z' + - '/your/local/directory/data:/var/www/FreshRSS/data' labels: - "traefik.backend=freshrss" - "traefik.docker.network=web" @@ -29,6 +29,8 @@ services: - "traefik.default.protocol=http" - "traefik.frontend.entryPoints=http,https" - "traefik.port=80" + environment: + - CRON_MIN=*/20 networks: web: -- cgit v1.2.3 From e98e40b7b4d8ebd4f7e03ade2d3fb6be2c22cc52 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 29 May 2018 23:55:31 +0200 Subject: Fix check default category, and PostgreSQL seq bug (#1907) checkDefault() was broken as it was not necessarily creating the default category with the right ID. PostgreSQL required additional care. https://github.com/FreshRSS/FreshRSS/issues/1890#issuecomment-392869777 https://github.com/FreshRSS/FreshRSS/pull/1322 https://github.com/FreshRSS/FreshRSS/issues/1312#issuecomment-254009397 --- app/Models/CategoryDAO.php | 26 ++++++++++++++++++++++---- app/SQL/install.sql.pgsql.php | 2 +- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index 68db17db3..ef2c402a0 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -134,7 +134,11 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable if (isset($cat[0])) { return $cat[0]; } else { - return false; + if (FreshRSS_Context::$isCli) { + fwrite(STDERR, 'FreshRSS database error: Default category not found!' . "\n"); + } + Minz_Log::error('FreshRSS database error: Default category not found!'); + return null; } } public function checkDefault() { @@ -144,13 +148,27 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable $cat = new FreshRSS_Category(_t('gen.short.default_category')); $cat->_id(self::DEFAULTCATEGORYID); + $sql = 'INSERT INTO `' . $this->prefix . 'category`(id, name) VALUES(?, ?)'; + if (parent::$sharedDbType === 'pgsql') { + //Force call to nextval() + $sql .= " RETURNING nextval('" . $this->prefix . "category_id_seq');"; + } + $stm = $this->bd->prepare($sql); + $values = array( - 'id' => $cat->id(), - 'name' => $cat->name(), + $cat->id(), + $cat->name(), ); - $this->addCategory($values); + if ($stm && $stm->execute($values)) { + return $this->bd->lastInsertId('"' . $this->prefix . 'category_id_seq"'); + } else { + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::error('SQL error check default category: ' . json_encode($info)); + return false; + } } + return true; } public function count() { diff --git a/app/SQL/install.sql.pgsql.php b/app/SQL/install.sql.pgsql.php index 99f5a05d3..b80fbf1e7 100644 --- a/app/SQL/install.sql.pgsql.php +++ b/app/SQL/install.sql.pgsql.php @@ -52,7 +52,7 @@ $SQL_CREATE_TABLES = array( 'CREATE INDEX %1$sis_read_index ON "%1$sentry" ("is_read");', '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);', +'INSERT INTO "%1$scategory" (id, name) SELECT 1, \'%2$s\' WHERE NOT EXISTS (SELECT id FROM "%1$scategory" WHERE id = 1) RETURNING nextval(\'%1$scategory_id_seq\');', ); global $SQL_CREATE_TABLE_ENTRYTMP; -- cgit v1.2.3 From 1cba18e3ef15958f9f962dc906705b5ae890b357 Mon Sep 17 00:00:00 2001 From: Bartłomiej Cieszkowski <11327694+bartlomiejcieszkowski@users.noreply.github.com> Date: Wed, 30 May 2018 09:22:04 +0200 Subject: Typo: FressRSS -> FreshRSS (#1908) @bartlomiejcieszkowski https://github.com/FreshRSS/FreshRSS/pull/1908/commits/81e014d0963a7e710e96bac3fdb350931ac1fc91 --- app/i18n/cz/feedback.php | 6 +++--- app/i18n/de/feedback.php | 6 +++--- app/i18n/en/feedback.php | 6 +++--- app/i18n/es/feedback.php | 6 +++--- app/i18n/he/feedback.php | 4 ++-- app/i18n/kr/feedback.php | 6 +++--- app/i18n/nl/feedback.php | 6 +++--- app/i18n/pt-br/feedback.php | 6 +++--- app/i18n/ru/feedback.php | 6 +++--- app/i18n/tr/feedback.php | 6 +++--- app/i18n/tr/install.php | 2 +- app/i18n/zh-cn/feedback.php | 6 +++--- 12 files changed, 33 insertions(+), 33 deletions(-) diff --git a/app/i18n/cz/feedback.php b/app/i18n/cz/feedback.php index 693efcb8e..ff9c87d12 100644 --- a/app/i18n/cz/feedback.php +++ b/app/i18n/cz/feedback.php @@ -31,11 +31,11 @@ return array( 'extensions' => array( 'already_enabled' => '%s je již zapnut', 'disable' => array( - 'ko' => '%s nelze vypnout. Pro více detailů zkontrolujte logy FressRSS.', + 'ko' => '%s nelze vypnout. Pro více detailů zkontrolujte logy FreshRSS.', 'ok' => '%s je nyní vypnut', ), 'enable' => array( - 'ko' => '%s nelze zapnout. Pro více detailů zkontrolujte logy FressRSS.', + 'ko' => '%s nelze zapnout. Pro více detailů zkontrolujte logy FreshRSS.', 'ok' => '%s je nyní zapnut', ), 'no_access' => 'Nemáte přístup k %s', @@ -72,7 +72,7 @@ return array( 'already_subscribed' => 'Již jste přihlášen k odběru %s', 'deleted' => 'Kanál byl smazán', 'error' => 'Kanál nelze aktualizovat', - 'internal_problem' => 'RSS kanál nelze přidat. Pro detaily zkontrolujte logy FressRSS.', // @todo + 'internal_problem' => 'RSS kanál nelze přidat. Pro detaily zkontrolujte logy FreshRSS.', // @todo 'invalid_url' => 'URL %s není platné', 'marked_read' => 'Kanály byly označeny jako přečtené', 'n_actualized' => '%d kanálů bylo aktualizováno', diff --git a/app/i18n/de/feedback.php b/app/i18n/de/feedback.php index 75e92538a..2c46bbe56 100644 --- a/app/i18n/de/feedback.php +++ b/app/i18n/de/feedback.php @@ -31,11 +31,11 @@ return array( 'extensions' => array( 'already_enabled' => '%s ist bereits aktiviert', 'disable' => array( - 'ko' => '%s kann nicht deaktiviert werden. Für Details prüfen Sie die FressRSS-Protokolle.', + 'ko' => '%s kann nicht deaktiviert werden. Für Details prüfen Sie die FreshRSS-Protokolle.', 'ok' => '%s ist jetzt deaktiviert', ), 'enable' => array( - 'ko' => '%s kann nicht aktiviert werden. Für Details prüfen Sie die FressRSS-Protokolle.', + 'ko' => '%s kann nicht aktiviert werden. Für Details prüfen Sie die FreshRSS-Protokolle.', 'ok' => '%s ist jetzt aktiviert', ), 'no_access' => 'Sie haben keinen Zugang zu %s', @@ -72,7 +72,7 @@ return array( 'already_subscribed' => 'Sie haben %s bereits abonniert', 'deleted' => 'Der Feed ist gelöscht worden', 'error' => 'Der Feed kann nicht aktualisiert werden', - 'internal_problem' => 'Der RSS-Feed konnte nicht hinzugefügt werden. Für Details prüfen Sie die FressRSS-Protokolle.', // @todo + 'internal_problem' => 'Der RSS-Feed konnte nicht hinzugefügt werden. Für Details prüfen Sie die FreshRSS-Protokolle.', // @todo 'invalid_url' => 'Die URL %s ist ungültig', 'marked_read' => 'Die Feeds sind als gelesen markiert worden', 'n_actualized' => 'Die %d Feeds sind aktualisiert worden', diff --git a/app/i18n/en/feedback.php b/app/i18n/en/feedback.php index 79fa2c347..a7fbda3a0 100644 --- a/app/i18n/en/feedback.php +++ b/app/i18n/en/feedback.php @@ -31,11 +31,11 @@ return array( 'extensions' => array( 'already_enabled' => '%s is already enabled', 'disable' => array( - 'ko' => '%s cannot be disabled. Check FressRSS logs for details.', + 'ko' => '%s cannot be disabled. Check FreshRSS logs for details.', 'ok' => '%s is now disabled', ), 'enable' => array( - 'ko' => '%s cannot be enabled. Check FressRSS logs for details.', + 'ko' => '%s cannot be enabled. Check FreshRSS logs for details.', 'ok' => '%s is now enabled', ), 'no_access' => 'You have no access on %s', @@ -72,7 +72,7 @@ return array( 'already_subscribed' => 'You have already subscribed to %s', 'deleted' => 'Feed has been deleted', 'error' => 'Feed cannot be updated', - 'internal_problem' => 'The newsfeed could not be added. Check FressRSS logs for details. You can try force adding by appending #force_feed to the URL.', + 'internal_problem' => 'The newsfeed could not be added. Check FreshRSS logs for details. You can try force adding by appending #force_feed to the URL.', 'invalid_url' => 'URL %s is invalid', 'marked_read' => 'Feeds have been marked as read', 'n_actualized' => '%d feeds have been updated', diff --git a/app/i18n/es/feedback.php b/app/i18n/es/feedback.php index ce5d5e75f..627c86afc 100755 --- a/app/i18n/es/feedback.php +++ b/app/i18n/es/feedback.php @@ -31,11 +31,11 @@ return array( 'extensions' => array( 'already_enabled' => '%s ya está activado', 'disable' => array( - 'ko' => '%s no se puede desactivar. Revisa el registro de FressRSS para más información.', + 'ko' => '%s no se puede desactivar. Revisa el registro de FreshRSS para más información.', 'ok' => '%s ha quedado desactivado', ), 'enable' => array( - 'ko' => '%s no se puede activar. Revisa el registro de FressRSS para más información.', + 'ko' => '%s no se puede activar. Revisa el registro de FreshRSS para más información.', 'ok' => '%s ha quedado activado', ), 'no_access' => 'No tienes acceso a %s', @@ -72,7 +72,7 @@ return array( 'already_subscribed' => 'Ya estás suscrito a %s', 'deleted' => 'Fuente eliminada', 'error' => 'No es posible actualizar la fuente', - 'internal_problem' => 'No ha sido posible agregar la fuente RSS. Revisa el registro de FressRSS para más información.', // @todo + 'internal_problem' => 'No ha sido posible agregar la fuente RSS. Revisa el registro de FreshRSS para más información.', // @todo 'invalid_url' => 'La URL %s es inválida', 'marked_read' => 'Fuentes marcadas como leídas', 'n_actualized' => 'Se han actualiado %d fuentes', diff --git a/app/i18n/he/feedback.php b/app/i18n/he/feedback.php index f86b37244..4b79b0d45 100644 --- a/app/i18n/he/feedback.php +++ b/app/i18n/he/feedback.php @@ -32,11 +32,11 @@ return array( 'extensions' => array( 'already_enabled' => '%s is already enabled', // @todo 'disable' => array( - 'ko' => '%s cannot be disabled. Check FressRSS logs for details.', // @todo + 'ko' => '%s cannot be disabled. Check FreshRSS logs for details.', // @todo 'ok' => '%s is now disabled', // @todo ), 'enable' => array( - 'ko' => '%s cannot be enabled. Check FressRSS logs for details.', // @todo + 'ko' => '%s cannot be enabled. Check FreshRSS logs for details.', // @todo 'ok' => '%s is now enabled', // @todo ), 'no_access' => 'You have no access on %s', // @todo diff --git a/app/i18n/kr/feedback.php b/app/i18n/kr/feedback.php index 4fd7c899f..f13675778 100644 --- a/app/i18n/kr/feedback.php +++ b/app/i18n/kr/feedback.php @@ -31,11 +31,11 @@ return array( 'extensions' => array( 'already_enabled' => '%s 확장 기능은 이미 활성화되어 있습니다', 'disable' => array( - 'ko' => '%s 확장 기능을 비활성화 할 수 없습니다. 자세한 내용은 FressRSS 로그를 참고하세요.', + 'ko' => '%s 확장 기능을 비활성화 할 수 없습니다. 자세한 내용은 FreshRSS 로그를 참고하세요.', 'ok' => '%s 확장 기능이 비활성화되었습니다', ), 'enable' => array( - 'ko' => '%s 확장 기능을 활성화 할 수 없습니다. 자세한 내용은 FressRSS 로그를 참고하세요.', + 'ko' => '%s 확장 기능을 활성화 할 수 없습니다. 자세한 내용은 FreshRSS 로그를 참고하세요.', 'ok' => '%s 확장 기능이 활성화되었습니다', ), 'no_access' => '%s 확장 기능에 접근 권한이 없습니다', @@ -72,7 +72,7 @@ return array( 'already_subscribed' => '이미 %s 피드를 구독 중입니다', 'deleted' => '피드가 삭제되었습니다', 'error' => '피드를 변경할 수 없습니다', - 'internal_problem' => 'RSS 피드를 추가할 수 없습니다. 자세한 내용은 FressRSS 로그를 참고하세요.', // @todo + 'internal_problem' => 'RSS 피드를 추가할 수 없습니다. 자세한 내용은 FreshRSS 로그를 참고하세요.', // @todo 'invalid_url' => 'URL (%s)이 유효하지 않습니다', 'marked_read' => '피드가 읽음으로 표시되었습니다', 'n_actualized' => '%d 개의 피드에서 새 글을 가져왔습니다', diff --git a/app/i18n/nl/feedback.php b/app/i18n/nl/feedback.php index 54636ab4b..e73f2f7bd 100644 --- a/app/i18n/nl/feedback.php +++ b/app/i18n/nl/feedback.php @@ -31,11 +31,11 @@ return array( 'extensions' => array( 'already_enabled' => '%s is al ingeschakeld', 'disable' => array( - 'ko' => '%s kan niet worden uitgeschakeld. Controleer FressRSS log bestanden voor details.', + 'ko' => '%s kan niet worden uitgeschakeld. Controleer FreshRSS log bestanden voor details.', 'ok' => '%s is nu uitgeschakeld', ), 'enable' => array( - 'ko' => '%s kan niet worden ingeschakeld. Controleer FressRSS log bestanden voor details.', + 'ko' => '%s kan niet worden ingeschakeld. Controleer FreshRSS log bestanden voor details.', 'ok' => '%s is nn ingeschakeld', ), 'no_access' => 'U hebt geen toegang voor %s', @@ -72,7 +72,7 @@ return array( 'already_subscribed' => 'U bent al geabonneerd op %s', 'deleted' => 'Feed is verwijderd', 'error' => 'Feed kan niet worden vernieuwd', - 'internal_problem' => 'De feed kon niet worden toegevoegd. Controleer de FressRSS-logbestanden voor details. Toevoegen forceren kan worden geprobeerd door #force_feed aan de URL toe te voegen.', + 'internal_problem' => 'De feed kon niet worden toegevoegd. Controleer de FreshRSS-logbestanden voor details. Toevoegen forceren kan worden geprobeerd door #force_feed aan de URL toe te voegen.', 'invalid_url' => 'URL %s is ongeldig', 'marked_read' => 'Feeds zijn gemarkeerd als gelezen', 'n_actualized' => '%d feeds zijn vernieuwd', diff --git a/app/i18n/pt-br/feedback.php b/app/i18n/pt-br/feedback.php index 0e115f2cf..2057cf985 100644 --- a/app/i18n/pt-br/feedback.php +++ b/app/i18n/pt-br/feedback.php @@ -31,11 +31,11 @@ return array( 'extensions' => array( 'already_enabled' => '%s já está habilitado', 'disable' => array( - 'ko' => '%s não pode ser desabilitado. verifique os logs do FressRSS para detalhes.', + 'ko' => '%s não pode ser desabilitado. verifique os logs do FreshRSS para detalhes.', 'ok' => '%s agora está desabilitado', ), 'enable' => array( - 'ko' => '%s não pode ser habilitado. verifique os logs do FressRSS para detalhes.', + 'ko' => '%s não pode ser habilitado. verifique os logs do FreshRSS para detalhes.', 'ok' => '%s agora está habilitado', ), 'no_access' => 'Você não tem acesso ao %s', @@ -72,7 +72,7 @@ return array( 'already_subscribed' => 'Você já está inscrito no %s', 'deleted' => 'o Feed foi deletado', 'error' => 'O feed não pode ser atualizado', - 'internal_problem' => 'O RSS feed não pôde ser adicionado. Verifique os FressRSS logs para detalhes.', // @todo + 'internal_problem' => 'O RSS feed não pôde ser adicionado. Verifique os FreshRSS logs para detalhes.', // @todo 'invalid_url' => 'URL %s é inválida', 'marked_read' => 'Feeds foram marcados como lidos', 'n_actualized' => '%d feeds foram atualizados', diff --git a/app/i18n/ru/feedback.php b/app/i18n/ru/feedback.php index ad4ff958f..6d4e5e3fe 100644 --- a/app/i18n/ru/feedback.php +++ b/app/i18n/ru/feedback.php @@ -31,11 +31,11 @@ return array( 'extensions' => array( 'already_enabled' => '%s is already enabled', //TODO 'disable' => array( - 'ko' => '%s cannot be disabled. Check FressRSS logs for details.', //TODO + 'ko' => '%s cannot be disabled. Check FreshRSS logs for details.', //TODO 'ok' => '%s is now disabled', //TODO ), 'enable' => array( - 'ko' => '%s cannot be enabled. Check FressRSS logs for details.', //TODO + 'ko' => '%s cannot be enabled. Check FreshRSS logs for details.', //TODO 'ok' => '%s is now enabled', //TODO ), 'no_access' => 'You have no access on %s', //TODO @@ -72,7 +72,7 @@ return array( 'already_subscribed' => 'You have already subscribed to %s', //TODO 'deleted' => 'Feed has been deleted', //TODO 'error' => 'Feed cannot be updated', //TODO - 'internal_problem' => 'The newsfeed could not be added. Check FressRSS logs for details. You can try force adding by appending #force_feed to the URL.', //TODO + 'internal_problem' => 'The newsfeed could not be added. Check FreshRSS logs for details. You can try force adding by appending #force_feed to the URL.', //TODO 'invalid_url' => 'URL %s is invalid', //TODO 'marked_read' => 'Feeds have been marked as read', //TODO 'n_actualized' => '%d feeds have been updated', //TODO diff --git a/app/i18n/tr/feedback.php b/app/i18n/tr/feedback.php index 7e9fdd229..df7d1b264 100644 --- a/app/i18n/tr/feedback.php +++ b/app/i18n/tr/feedback.php @@ -31,11 +31,11 @@ return array( 'extensions' => array( 'already_enabled' => '%s zaten aktif', 'disable' => array( - 'ko' => '%s gösterilemiyor. Detaylar için FressRSS log kayıtlarını kontrol edin.', + 'ko' => '%s gösterilemiyor. Detaylar için FreshRSS log kayıtlarını kontrol edin.', 'ok' => '%s pasif', ), 'enable' => array( - 'ko' => '%s aktifleştirilemiyor. Detaylar için FressRSS log kayıtlarını kontrol edin.', + 'ko' => '%s aktifleştirilemiyor. Detaylar için FreshRSS log kayıtlarını kontrol edin.', 'ok' => '%s aktif', ), 'no_access' => '%s de yetkiniz yok', @@ -72,7 +72,7 @@ return array( 'already_subscribed' => '%s için zaten aboneliğiniz bulunmakta', 'deleted' => 'Akış silindi', 'error' => 'Akış güncellenemiyor', - 'internal_problem' => 'RSS akışı eklenemiyor. Detaylar için FressRSS log kayıtlarını kontrol edin.', // @todo + 'internal_problem' => 'RSS akışı eklenemiyor. Detaylar için FreshRSS log kayıtlarını kontrol edin.', // @todo 'invalid_url' => 'URL %s geçersiz', 'marked_read' => 'Akışlar okundu olarak işaretlendi', 'n_actualized' => '%d akışları güncellendi', diff --git a/app/i18n/tr/install.php b/app/i18n/tr/install.php index d5564297b..4ae0ad7e3 100644 --- a/app/i18n/tr/install.php +++ b/app/i18n/tr/install.php @@ -103,7 +103,7 @@ return array( 'fix_errors_before' => 'Lütfen sonraki adıma geçmek için hataları düzeltin.', 'javascript_is_better' => 'FreshRSS JavaScript ile daha işlevseldir', 'js' => array( - 'confirm_reinstall' => 'FressRSS i yeniden kurarak önceki yapılandırma ayarlarınızı kaybedeceksiniz. Devam etmek istiyor musunuz ?', + 'confirm_reinstall' => 'FreshRSS i yeniden kurarak önceki yapılandırma ayarlarınızı kaybedeceksiniz. Devam etmek istiyor musunuz ?', ), 'language' => array( '_' => 'Dil', diff --git a/app/i18n/zh-cn/feedback.php b/app/i18n/zh-cn/feedback.php index 0021308bd..1db879891 100644 --- a/app/i18n/zh-cn/feedback.php +++ b/app/i18n/zh-cn/feedback.php @@ -31,11 +31,11 @@ return array( 'extensions' => array( 'already_enabled' => '%s 已启用', 'disable' => array( - 'ko' => '%s 禁用失败。检查 FressRSS 日志 查看详情。', + 'ko' => '%s 禁用失败。检查 FreshRSS 日志 查看详情。', 'ok' => '%s 现已禁用', ), 'enable' => array( - 'ko' => '%s 启用失败。检查 FressRSS 日志 查看详情。', + 'ko' => '%s 启用失败。检查 FreshRSS 日志 查看详情。', 'ok' => '%s 现已禁用', ), 'no_access' => '你无权访问 %s', @@ -72,7 +72,7 @@ return array( 'already_subscribed' => '你已订阅 %s', 'deleted' => 'RSS 源已删除', 'error' => 'RSS 源更新失败', - 'internal_problem' => 'RSS 源添加失败。检查 FressRSS 日志 查看详情。', // @todo + 'internal_problem' => 'RSS 源添加失败。检查 FreshRSS 日志 查看详情。', // @todo 'invalid_url' => 'URL %s 无效', 'marked_read' => 'RSS 源已被设为已读', 'n_actualized' => '%d 个 RSS 源已更新', -- cgit v1.2.3 From 4c8f1472382e9bf4f5d0cea0d9fe886c7bbe971b Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 31 May 2018 19:22:02 +0200 Subject: Suppress XML error during conversion attempt (#1909) Using LIBXML_NOERROR. Seems to be necessary for newer PHP versions because the at sign does not suppress warnings. In a FreshRSS-specific SimplePie section. Warning: DOMDocument::loadXML(): Namespace prefix media on content is not defined in Entity, line: 42 in /..../FreshRSS/lib/SimplePie/SimplePie/Parser.php on line 144 --- lib/SimplePie/SimplePie/Parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SimplePie/SimplePie/Parser.php b/lib/SimplePie/SimplePie/Parser.php index 9348382ad..bdcc1a516 100644 --- a/lib/SimplePie/SimplePie/Parser.php +++ b/lib/SimplePie/SimplePie/Parser.php @@ -141,7 +141,7 @@ class SimplePie_Parser $dom = new DOMDocument(); $dom->recover = true; $dom->strictErrorChecking = false; - @$dom->loadXML($data); + @$dom->loadXML($data, LIBXML_NOERROR | LIBXML_NOWARNING); $this->encoding = $encoding = $dom->encoding = 'UTF-8'; $data2 = $dom->saveXML(); if (function_exists('mb_convert_encoding')) -- cgit v1.2.3 From fa3532dc8eec13edaff0a9c9fe145236a0eccde3 Mon Sep 17 00:00:00 2001 From: Girish Ramakrishnan Date: Fri, 1 Jun 2018 14:19:24 -0700 Subject: Use realpath of EXTENSIONS_PATH (#1911) This handles the case where the extensions directory might be a symlink --- p/ext.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/p/ext.php b/p/ext.php index bb16d02d3..427bdc253 100644 --- a/p/ext.php +++ b/p/ext.php @@ -19,13 +19,14 @@ require(__DIR__ . '/../constants.php'); */ function is_valid_path($path) { // It must be under the extension path. - $in_ext_path = (substr($path, 0, strlen(EXTENSIONS_PATH)) === EXTENSIONS_PATH); + $real_ext_path = realpath(EXTENSIONS_PATH); + $in_ext_path = (substr($path, 0, strlen($real_ext_path)) === $real_ext_path); if (!$in_ext_path) { return false; } // File to serve must be under a `ext_dir/static/` directory. - $path_relative_to_ext = substr($path, strlen(EXTENSIONS_PATH) + 1); + $path_relative_to_ext = substr($path, strlen($real_ext_path) + 1); $path_splitted = explode('/', $path_relative_to_ext); if (count($path_splitted) < 3 || $path_splitted[1] !== 'static') { return false; -- cgit v1.2.3 From e4775bf30db3d86e56101c9eaa1d7bc8a7a2676c Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 1 Jun 2018 23:20:29 +0200 Subject: Fever API: Log messages for wrong credendials (#1910) --- p/api/fever.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/p/api/fever.php b/p/api/fever.php index 749116183..6c9e2085d 100644 --- a/p/api/fever.php +++ b/p/api/fever.php @@ -18,7 +18,7 @@ Minz_Configuration::register('system', DATA_PATH . '/config.php', FRESHRSS_PATH // check if API is enabled globally FreshRSS_Context::$system_conf = Minz_Configuration::get('system'); if (!FreshRSS_Context::$system_conf->api_enabled) { - Minz_Log::warning('serviceUnavailable() ' . debugInfo(), API_LOG); + Minz_Log::warning('Fever API: serviceUnavailable() ' . debugInfo(), API_LOG); header('HTTP/1.1 503 Service Unavailable'); header('Content-Type: text/plain; charset=UTF-8'); die('Service Unavailable!'); @@ -146,13 +146,19 @@ class FeverAPI $username = @file_get_contents(DATA_PATH . '/fever/.key-' . sha1(FreshRSS_Context::$system_conf->salt) . '-' . $feverKey . '.txt', false); if ($username != false) { $username = trim($username); + Minz_Session::_param('currentUser', $username); $user_conf = get_user_configuration($username); if ($user_conf != null && $feverKey === $user_conf->feverKey) { FreshRSS_Context::$user_conf = $user_conf; - Minz_Session::_param('currentUser', $username); + return true; } + Minz_Log::error('Fever API: Reset API password for user: ' . $username, API_LOG); + Minz_Log::error('Fever API: Please reset your API password!'); + Minz_Session::_param('currentUser'); } + Minz_Log::warning('Fever API: wrong credentials! ' . $feverKey, API_LOG); } + return false; } /** @@ -616,7 +622,7 @@ class FeverAPI // ================================================================================================ // refresh is not allowed yet, probably we find a way to support it later if (isset($_REQUEST["refresh"])) { - Minz_Log::warning('Refresh items for fever API - notImplemented()', API_LOG); + Minz_Log::warning('Fever API: Refresh items - notImplemented()', API_LOG); header('HTTP/1.1 501 Not Implemented'); header('Content-Type: text/plain; charset=UTF-8'); die('Not Implemented!'); -- cgit v1.2.3 From 15f0ad645265d652c69c298121d7c0a37c06718d Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 1 Jun 2018 23:35:03 +0200 Subject: Changelog 1903, 1907, 1909, 1911 https://github.com/FreshRSS/FreshRSS/pull/1903 https://github.com/FreshRSS/FreshRSS/pull/1907 https://github.com/FreshRSS/FreshRSS/pull/1909 https://github.com/FreshRSS/FreshRSS/pull/1911 --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 759f5eaaf..5f1ad1b0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # FreshRSS changelog -## 2018-05-XX FreshRSS 1.10.3-dev +## 2018-06-0X FreshRSS 1.10.3-dev * API * Add support for Fever compatible API, enabling more clients [#1406](https://github.com/FreshRSS/FreshRSS/pull/1406) @@ -26,6 +26,7 @@ * Fix muted feeds that were not actually muted [#1844](https://github.com/FreshRSS/FreshRSS/issues/1844) * Fix null exception in shares, showing only the first article [#1824](https://github.com/FreshRSS/FreshRSS/issues/1824) * Fix error during import[#1890](https://github.com/FreshRSS/FreshRSS/issues/1890) + * Fix additional automatic sequence bug with PostgreSQL [#1907](https://github.com/FreshRSS/FreshRSS/pull/1907) * Fix errors in case of empty/wrong username when updating user settings [#1857](https://github.com/FreshRSS/FreshRSS/pull/1857) * Fixes in subscription menu [#1858](https://github.com/FreshRSS/FreshRSS/pull/1858) * Fix allowing Unix sockets for MySQL and PostgreSQL [#1888](https://github.com/FreshRSS/FreshRSS/issues/1888) @@ -33,6 +34,7 @@ * SimplePie * Work-around for feeds with invalid non-unique GUIDs [#1887](https://github.com/FreshRSS/FreshRSS/pull/1887) * Fix for Atom feeds using a namespace for type [#1892](https://github.com/FreshRSS/FreshRSS/issues/1892) + * Remove some warnings during parsing attemps of some bad feeds [#1909](https://github.com/FreshRSS/FreshRSS/pull/1909) * Security * Strip HTTP credentials from HTTP Referer in SimplePie [#1891](https://github.com/FreshRSS/FreshRSS/pull/1891) * Use `autocomplete="new-password"` to prevent form autocomplete in user management pages (fix bug with e.g. Firefox) [#1877](https://github.com/FreshRSS/FreshRSS/pull/1877) @@ -41,8 +43,10 @@ * I18n * Improve i18n tools [#1829](https://github.com/FreshRSS/FreshRSS/pull/1829) * Updated German [#1856](https://github.com/FreshRSS/FreshRSS/pull/1856) + * Updated Dutch [#1903](https://github.com/FreshRSS/FreshRSS/pull/1903) * Misc. * Add error log information when SQLite has not enough temp space [#1816](https://github.com/FreshRSS/FreshRSS/issues/1816) + * Allow extension dir to be a symlink [#1911](https://github.com/FreshRSS/FreshRSS/pull/1911) ## 2018-03-09 FreshRSS 1.10.2 (Docker only) -- cgit v1.2.3 From ccc62b0a2cb41663ebee7b33601c5fb1d000d4cb Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 2 Jun 2018 23:08:09 +0200 Subject: Use cURL for GET full content (#1913) * Use cURL for GET full content Fix https://github.com/FreshRSS/FreshRSS/issues/1870 * Changelog 1870 https://github.com/FreshRSS/FreshRSS/issues/1870 https://github.com/FreshRSS/FreshRSS/pull/1913 --- CHANGELOG.md | 1 + app/Models/Entry.php | 3 ++- lib/lib_rss.php | 42 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f1ad1b0e..933c831c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ * Updated German [#1856](https://github.com/FreshRSS/FreshRSS/pull/1856) * Updated Dutch [#1903](https://github.com/FreshRSS/FreshRSS/pull/1903) * Misc. + * Use cURL for fetching full articles content [#1870](https://github.com/FreshRSS/FreshRSS/issues/1870) * Add error log information when SQLite has not enough temp space [#1816](https://github.com/FreshRSS/FreshRSS/issues/1816) * Allow extension dir to be a symlink [#1911](https://github.com/FreshRSS/FreshRSS/pull/1911) diff --git a/app/Models/Entry.php b/app/Models/Entry.php index 0ad3781e5..c6b26a7cc 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -193,7 +193,8 @@ class FreshRSS_Entry extends Minz_Model { try { // l'article n'est pas en BDD, on va le chercher sur le site $this->content = get_content_by_parsing( - htmlspecialchars_decode($this->link(), ENT_QUOTES), $pathEntries + htmlspecialchars_decode($this->link(), ENT_QUOTES), $pathEntries, + $this->feed->attributes() ); } catch (Exception $e) { // rien à faire, on garde l'ancien contenu(requête a échoué) diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 9dfca385d..abb20f16a 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -254,11 +254,47 @@ function sanitizeHTML($data, $base = '') { } /* permet de récupérer le contenu d'un article pour un flux qui n'est pas complet */ -function get_content_by_parsing ($url, $path) { +function get_content_by_parsing($url, $path, $attributes = array()) { require_once(LIB_PATH . '/lib_phpQuery.php'); + $system_conf = Minz_Configuration::get('system'); + $limits = $system_conf->limits; + $feed_timeout = empty($attributes['timeout']) ? 0 : intval($attributes['timeout']); + + if ($system_conf->simplepie_syslog_enabled) { + syslog(LOG_INFO, 'FreshRSS GET ' . SimplePie_Misc::url_remove_credentials($url)); + } + + $ch = curl_init(); + curl_setopt_array($ch, array( + CURLOPT_URL => $url, + CURLOPT_REFERER => SimplePie_Misc::url_remove_credentials($url), + CURLOPT_HTTPHEADER => array('Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'), + CURLOPT_USERAGENT => FRESHRSS_USERAGENT, + CURLOPT_CONNECTTIMEOUT => $feed_timeout > 0 ? $feed_timeout : $limits['timeout'], + CURLOPT_TIMEOUT => $feed_timeout > 0 ? $feed_timeout : $limits['timeout'], + //CURLOPT_FAILONERROR => true; + CURLOPT_MAXREDIRS => 4, + CURLOPT_RETURNTRANSFER => true, + )); + if (version_compare(PHP_VERSION, '5.6.0') >= 0 || ini_get('open_basedir') == '') { + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); //Keep option separated for open_basedir PHP bug 65646 + } + if (defined('CURLOPT_ENCODING')) { + curl_setopt($ch, CURLOPT_ENCODING, ''); //Enable all encodings + } + curl_setopt_array($ch, $system_conf->curl_options); + if (isset($attributes['ssl_verify'])) { + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $attributes['ssl_verify'] ? 2 : 0); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $attributes['ssl_verify'] ? true : false); + } + $html = curl_exec($ch); + $c_status = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $c_error = curl_error($ch); + curl_close($ch); - Minz_Log::notice('FreshRSS GET ' . SimplePie_Misc::url_remove_credentials($url)); - $html = file_get_contents($url); + if ($c_status != 200 || $c_error != '') { + Minz_Log::warning('Error fetching content: HTTP code ' . $c_status . ': ' . $c_error . ' ' . $url); + } if ($html) { $doc = phpQuery::newDocument($html); -- cgit v1.2.3 From 4b9f0710ebad4b07da1d5e95eced5771bf576847 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 3 Jun 2018 13:14:42 +0200 Subject: A bit of API documentation (#1914) https://github.com/FreshRSS/FreshRSS/issues/1754 --- CHANGELOG.md | 2 +- README.fr.md | 6 +++--- README.md | 6 +++--- docs/en/users/06_Fever_API.md | 3 +++ docs/en/users/06_Mobile_access.md | 34 ++++++++++++++++++++++++++++++++++ docs/fr/users/06_Fever_API.md | 2 ++ docs/fr/users/06_Mobile_access.md | 36 +++++++++++++++++++++++++++++++++++- 7 files changed, 81 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 933c831c6..39bf723d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ * Fix Docker bug affecting Apache `CustomLog` (unwanted local copy of access logs), `ErrorLog`, `Listen` (IPv6 bug) [#1873](https://github.com/FreshRSS/FreshRSS/pull/1873) * Fix muted feeds that were not actually muted [#1844](https://github.com/FreshRSS/FreshRSS/issues/1844) * Fix null exception in shares, showing only the first article [#1824](https://github.com/FreshRSS/FreshRSS/issues/1824) - * Fix error during import[#1890](https://github.com/FreshRSS/FreshRSS/issues/1890) + * Fix error during import [#1890](https://github.com/FreshRSS/FreshRSS/issues/1890) * Fix additional automatic sequence bug with PostgreSQL [#1907](https://github.com/FreshRSS/FreshRSS/pull/1907) * Fix errors in case of empty/wrong username when updating user settings [#1857](https://github.com/FreshRSS/FreshRSS/pull/1857) * Fixes in subscription menu [#1858](https://github.com/FreshRSS/FreshRSS/pull/1858) diff --git a/README.fr.md b/README.fr.md index 2cdab269b..78a182d7b 100644 --- a/README.fr.md +++ b/README.fr.md @@ -186,10 +186,10 @@ Voir notre [documentation sur l’API Fever](https://freshrss.github.io/FreshRSS Tout client supportant une API de type Fever ; Sélection : * iOS - * [Fiery Feeds](https://itunes.apple.com/app/fiery-feeds-rss-reader/id1158763303) - * [Unread](https://itunes.apple.com/app/unread-rss-reader/id1252376153) + * [Fiery Feeds](https://itunes.apple.com/app/fiery-feeds-rss-reader/id1158763303) (Propriétaire) + * [Unread](https://itunes.apple.com/app/unread-rss-reader/id1252376153) (Propriétaire) * MacOS - * [Readkit](https://itunes.apple.com/app/readkit/id588726889) + * [Readkit](https://itunes.apple.com/app/readkit/id588726889) (Propriétaire) # Bibliothèques incluses diff --git a/README.md b/README.md index 75136e5e7..7d6c51338 100644 --- a/README.md +++ b/README.md @@ -186,10 +186,10 @@ See our [Fever API documentation](https://freshrss.github.io/FreshRSS/en/users/0 Supported clients are: * iOS - * [Fiery Feeds](https://itunes.apple.com/app/fiery-feeds-rss-reader/id1158763303) - * [Unread](https://itunes.apple.com/app/unread-rss-reader/id1252376153) + * [Fiery Feeds](https://itunes.apple.com/app/fiery-feeds-rss-reader/id1158763303) (Closed source) + * [Unread](https://itunes.apple.com/app/unread-rss-reader/id1252376153) (Closed source) * MacOS - * [Readkit](https://itunes.apple.com/app/readkit/id588726889) + * [Readkit](https://itunes.apple.com/app/readkit/id588726889) (Closed source) # Included libraries diff --git a/docs/en/users/06_Fever_API.md b/docs/en/users/06_Fever_API.md index 58e986a64..6d8a103b9 100644 --- a/docs/en/users/06_Fever_API.md +++ b/docs/en/users/06_Fever_API.md @@ -1,5 +1,8 @@ # FreshRSS - Fever API implementation +See the [page about our Google Reader compatible API](06_Mobile_access.md) for another possibility +and general aspects of API access. + ## RSS clients There are many RSS clients existing supporting Fever APIs but they seem to understand the Fever API a bit differently. diff --git a/docs/en/users/06_Mobile_access.md b/docs/en/users/06_Mobile_access.md index 166985585..c354f98f0 100644 --- a/docs/en/users/06_Mobile_access.md +++ b/docs/en/users/06_Mobile_access.md @@ -7,6 +7,9 @@ This page assumes you have completed the [server setup](../admins/02_Installatio * Every user must define an API password. * The reason for an API-specific password is that it may be used in less safe situations than the main password, and does not grant access to as many things. +The rest of this page is about the Google Reader compatible API. +See the [page about the Fever compatible API](06_Fever_API.md) for another possibility. + # Testing @@ -50,3 +53,34 @@ This page assumes you have completed the [server setup](../admins/02_Installatio * [EasyRSS](https://github.com/Alkarex/EasyRSS) (Open source, [F-Droid](https://f-droid.org/packages/org.freshrss.easyrss/)) * Linux * [FeedReader 2.0+](https://jangernert.github.io/FeedReader/) (Open source) + + +# Google Reader compatible API + +Examples of basic queries: + +```sh +# Initial login, using API password (Email and Passwd can be given either as GET, or POST - better) +curl 'https://freshrss.example.net/api/greader.php/accounts/ClientLogin?Email=alice&Passwd=Abcdef123456' +SID=alice/8e6845e089457af25303abc6f53356eb60bdb5f8 +Auth=alice/8e6845e089457af25303abc6f53356eb60bdb5f8 + +# Examples of read-only requests +curl -s -H "Authorization:GoogleLogin auth=alice/8e6845e089457af25303abc6f53356eb60bdb5f8" \ + 'https://freshrss.example.net/api/greader.php/reader/api/0/subscription/list?output=json' + +curl -s -H "Authorization:GoogleLogin auth=alice/8e6845e089457af25303abc6f53356eb60bdb5f8" \ + 'https://freshrss.example.net/api/greader.php/reader/api/0/unread-count?output=json' + +curl -s -H "Authorization:GoogleLogin auth=alice/8e6845e089457af25303abc6f53356eb60bdb5f8" \ + 'https://freshrss.example.net/api/greader.php/reader/api/0/tag/list?output=json' + +# Retrieve a token for requests making modifications +curl -H "Authorization:GoogleLogin auth=alice/8e6845e089457af25303abc6f53356eb60bdb5f8" \ + 'https://freshrss.example.net/api/greader.php/reader/api/0/token' +8e6845e089457af25303abc6f53356eb60bdb5f8ZZZZZZZZZZZZZZZZZ + +# Get articles, piped to jq for easier JSON reading +curl -s -H "Authorization:GoogleLogin auth=alice/8e6845e089457af25303abc6f53356eb60bdb5f8" \ + 'https://freshrss.example.net/api/greader.php/reader/api/0/stream/contents/reading-list' | jq . +``` diff --git a/docs/fr/users/06_Fever_API.md b/docs/fr/users/06_Fever_API.md index f9dcd5d73..6ad817041 100644 --- a/docs/fr/users/06_Fever_API.md +++ b/docs/fr/users/06_Fever_API.md @@ -1,5 +1,7 @@ # FreshRSS - API compatible Fever +Voir la page [sur notre API compatible Google Reader](06_Mobile_access.md) pour une autre possibilité +et des généralités sur l’accès par API. ## Compatibilité diff --git a/docs/fr/users/06_Mobile_access.md b/docs/fr/users/06_Mobile_access.md index 8ef3d038a..b8e7c31f0 100644 --- a/docs/fr/users/06_Mobile_access.md +++ b/docs/fr/users/06_Mobile_access.md @@ -7,6 +7,9 @@ Cette page suppose que vous avez fini [l’installation du serveur](01_Installat * Chaque utilisateur doit choisir son mot de passe API. * La raison d’être d’un mot de passe API différent du mot de passe principal est que le mot de passe API est potentiellement utilisé de manière moins sûre, mais il permet aussi moins de choses. +Le reste de cette page concerne l’API compatible Google Reader. +Voir la [page sur l’API compatible Fever](06_Fever_API.md) pour une autre possibilité. + # Tester @@ -17,7 +20,7 @@ Cette page suppose que vous avez fini [l’installation du serveur](01_Installat * Si vous obtenez un autre message d’erreur, passer à l’étape 5. -# Débogger la configuration du serveur +# Déboguer la configuration du serveur 5. Cliquer sur le second lien “Check partial server configuration (without `%2F` support)”: * Si vous obtenez `PASS`, alors le problème est bien que votre serveur n’accepte pas les slashs `/` qui sont encodés `%2F`. @@ -48,3 +51,34 @@ Tout client supportant une API de type Google Reader. Sélection : * [EasyRSS](https://github.com/Alkarex/EasyRSS) (Libre, F-Droid) * Linux * [FeedReader 2.0+](https://jangernert.github.io/FeedReader/) (Libre) + + +# API compatible Google Reader + +Exemples de requêtes simples: + +```sh +# Authentification utilisant le mot de passe API (Email et Passwd peuvent être passés en GET, ou POST - mieux) +curl 'https://freshrss.example.net/api/greader.php/accounts/ClientLogin?Email=alice&Passwd=Abcdef123456' +SID=alice/8e6845e089457af25303abc6f53356eb60bdb5f8 +Auth=alice/8e6845e089457af25303abc6f53356eb60bdb5f8 + +# Exemples de requêtes en lecture +curl -s -H "Authorization:GoogleLogin auth=alice/8e6845e089457af25303abc6f53356eb60bdb5f8" \ + 'https://freshrss.example.net/api/greader.php/reader/api/0/subscription/list?output=json' + +curl -s -H "Authorization:GoogleLogin auth=alice/8e6845e089457af25303abc6f53356eb60bdb5f8" \ + 'https://freshrss.example.net/api/greader.php/reader/api/0/unread-count?output=json' + +curl -s -H "Authorization:GoogleLogin auth=alice/8e6845e089457af25303abc6f53356eb60bdb5f8" \ + 'https://freshrss.example.net/api/greader.php/reader/api/0/tag/list?output=json' + +# Demande de jeton pour faire de requêtes de modification +curl -H "Authorization:GoogleLogin auth=alice/8e6845e089457af25303abc6f53356eb60bdb5f8" \ + 'https://freshrss.example.net/api/greader.php/reader/api/0/token' +8e6845e089457af25303abc6f53356eb60bdb5f8ZZZZZZZZZZZZZZZZZ + +# Récupère les articles, envoyés à jq pour une lecture JSON plus facile +curl -s -H "Authorization:GoogleLogin auth=alice/8e6845e089457af25303abc6f53356eb60bdb5f8" \ + 'https://freshrss.example.net/api/greader.php/reader/api/0/stream/contents/reading-list' | jq . +``` -- cgit v1.2.3 From be778c6bc2d8075e5a923153183b47507a2a71e3 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 3 Jun 2018 13:29:01 +0200 Subject: Release FreshRSS 1.11.0 https://github.com/FreshRSS/FreshRSS/pull/1902 --- CHANGELOG.md | 2 +- constants.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39bf723d5..ec30f7470 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # FreshRSS changelog -## 2018-06-0X FreshRSS 1.10.3-dev +## 2018-06-03 FreshRSS 1.11.0 * API * Add support for Fever compatible API, enabling more clients [#1406](https://github.com/FreshRSS/FreshRSS/pull/1406) diff --git a/constants.php b/constants.php index af676247c..29ee59181 100644 --- a/constants.php +++ b/constants.php @@ -2,7 +2,7 @@ //NB: Do not edit; use ./constants.local.php instead. // -define('FRESHRSS_VERSION', '1.10.3-dev'); +define('FRESHRSS_VERSION', '1.11.0'); define('FRESHRSS_WEBSITE', 'https://freshrss.org'); define('FRESHRSS_WIKI', 'https://freshrss.github.io/FreshRSS/'); -- cgit v1.2.3