New option to automatically mark as read gone articles (#4426)

* New option to automatically mark as read gone articles
Option to automatically and immediately mark as read entries / articles that are no longer provided in their upstream RSS / ATOM / XPath feed

* Reduce SQL queries
Optimisation: Perform cache update only once
This commit is contained in:
Alexandre Alapetite
2022-06-25 11:15:51 +02:00
committed by GitHub
parent 07a52137a9
commit d785ddde2a
26 changed files with 143 additions and 64 deletions

View File

@@ -121,6 +121,7 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
FreshRSS_Context::$user_conf->sort_order = Minz_Request::param('sort_order', 'DESC');
FreshRSS_Context::$user_conf->mark_when = array(
'article' => Minz_Request::param('mark_open_article', false),
'gone' => Minz_Request::param('read_upon_gone', false),
'max_n_unread' => Minz_Request::paramBoolean('enable_keep_max_n_unread') ? Minz_Request::param('keep_max_n_unread', false) : false,
'reception' => Minz_Request::param('mark_upon_reception', false),
'same_title_in_feed' => Minz_Request::paramBoolean('enable_read_when_same_title_in_feed') ?

View File

@@ -503,10 +503,11 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
}
$feedDAO->updateLastUpdate($feed->id(), false, $mtime);
$needFeedCacheRefresh |= ($feed->keepMaxUnread() != false);
$needFeedCacheRefresh |= ($feed->markAsReadUponGone() != false);
if ($needFeedCacheRefresh) {
$feedDAO->updateCachedValues($feed->id());
}
$feed->keepMaxUnread();
if ($entryDAO->inTransaction()) {
$entryDAO->commit();
}

View File

@@ -126,6 +126,7 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
$ttl = FreshRSS_Context::$user_conf->ttl_default;
}
$feed->_attributes('read_upon_gone', Minz_Request::paramTernary('read_upon_gone'));
$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('clear_cache', Minz_Request::paramTernary('clear_cache'));

View File

@@ -649,15 +649,37 @@ class FreshRSS_Feed extends Minz_Model {
$this->nbPendingNotRead += $n;
}
/**
* Remember to call updateCachedValue($id_feed) or updateCachedValues() just after.
* @return int|false the number of lines affected, or false if not applicable
*/
public function keepMaxUnread() {
$keepMaxUnread = $this->attributes('keep_max_n_unread');
if ($keepMaxUnread == false) {
if ($keepMaxUnread === null) {
$keepMaxUnread = FreshRSS_Context::$user_conf->mark_when['max_n_unread'];
}
if ($keepMaxUnread > 0 && $this->nbNotRead(false) + $this->nbPendingNotRead > $keepMaxUnread) {
$feedDAO = FreshRSS_Factory::createFeedDao();
$feedDAO->keepMaxUnread($this->id(), max(0, $keepMaxUnread - $this->nbPendingNotRead));
return $feedDAO->keepMaxUnread($this->id(), max(0, $keepMaxUnread - $this->nbPendingNotRead));
}
return false;
}
/**
* Applies the *mark as read upon gone* policy, if enabled.
* Remember to call updateCachedValue($id_feed) or updateCachedValues() just after.
* @return int|false the number of lines affected, or false if not applicable
*/
public function markAsReadUponGone() {
$readUponGone = $this->attributes('read_upon_gone');
if ($readUponGone === null) {
$readUponGone = FreshRSS_Context::$user_conf->mark_when['gone'];
}
if ($readUponGone) {
$feedDAO = FreshRSS_Factory::createFeedDao();
return $feedDAO->markAsReadUponGone($this->id());
}
return false;
}
/**

View File

@@ -447,7 +447,8 @@ SQL;
}
/**
* @return int|false
* Remember to call updateCachedValues() after calling this function
* @return int|false number of lines affected or false in case of error
*/
public function keepMaxUnread(int $id, int $n) {
//Double SELECT for MySQL workaround ERROR 1093 (HY000)
@@ -461,32 +462,41 @@ WHERE id_feed=:id_feed1 AND is_read=0 AND id <= (SELECT e3.id FROM (
OFFSET :limit) e3)
SQL;
$stm = $this->pdo->prepare($sql);
$stm->bindParam(':id_feed1', $id, PDO::PARAM_INT);
$stm->bindParam(':id_feed2', $id, PDO::PARAM_INT);
$stm->bindParam(':limit', $n, PDO::PARAM_INT);
if (!$stm || !$stm->execute()) {
if (($stm = $this->pdo->prepare($sql)) &&
$stm->bindParam(':id_feed1', $id, PDO::PARAM_INT) &&
$stm->bindParam(':id_feed2', $id, PDO::PARAM_INT) &&
$stm->bindParam(':limit', $n, PDO::PARAM_INT) &&
$stm->execute()) {
return $stm->rowCount();
} else {
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
Minz_Log::error('SQL error keepMaxUnread: ' . json_encode($info));
return false;
}
$affected = $stm->rowCount();
}
if ($affected > 0) {
$sql = 'UPDATE `_feed` '
. 'SET `cache_nbUnreads`=`cache_nbUnreads`-' . $affected
. ' WHERE id=:id';
$stm = $this->pdo->prepare($sql);
$stm->bindParam(':id', $id, PDO::PARAM_INT);
if (!($stm && $stm->execute())) {
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
Minz_Log::error('SQL error keepMaxUnread cache: ' . json_encode($info));
return false;
}
/**
* Remember to call updateCachedValues() after calling this function
* @return int|false number of lines affected or false in case of error
*/
public function markAsReadUponGone(int $id) {
//Double SELECT for MySQL workaround ERROR 1093 (HY000)
$sql = <<<'SQL'
UPDATE `_entry` SET is_read=1
WHERE id_feed=:id_feed1 AND is_read=0 AND `lastSeen` < (SELECT e3.maxlastseen FROM (
SELECT MAX(e2.`lastSeen`) AS maxlastseen FROM `_entry` e2 WHERE e2.id_feed = :id_feed2) e3)
SQL;
if (($stm = $this->pdo->prepare($sql)) &&
$stm->bindParam(':id_feed1', $id, PDO::PARAM_INT) &&
$stm->bindParam(':id_feed2', $id, PDO::PARAM_INT) &&
$stm->execute()) {
return $stm->rowCount();
} else {
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
Minz_Log::error('SQL error markAsReadUponGone: ' . json_encode($info));
return false;
}
return $affected;
}
/**

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => 'když je článek zobrazen',
'keep_max_n_unread' => 'Maximální počet článků, které ponechat jako nepřečtené',
'scroll' => 'během posouvání',
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => 'po obdržení článku',
'when' => 'Označit článek jako přečtený…',
'when_same_title' => 'když shodný název již existuje v top <i>n</i> nejnovějších článcích',

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => 'wenn der Artikel angesehen wird',
'keep_max_n_unread' => 'Max. Anzahl von ungelesenen Artikeln',
'scroll' => 'beim Scrollen bzw. Überspringen',
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => 'beim Empfang des Artikels',
'when' => 'Artikel als gelesen markieren…',
'when_same_title' => 'falls der identische Titel bereit in den <i>n</i> neusten Artikel vorhanden ist.',

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => 'when the article is viewed', // IGNORE
'keep_max_n_unread' => 'Max number of articles to keep unread', // IGNORE
'scroll' => 'while scrolling', // IGNORE
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => 'upon receiving the article', // IGNORE
'when' => 'Mark an article as read…', // IGNORE
'when_same_title' => 'if an identical title already exists in the top <i>n</i> newest articles', // IGNORE

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => 'when the article is viewed',
'keep_max_n_unread' => 'Max number of articles to keep unread',
'scroll' => 'while scrolling',
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => 'upon receiving the article',
'when' => 'Mark an article as read…',
'when_same_title' => 'if an identical title already exists in the top <i>n</i> newest articles',

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => 'cuando se muestre el artículo',
'keep_max_n_unread' => 'Número máximo de artículos para mantener sin leer',
'scroll' => 'durante el desplazamiento',
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => 'al recibir el artículo',
'when' => 'Marcar el artículo como leído…',
'when_same_title' => 'Si ya existe un título idéntico en la parte superior <i>n</i> artículos más recientes',

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => 'lorsque larticle est affiché',
'keep_max_n_unread' => 'Nombre maximum darticles conservés non lus',
'scroll' => 'au défilement de la page',
'upon_gone' => 'lorsquil nest plus dans le flux dactualités en amont',
'upon_reception' => 'dès la réception du nouvel article',
'when' => 'Marquer un article comme lu…',
'when_same_title' => 'si un même titre existe déjà dans les <i>n</i> articles plus récents',

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => 'כאשר מאמר נצפה',
'keep_max_n_unread' => 'Max number of articles to keep unread', // TODO
'scroll' => 'כאשר גוללים',
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => 'כאשר המאמר מתקבל',
'when' => 'סימון מאמרים כנקראו…',
'when_same_title' => 'if an identical title already exists in the top <i>n</i> newest articles', // TODO

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => 'Quando un articolo viene letto',
'keep_max_n_unread' => 'Max number of articles to keep unread', // TODO
'scroll' => 'Scorrendo la pagina',
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => 'Alla ricezione del contenuto',
'when' => 'Segna articoli come letti…',
'when_same_title' => 'if an identical title already exists in the top <i>n</i> newest articles', // TODO

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => '記事を読んだとき',
'keep_max_n_unread' => '未読の記事として残す最大数',
'scroll' => 'スクロールしているとき',
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => '記事を受け取ったとき',
'when' => '記事を既読にする…',
'when_same_title' => '同一タイトルの新しい記事があるときには、上部へ表示する',

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => '글을 읽었을 때',
'keep_max_n_unread' => '읽지 않은 상태로 유지할 최대 글 개수',
'scroll' => '스크롤을 하며 지나갈 때',
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => '글을 가져오자마자',
'when' => '읽음으로 표시…',
'when_same_title' => '상위 <i>n</i>개의 최신 글에 동일한 제목이 이미 있는 경우',

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => 'als het artikel wordt bekeken',
'keep_max_n_unread' => 'Max aantal artikelen ongelezen houden',
'scroll' => 'tijdens het scrollen',
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => 'bij ontvangst van het artikel',
'when' => 'Markeer artikel als gelezen…',
'when_same_title' => 'als een zelfde titel al voorkomt in de top <i>n</i> nieuwste artikelen',

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => 'quand larticle es mostrat',
'keep_max_n_unread' => 'Nombre max darticles a gardar pas legits',
'scroll' => 'en davalar la pagina',
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => 'en recebre un article novèl',
'when' => 'Marcar un article coma legit…',
'when_same_title' => 'se un títol identic existís ja demest lo <i>n</i> articles mai recents',

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => 'gdy wiadomość jest otworzona',
'keep_max_n_unread' => 'Maksymalna liczba nieprzeczytanych wiadomości',
'scroll' => 'podczas przewijania',
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => 'po otrzymaniu wiadomości',
'when' => 'Oznacz wiadomość jako przeczytaną…',
'when_same_title' => 'gdy identyczny tytuł już istnieje w <i>n</i> najnowszych wiadomościach',

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => 'Quando o artigo é visualizado',
'keep_max_n_unread' => 'Número máximo de artigos para manter como não lido',
'scroll' => 'enquanto scrolling',
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => 'ao receber um artigo',
'when' => 'Marcar artigo como lido…',
'when_same_title' => 'Se um título idêntico já existir nos últimos<i>n</i> artigos mais novos',

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => 'когда статья просматривается',
'keep_max_n_unread' => 'Максимальное количество непрочитанных статей',
'scroll' => 'во время прокрутки',
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => 'по получении статьи',
'when' => 'Отмечать статью прочитанной…',
'when_same_title' => 'если идентичный заголовок уже существует в верхних <i>n</i> новейших статьях',

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => 'keď je článok zobrazený',
'keep_max_n_unread' => 'Maximálny počet článkov ponechať ako neprečítané',
'scroll' => 'počas skrolovania',
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => 'po načítaní článku',
'when' => 'Označiť článok ako prečítaný…',
'when_same_title' => 'ak rovnaký nadpis už existuje v TOP <i>n</i> najnovších článkoch',

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => 'makale görüntülendiğinde',
'keep_max_n_unread' => 'Max number of articles to keep unread', // TODO
'scroll' => 'kaydırma yapılırken',
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => 'makale üzerinde gelince',
'when' => 'Makaleyi okundu olarak işaretle…',
'when_same_title' => 'if an identical title already exists in the top <i>n</i> newest articles', // TODO

View File

@@ -160,6 +160,7 @@ return array(
'article_viewed' => '在文章被浏览后',
'keep_max_n_unread' => '未读最多保留 n 条',
'scroll' => '在滚动浏览后',
'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
'upon_reception' => '在接收文章后',
'when' => '何时将文章标记为已读',
'when_same_title' => '已存在 n 条相同标题文章',

View File

@@ -69,7 +69,7 @@
</div>
</fieldset>
<fieldset>
<legend><?= _t('conf.reading.headline.categories') ?></legend>
<div class="form-group">
@@ -200,21 +200,6 @@
</div>
</div>
<div class="form-group">
<label class="group-name" for="enable_read_when_same_title_in_feed"><?= _t('conf.reading.read.when') ?></label>
<div class="group-controls">
<label class="checkbox" for="enable_read_when_same_title_in_feed">
<input type="checkbox" name="enable_read_when_same_title_in_feed" id="enable_read_when_same_title_in_feed" value="1"<?=
empty(FreshRSS_Context::$user_conf->mark_when['same_title_in_feed']) ? '' : ' checked="checked"' ?>
data-leave-validation="<?= empty(FreshRSS_Context::$user_conf->mark_when['same_title_in_feed']) ? 0 : 1 ?>"/>
<?= _t('conf.reading.read.when_same_title') ?>
<?php $read_when_same_title_in_feed = empty(FreshRSS_Context::$user_conf->mark_when['same_title_in_feed']) ? 25 : FreshRSS_Context::$user_conf->mark_when['same_title_in_feed']; ?>
<input type="number" id="read_when_same_title_in_feed" name="read_when_same_title_in_feed" min="0"
value="<?= $read_when_same_title_in_feed ?>" data-leave-validation="<?= $read_when_same_title_in_feed ?>" />
</label>
</div>
</div>
<div class="form-group">
<label class="group-name" for="check_open_article"><?= _t('conf.reading.read.when') ?></label>
<div class="group-controls">
@@ -236,7 +221,7 @@
data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_when['site'] ?>"/>
<?= _t('conf.reading.read.article_open_on_website') ?>
</label>
</div>
</div>
</div>
<div class="form-group">
@@ -248,7 +233,46 @@
data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_when['scroll'] ?>"/>
<?= _t('conf.reading.read.scroll') ?>
</label>
</div>
</div>
</div>
<div class="form-group">
<label class="group-name" for="enable_read_when_same_title_in_feed"><?= _t('conf.reading.read.when') ?></label>
<div class="group-controls">
<label class="checkbox" for="enable_read_when_same_title_in_feed">
<input type="checkbox" name="enable_read_when_same_title_in_feed" id="enable_read_when_same_title_in_feed" value="1"<?=
empty(FreshRSS_Context::$user_conf->mark_when['same_title_in_feed']) ? '' : ' checked="checked"' ?>
data-leave-validation="<?= empty(FreshRSS_Context::$user_conf->mark_when['same_title_in_feed']) ? 0 : 1 ?>"/>
<?= _t('conf.reading.read.when_same_title') ?>
<?php $read_when_same_title_in_feed = empty(FreshRSS_Context::$user_conf->mark_when['same_title_in_feed']) ? 25 : FreshRSS_Context::$user_conf->mark_when['same_title_in_feed']; ?>
<input type="number" id="read_when_same_title_in_feed" name="read_when_same_title_in_feed" min="0"
value="<?= $read_when_same_title_in_feed ?>" data-leave-validation="<?= $read_when_same_title_in_feed ?>" />
</label>
</div>
</div>
<div class="form-group">
<label class="group-name" for="mark_upon_reception"><?= _t('conf.reading.read.when') ?></label>
<div class="group-controls">
<label class="checkbox" for="mark_upon_reception">
<input type="checkbox" name="mark_upon_reception" id="mark_upon_reception" value="1"<?=
FreshRSS_Context::$user_conf->mark_when['reception'] ? ' checked="checked"' : '' ?>
data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_when['reception'] ?>"/>
<?= _t('conf.reading.read.upon_reception') ?>
</label>
</div>
</div>
<div class="form-group">
<label class="group-name" for="read_upon_gone"><?= _t('conf.reading.read.when') ?></label>
<div class="group-controls">
<label class="checkbox" for="read_upon_gone">
<input type="checkbox" name="read_upon_gone" id="read_upon_gone" value="1"<?=
FreshRSS_Context::$user_conf->mark_when['gone'] ? ' checked="checked"' : '' ?>
data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_when['gone'] ?>"/>
<?= _t('conf.reading.read.upon_gone') ?>
</label>
</div>
</div>
<div class="form-group">
@@ -263,17 +287,6 @@
</label>
</div>
</div>
<div class="form-group">
<div class="group-controls">
<label class="checkbox" for="check_reception">
<input type="checkbox" name="mark_upon_reception" id="check_reception" value="1"<?=
FreshRSS_Context::$user_conf->mark_when['reception'] ? ' checked="checked"' : '' ?>
data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_when['reception'] ?>"/>
<?= _t('conf.reading.read.upon_reception') ?>
</label>
</div>
</div>
</fieldset>
<fieldset>

View File

@@ -186,9 +186,17 @@
</div>
<div class="form-group">
<label class="group-name" for="keep_max_n_unread"><?= _t('conf.reading.read.keep_max_n_unread') ?></label>
<label class="group-name" for="read_when_same_title_in_feed"><?= _t('conf.reading.read.when') ?></label>
<div class="group-controls">
<input type="number" name="keep_max_n_unread" id="keep_max_n_unread" class="w50" min="1" max="10000000" value="<?= $this->feed->attributes('keep_max_n_unread') ?>" placeholder="<?= _t('gen.short.by_default') ?>" />
<select name="read_when_same_title_in_feed" id="read_when_same_title_in_feed" class="w50">
<option value=""<?= $this->feed->attributes('read_when_same_title_in_feed') === null ? ' selected="selected"' : '' ?>><?= _t('gen.short.by_default') ?></option>
<option value="0"<?= $this->feed->attributes('read_when_same_title_in_feed') === false ? ' selected="selected"' : '' ?>><?= _t('gen.short.no') ?></option>
<option value="10"<?= $this->feed->attributes('read_when_same_title_in_feed') == 10 ? ' selected="selected"' : '' ?>>10</option>
<option value="25"<?= $this->feed->attributes('read_when_same_title_in_feed') == 25 ? ' selected="selected"' : '' ?>>25</option>
<option value="100"<?= $this->feed->attributes('read_when_same_title_in_feed') == 100 ? ' selected="selected"' : '' ?>>100</option>
<option value="1000"<?= $this->feed->attributes('read_when_same_title_in_feed') == 1000 ? ' selected="selected"' : '' ?>>1 000</option>
</select>
<?= _t('conf.reading.read.when_same_title') ?>
</div>
</div>
@@ -205,17 +213,14 @@
</div>
<div class="form-group">
<label class="group-name" for="read_when_same_title_in_feed"><?= _t('conf.reading.read.when') ?></label>
<label class="group-name" for="read_upon_gone"><?= _t('conf.reading.read.when') ?></label>
<div class="group-controls">
<select name="read_when_same_title_in_feed" id="read_when_same_title_in_feed" class="w50">
<option value=""<?= $this->feed->attributes('read_when_same_title_in_feed') === null ? ' selected="selected"' : '' ?>><?= _t('gen.short.by_default') ?></option>
<option value="0"<?= $this->feed->attributes('read_when_same_title_in_feed') === false ? ' selected="selected"' : '' ?>><?= _t('gen.short.no') ?></option>
<option value="10"<?= $this->feed->attributes('read_when_same_title_in_feed') == 10 ? ' selected="selected"' : '' ?>>10</option>
<option value="25"<?= $this->feed->attributes('read_when_same_title_in_feed') == 25 ? ' selected="selected"' : '' ?>>25</option>
<option value="100"<?= $this->feed->attributes('read_when_same_title_in_feed') == 100 ? ' selected="selected"' : '' ?>>100</option>
<option value="1000"<?= $this->feed->attributes('read_when_same_title_in_feed') == 1000 ? ' selected="selected"' : '' ?>>1 000</option>
<select name="read_upon_gone" id="read_upon_gone" class="w50">
<option value=""<?= $this->feed->attributes('read_upon_gone') === null ? ' selected="selected"' : '' ?>><?= _t('gen.short.by_default') ?></option>
<option value="0"<?= $this->feed->attributes('read_upon_gone') === false ? ' selected="selected"' : '' ?>><?= _t('gen.short.no') ?></option>
<option value="1"<?= $this->feed->attributes('read_upon_gone') === true ? ' selected="selected"' : '' ?>><?= _t('gen.short.yes') ?></option>
</select>
<?= _t('conf.reading.read.when_same_title') ?>
<?= _t('conf.reading.read.upon_gone') ?>
</div>
</div>
@@ -231,6 +236,13 @@
</div>
</div>
<div class="form-group">
<label class="group-name" for="keep_max_n_unread"><?= _t('conf.reading.read.keep_max_n_unread') ?></label>
<div class="group-controls">
<input type="number" name="keep_max_n_unread" id="keep_max_n_unread" class="w50" min="1" max="10000000" value="<?= $this->feed->attributes('keep_max_n_unread') ?>" placeholder="<?= _t('gen.short.by_default') ?>" />
</div>
</div>
<div class="form-group form-actions">
<div class="group-controls">
<button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button>

View File

@@ -49,6 +49,7 @@ return array (
'anon_access' => false,
'mark_when' => array (
'article' => true,
'gone' => false,
'max_n_unread' => false,
'reception' => false,
'same_title_in_feed' => false,