Преглед изворни кода

Fix ensure maximum number of feeds also with Dynamic OPML (#8832)

Fix https://github.com/FreshRSS/FreshRSS/issues/8822
And also add a warning sign in case of error.
Fix related bug about showing errored categories.
Alexandre Alapetite пре 5 дана
родитељ
комит
146d5f0d60

+ 15 - 1
app/Models/Category.php

@@ -222,6 +222,9 @@ class FreshRSS_Category extends Minz_Model {
 			$importService->importOpml($opml, $dryRunCategory, true);
 			if ($importService->lastStatus()) {
 				$feedDAO = FreshRSS_Factory::createFeedDao();
+				$limits = FreshRSS_Context::systemConf()->limits;
+				$maxFeeds = (int)($limits['max_feeds'] ?? 0);
+				$nbFeeds = $maxFeeds > 0 ? $feedDAO->count() : 0;
 
 				/** @var array<string,FreshRSS_Feed> */
 				$dryRunFeeds = [];
@@ -245,8 +248,19 @@ class FreshRSS_Category extends Minz_Model {
 				foreach ($dryRunCategory->feeds() as $dryRunFeed) {
 					if (empty($existingFeeds[$dryRunFeed->url()])) {
 						// The feed does not exist in the current category, so add that feed
+						if ($maxFeeds > 0 && $nbFeeds >= $maxFeeds) {
+							// Respect the per-user maximum number of feeds
+							Minz_Log::warning(_t('feedback.sub.feed.over_max', $maxFeeds) .
+								' (dynamic OPML category ' . $this->id() . ')');
+							$ok = false;
+							break;
+						}
 						$dryRunFeed->_category($this);
-						$ok &= ($feedDAO->addFeedObject($dryRunFeed) !== false);
+						if ($feedDAO->addFeedObject($dryRunFeed) === false) {
+							$ok = false;
+						} else {
+							$nbFeeds++;
+						}
 						$existingFeeds[$dryRunFeed->url()] = $dryRunFeed;
 					} else {
 						$existingFeed = $existingFeeds[$dryRunFeed->url()];

+ 2 - 0
app/Models/CategoryDAO.php

@@ -499,6 +499,8 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo {
 					$feedDao::daoToFeeds($feedsDao, $previousLine['c_id'])
 				);
 				$cat->_kind($previousLine['c_kind']);
+				$cat->_lastUpdate($previousLine['c_last_update'] ?? 0);
+				$cat->_error($previousLine['c_error'] ?? 0);
 				$cat->_attributes($previousLine['c_attributes'] ?? '[]');
 				$list[$cat->id()] = $cat;
 

+ 1 - 0
app/i18n/cs/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Zadejte adresu URL na <a href="http://opml.org/" target="_blank">OPML soubor</a> k dynamickému naplnění této kategorie RSS kanály',
 		),
 		'empty' => 'Vyprázdit kategorii',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Expand category',	// TODO
 		'information' => 'Informace',
 		'open' => 'Open category',	// TODO

+ 1 - 0
app/i18n/de/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'URL zu einer <a href="http://opml.org/" target="_blank">OPML Datei</a>, um die Kategorie dynamisch mit Feeds zu befüllen',
 		),
 		'empty' => 'Leere Kategorie',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Kategorie aufklappen',
 		'information' => 'Information',	// IGNORE
 		'open' => 'Kategorie öffnen',

+ 1 - 0
app/i18n/el/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Provide the URL to an <a href="http://opml.org/" target="_blank">OPML file</a> to dynamically populate this category with feeds',	// TODO
 		),
 		'empty' => 'Empty category',	// TODO
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Expand category',	// TODO
 		'information' => 'Information',	// TODO
 		'open' => 'Open category',	// TODO

+ 1 - 0
app/i18n/en-US/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Provide the URL to an <a href="http://opml.org/" target="_blank">OPML file</a> to dynamically populate this category with feeds',	// IGNORE
 		),
 		'empty' => 'Empty category',	// IGNORE
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// IGNORE
 		'expand' => 'Expand category',	// IGNORE
 		'information' => 'Information',	// IGNORE
 		'open' => 'Open category',	// IGNORE

+ 1 - 0
app/i18n/en/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Provide the URL to an <a href="http://opml.org/" target="_blank">OPML file</a> to dynamically populate this category with feeds',
 		),
 		'empty' => 'Empty category',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',
 		'expand' => 'Expand category',
 		'information' => 'Information',
 		'open' => 'Open category',

+ 1 - 0
app/i18n/es/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Provee la URL a un <a href=http://opml.org/ target="_blank">archivo OPML</a> para llenar dinámicamente esta categoría con fuentes',
 		),
 		'empty' => 'Vaciar categoría',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Expandir categoría',
 		'information' => 'Información',
 		'open' => 'Abrir categoría',

+ 1 - 0
app/i18n/fa/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => ' URL را به <a href="http://opml.org/" target="_blank">فایل OPML</a> ارائه دهید تا به صورت پویا این دسته با فیدها پر شود.',
 		),
 		'empty' => ' دسته خالی',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'دسته‌بندی را گسترش دهید',
 		'information' => ' اطلاعات',
 		'open' => 'دسته‌بندی باز',

+ 1 - 0
app/i18n/fi/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Voit tuoda syötteet tähän luokkaan automaattisesti antamalla <a href="http://opml.org/" target="_blank">OPML-tiedoston</a> URL-osoitteen',
 		),
 		'empty' => 'Tyhjä luokka',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Laajenna luokka',
 		'information' => 'Tiedot',
 		'open' => 'Avaa luokka',

+ 1 - 0
app/i18n/fr/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Fournir l’URL d’un <a href="http://opml.org/" target="_blank">fichier OPML</a> qui donnera dynamiquement la liste des flux de cette catégorie',
 		),
 		'empty' => 'Catégorie vide',
+		'error' => 'Cette catégorie OPML dynamique a rencontré un problème. Vérifiez que l’URL de l’OPML est toujours accessible et que le nombre maximum de flux par utilisateur n’a pas été dépassé.',
 		'expand' => 'Développer la catégorie',
 		'information' => 'Informations',
 		'open' => 'Ouvrir la catégorie',

+ 1 - 0
app/i18n/he/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Provide the URL to an <a href="http://opml.org/" target="_blank">OPML file</a> to dynamically populate this category with feeds',	// TODO
 		),
 		'empty' => 'Empty category',	// TODO
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Expand category',	// TODO
 		'information' => 'מידע',
 		'open' => 'Open category',	// TODO

+ 1 - 0
app/i18n/hu/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Adj meg egy URL-t az <a href="http://opml.org/" target="_blank">OPML fájlhoz</a> hogy automatikusan kitöltődjön ez a kategória hírforrásokkal',
 		),
 		'empty' => 'Üres kategória',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Kategória kinyitása',
 		'information' => 'Információ',
 		'open' => 'Kategória megnyitása',

+ 1 - 0
app/i18n/id/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Sediakan URL ke <a href="http://opml.org/" target="_blank">berkas OPML</a> untuk memasukkan umpan ke kategori ini secara dinamis',
 		),
 		'empty' => 'Kategori kosong',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Kembangkan kategori',
 		'information' => 'Informasi',
 		'open' => 'Buka kategori',

+ 1 - 0
app/i18n/it/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Fornisci l’URL ad un <a href="http://opml.org/" target="_blank">file OPML</a> per popolare dinamicamente questa categoria con i feed',
 		),
 		'empty' => 'Categoria vuota',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Espandi categoria',
 		'information' => 'Informazioni',
 		'open' => 'Aprire la categoria!',

+ 1 - 0
app/i18n/ja/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'このカテゴリに動的フィードを追加するための<a href="http://opml.org/" target="_blank">OPMLファイル</a>のURLを指定します。',
 		),
 		'empty' => '空のカテゴリ',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'カテゴリを展開する',
 		'information' => 'インフォメーション',
 		'open' => 'カテゴリを開く',

+ 1 - 0
app/i18n/ko/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => '이 카테코리에 피드를 동적으로 채우려면 <a href="http://opml.org/" target="_blank">OPML 파일</a> 의 URL을 제공해주세요',
 		),
 		'empty' => '빈 카테고리',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Expand category',	// TODO
 		'information' => '정보',
 		'open' => 'Open category',	// TODO

+ 1 - 0
app/i18n/lv/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Norādiet URL uz <a href="http://opml.org/" target="_blank">OPML failu</a>, lai dinamiski papildinātu šo kategoriju ar barotnēm.',
 		),
 		'empty' => 'Tukša kategorija',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Expand category',	// TODO
 		'information' => 'Informācija',
 		'open' => 'Open category',	// TODO

+ 1 - 0
app/i18n/nl/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Geef de URL naar een <a href="http://opml.org/" target="_blank">OPML-bestand</a> om deze categorie dynamisch met feeds te vullen',
 		),
 		'empty' => 'Lege categorie',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Categorie uitklappen',
 		'information' => 'Informatie',
 		'open' => 'Categorie openen',

+ 1 - 0
app/i18n/oc/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Fornís l’URL per un <a href="http://opml.org/" target="_blank">fichièr OPML</a> per garnir automaticament aquesta categoria amb de flux',
 		),
 		'empty' => 'Categoria voida',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Expand category',	// TODO
 		'information' => 'Informacions',
 		'open' => 'Open category',	// TODO

+ 1 - 0
app/i18n/pl/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Podaj adres <a href="http://opml.org/" target="_blank">pliku OPML</a>, aby dynamicznie zapełnić tę kategorię kanałami',
 		),
 		'empty' => 'Pusta kategoria',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Rozszerz kategorię',
 		'information' => 'Informacje',
 		'open' => 'Otwórz kategorię',

+ 1 - 0
app/i18n/pt-BR/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Forneça uma URL para <a href="http://opml.org/" target="_blank">o arquivo OPML </a> para preencher dinamicamente esta categoria com feeds',
 		),
 		'empty' => 'Categoria vazia',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Expandir categoria',
 		'information' => 'Informações',
 		'open' => 'Abrir categoria',

+ 1 - 0
app/i18n/pt-PT/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Forneça uma URL para <a href="http://opml.org/" target="_blank">o arquivo OPML </a> para preencher dinamicamente esta categoria com feeds',
 		),
 		'empty' => 'Categoria vazia',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Expand category',	// TODO
 		'information' => 'Informações',
 		'open' => 'Open category',	// TODO

+ 1 - 0
app/i18n/ru/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Предоставьте ссылку на <a href="http://opml.org/" target="_blank">OPML файл</a> чтобы динамично заполнять эту категорию лентами',
 		),
 		'empty' => 'Пустая категория',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Развернуть категорию',
 		'information' => 'Информация',
 		'open' => 'Открыть категорию',

+ 1 - 0
app/i18n/sk/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Zadajte URL adresu k <a href="http://opml.org/" target="_blank">OPML súboru</a>, z ktorého sa táto kategória automaticky naplní kanálmi.',
 		),
 		'empty' => 'Prázdna kategória',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Expand category',	// TODO
 		'information' => 'Informácia',
 		'open' => 'Open category',	// TODO

+ 1 - 0
app/i18n/tr/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Bu kategoriyi beslemelerle dinamik olarak doldurmak için bir <a href="http://opml.org/" target="_blank">OPML dosyası</a> URL’si sağlayın.',
 		),
 		'empty' => 'Boş kategori',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Kategoriyi genişlet',
 		'information' => 'Bilgi',
 		'open' => 'Kategoriyi aç',

+ 1 - 0
app/i18n/uk/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => 'Щоб динамічно заповнювати цю категорію стрічками, вкажіть URL-адресу <a href="http://opml.org/" target="_blank">OPML-файлу</a>',
 		),
 		'empty' => 'Порожня категорія',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => 'Розгорнути категорію',
 		'information' => 'Інформація',
 		'open' => 'Відкрити категорію',

+ 1 - 0
app/i18n/zh-CN/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => '使用 URL 上的 <a href="http://opml.org/" target="_blank">OPML 文件</a> 中的订阅源填充这一分类',
 		),
 		'empty' => '空分类',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => '展开分类',
 		'information' => '信息',
 		'open' => '打开分类',

+ 1 - 0
app/i18n/zh-TW/sub.php

@@ -29,6 +29,7 @@ return array(
 			'help' => '提供指向 <a href="http://opml.org/" target="_blank">OPML 檔案</a> 的 URL 以便動態地為此類別填充訂閱',
 		),
 		'empty' => '空類別',
+		'error' => 'This dynamic OPML category has encountered a problem. Check that the OPML URL is still reachable and that the maximum number of feeds per user has not been exceeded.',	// TODO
 		'expand' => '展開類別',
 		'information' => '資訊',
 		'open' => '開啟類別',

+ 3 - 2
app/layout/aside_feed.phtml

@@ -99,8 +99,9 @@
 		<li id="c_<?= $cat->id() ?>" class="tree-folder category<?= $c_active ? ' active' : '' ?>"<?= null === $position ? '' : " data-position='$position'" ?> data-unread="<?= $cat->nbNotRead() ?>">
 			<a href="<?= _url('index', $actual_view, 'get', 'c_' . $cat->id()) . $state_filter_manual ?>" class="tree-folder-title">
 				<button class="dropdown-toggle" title="<?= _t('sub.category.expand') ?>"><?= _i($c_show ? 'up' : 'down') ?></button><?php
-				?><span title="<?= _t('sub.category.open') ?>" class="title<?= $cat->hasFeedsWithError() ? ' error' : '' ?>" data-unread="<?= format_number($cat->nbNotRead()) ?>" ><?=
-					$cat->name() ?><?= $cat->kind() === FreshRSS_Category::KIND_DYNAMIC_OPML ? _i('opml-dyn') : ''
+				?><span title="<?= $cat->inError() ? _t('sub.category.error') : _t('sub.category.open') ?>" class="title<?=
+					$cat->hasFeedsWithError() || $cat->inError() ? ' error' : '' ?>" data-unread="<?= format_number($cat->nbNotRead()) ?>"><?=
+					$cat->name() ?><?= $cat->kind() === FreshRSS_Category::KIND_DYNAMIC_OPML ? ' ' . _i('opml-dyn') : ''
 				?></span>
 			</a>
 

+ 3 - 1
app/views/subscription/index.phtml

@@ -31,7 +31,9 @@
 		<div class="box">
 			<div class="box-title">
 				<a class="configure open-slider" href="<?= _url('category', 'update', 'id', $cat->id()) ?>" title="<?= _t('gen.action.manage') ?>" data-cat-position="<?= $cat->attributeString('position') ?>"><?= _i('configure') ?></a>
-				<h2><?= $cat->name() ?><?= $cat->kind() === FreshRSS_Category::KIND_DYNAMIC_OPML ? ' ' . _i('opml-dyn') : '' ?></h2>
+				<h2><span class="title<?= $cat->inError() ? ' error' : '' ?>"<?=
+					$cat->inError() ? ' title="' . _t('sub.category.error') . '"' : '' ?>><?=$cat->name() ?><?=
+					$cat->kind() === FreshRSS_Category::KIND_DYNAMIC_OPML ? ' ' . _i('opml-dyn') : '' ?></span></h2>
 			</div>
 			<ul class="box-content drop-zone scrollbar-thin" dropzone="move" data-cat-id="<?= $cat->id() ?>">
 				<?php

+ 1 - 1
p/themes/Nord/nord.css

@@ -965,7 +965,7 @@ li.item.active {
 
 /*=== DIVERS */
 /*===========*/
-.category .title.error::before {
+.title.error::before {
 	color: var(--red);
 }
 

+ 1 - 1
p/themes/Nord/nord.rtl.css

@@ -965,7 +965,7 @@ li.item.active {
 
 /*=== DIVERS */
 /*===========*/
-.category .title.error::before {
+.title.error::before {
 	color: var(--red);
 }
 

+ 5 - 7
p/themes/Swage/swage.css

@@ -210,13 +210,11 @@ form {
 	}
 }
 
-.category {
-	.title.error::before {
-		display: inline-block;
-		padding-right: 7px;
-		width: 16px;
-		content: url(../Swage/icons/error.svg);
-	}
+.title.error::before {
+	display: inline-block;
+	padding-right: 7px;
+	width: 16px;
+	content: url(../Swage/icons/error.svg);
 }
 
 .form-group {

+ 5 - 7
p/themes/Swage/swage.rtl.css

@@ -210,13 +210,11 @@ form {
 	}
 }
 
-.category {
-	.title.error::before {
-		display: inline-block;
-		padding-left: 7px;
-		width: 16px;
-		content: url(../Swage/icons/error.svg);
-	}
+.title.error::before {
+	display: inline-block;
+	padding-left: 7px;
+	width: 16px;
+	content: url(../Swage/icons/error.svg);
 }
 
 .form-group {

+ 2 - 2
p/themes/base-theme/frss.css

@@ -150,7 +150,7 @@ h2 {
 h2 .icon,
 legend .icon {
 	height: 0.8em;
-	vertical-align: baseline;
+	vertical-align: middle;
 }
 
 h3 {
@@ -242,7 +242,7 @@ img.favicon.upload {
 	font-style: italic;
 }
 
-.category .title.error::before,
+.title.error::before,
 .item.feed.error .item-title .title::before,
 .box-content .item.feed.error .item-title::before {
 	content: " ⚠ ";

+ 2 - 2
p/themes/base-theme/frss.rtl.css

@@ -150,7 +150,7 @@ h2 {
 h2 .icon,
 legend .icon {
 	height: 0.8em;
-	vertical-align: baseline;
+	vertical-align: middle;
 }
 
 h3 {
@@ -242,7 +242,7 @@ img.favicon.upload {
 	font-style: italic;
 }
 
-.category .title.error::before,
+.title.error::before,
 .item.feed.error .item-title .title::before,
 .box-content .item.feed.error .item-title::before {
 	content: " ⚠ ";