diff options
| author | 2021-08-30 10:58:06 +0200 | |
|---|---|---|
| committer | 2021-08-30 10:58:06 +0200 | |
| commit | 50ba6bbe07b0bb86eb07e3212ba2e22cb2878cf2 (patch) | |
| tree | 3e54faf8e06be88468d0be1e9df808f88691194e | |
| parent | 812eda1fa05e370c4c1645b5b82f09f9da2c7bf7 (diff) | |
UI: Add optional thumbnail and summary on feed items (#3805)
* UI: Add optional thumbnail and summary on feed items
Implements #561
* UI: Thumbnail: Own column, Custom size, Lazy load
* UI: Thumbnail: Remove unnecessary CSS rule
Remove rule already defined in base theme, no override needed
* CSS lint + RTL
* Improve thumbail and summary generation
* Support img alt
* Missing htmlspecialchars
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
28 files changed, 418 insertions, 5 deletions
diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 7415546e6..388c34624 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -48,6 +48,8 @@ class FreshRSS_configure_Controller extends Minz_ActionController { FreshRSS_Context::$user_conf->topline_favorite = Minz_Request::param('topline_favorite', false); FreshRSS_Context::$user_conf->topline_date = Minz_Request::param('topline_date', false); FreshRSS_Context::$user_conf->topline_link = Minz_Request::param('topline_link', false); + FreshRSS_Context::$user_conf->topline_thumbnail = Minz_Request::param('topline_thumbnail', false); + FreshRSS_Context::$user_conf->topline_summary = Minz_Request::param('topline_summary', false); FreshRSS_Context::$user_conf->topline_display_authors = Minz_Request::param('topline_display_authors', false); FreshRSS_Context::$user_conf->bottomline_read = Minz_Request::param('bottomline_read', false); FreshRSS_Context::$user_conf->bottomline_favorite = Minz_Request::param('bottomline_favorite', false); diff --git a/app/Models/ConfigurationSetter.php b/app/Models/ConfigurationSetter.php index 6fc056eb2..ee58ebe5b 100644 --- a/app/Models/ConfigurationSetter.php +++ b/app/Models/ConfigurationSetter.php @@ -254,6 +254,16 @@ class FreshRSS_ConfigurationSetter { private function _topline_read(&$data, $value) { $data['topline_read'] = $this->handleBool($value); } + private function _topline_thumbnail(&$data, $value) { + $value = strtolower($value); + if (!in_array($value, array('none', 'portrait', 'square', 'landscape'))) { + $value = 'none'; + } + $data['topline_thumbnail'] = $value; + } + private function _topline_summary(&$data, $value) { + $data['topline_summary'] = $this->handleBool($value); + } private function _topline_display_authors(&$data, $value) { $data['topline_display_authors'] = $this->handleBool($value); } diff --git a/app/Models/Entry.php b/app/Models/Entry.php index 9057cecf8..66e554e22 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -59,13 +59,19 @@ class FreshRSS_Entry extends Minz_Model { public function content() { return $this->content; } - public function enclosures() { + + public function enclosures($searchBodyImages = false) { $results = []; try { - if (strpos($this->content, '<p class="enclosure-content') !== false) { + $searchEnclosures = strpos($this->content, '<p class="enclosure-content') !== false; + $searchBodyImages &= (stripos($this->content, '<img') !== false); + $xpath = null; + if ($searchEnclosures || $searchBodyImages) { $dom = new DOMDocument(); - $dom->loadHTML($this->content, LIBXML_NONET | LIBXML_NOERROR | LIBXML_NOWARNING); + $dom->loadHTML('<?xml version="1.0" encoding="UTF-8" ?>' . $this->content, LIBXML_NONET | LIBXML_NOERROR | LIBXML_NOWARNING); $xpath = new DOMXpath($dom); + } + if ($searchEnclosures) { $enclosures = $xpath->query('//div[@class="enclosure"]/p[@class="enclosure-content"]/*[@src]'); foreach ($enclosures as $enclosure) { $results[] = [ @@ -75,12 +81,36 @@ class FreshRSS_Entry extends Minz_Model { ]; } } + if ($searchBodyImages) { + $images = $xpath->query('//img'); + foreach ($images as $img) { + $src = $img->getAttribute('src'); + if ($src == null) { + $src = $img->getAttribute('data-src'); + } + if ($src != null) { + $results[] = [ + 'url' => $src, + 'alt' => $img->getAttribute('alt'), + ]; + } + } + } return $results; } catch (Exception $ex) { return $results; } } + public function thumbnail() { + foreach ($this->enclosures(true) as $enclosure) { + if (!empty($enclosure['url']) && empty($enclosure['type'])) { + return $enclosure; + } + } + return null; + } + public function link() { return $this->link; } diff --git a/app/i18n/cz/conf.php b/app/i18n/cz/conf.php index 2d8f499cd..fa744ef71 100644 --- a/app/i18n/cz/conf.php +++ b/app/i18n/cz/conf.php @@ -25,6 +25,7 @@ return array( '_' => 'Zobrazení', 'icon' => array( 'bottom_line' => 'Spodní řádek', + 'summary' => 'Summary', // TODO - Translation 'display_authors' => 'Authors', // TODO - Translation 'entry' => 'Ikony článků', 'publication_date' => 'Datum vydání', @@ -47,6 +48,13 @@ return array( 'no_limit' => 'Bez limitu', 'thin' => 'Tenká', ), + 'thumbnail' => array( + 'label' => 'Thumbnail', // TODO - Translation + 'none' => 'None', // TODO - Translation + 'portrait' => 'Portrait', // TODO - Translation + 'square' => 'Square', // TODO - Translation + 'landscape' => 'Landscape', // TODO - Translation + ), ), 'profile' => array( '_' => 'Správa profilu', diff --git a/app/i18n/de/conf.php b/app/i18n/de/conf.php index 3a1ffb5c8..460506f23 100644 --- a/app/i18n/de/conf.php +++ b/app/i18n/de/conf.php @@ -25,6 +25,7 @@ return array( '_' => 'Anzeige', 'icon' => array( 'bottom_line' => 'Fußzeile', + 'summary' => 'Summary', // TODO - Translation 'display_authors' => 'Autoren', 'entry' => 'Artikel-Symbole', 'publication_date' => 'Datum der Veröffentlichung', @@ -47,6 +48,13 @@ return array( 'no_limit' => 'Keine Begrenzung', 'thin' => 'Klein', ), + 'thumbnail' => array( + 'label' => 'Thumbnail', // TODO - Translation + 'none' => 'None', // TODO - Translation + 'portrait' => 'Portrait', // TODO - Translation + 'square' => 'Square', // TODO - Translation + 'landscape' => 'Landscape', // TODO - Translation + ), ), 'profile' => array( '_' => 'Profil-Verwaltung', diff --git a/app/i18n/en-us/conf.php b/app/i18n/en-us/conf.php index facd3d08c..caea7eff9 100644 --- a/app/i18n/en-us/conf.php +++ b/app/i18n/en-us/conf.php @@ -25,6 +25,7 @@ return array( '_' => 'Display', 'icon' => array( 'bottom_line' => 'Bottom line', + 'summary' => 'Summary', 'display_authors' => 'Authors', 'entry' => 'Article icons', 'publication_date' => 'Date of publication', @@ -47,6 +48,13 @@ return array( 'no_limit' => 'Full Width', 'thin' => 'Narrow', ), + 'thumbnail' => array( + 'label' => 'Thumbnail', + 'none' => 'None', + 'portrait' => 'Portrait', + 'square' => 'Square', + 'landscape' => 'Landscape', + ), ), 'profile' => array( '_' => 'Profile management', diff --git a/app/i18n/en/conf.php b/app/i18n/en/conf.php index 188cf811b..121ed0177 100644 --- a/app/i18n/en/conf.php +++ b/app/i18n/en/conf.php @@ -25,6 +25,7 @@ return array( '_' => 'Display', 'icon' => array( 'bottom_line' => 'Bottom line', + 'summary' => 'Summary', 'display_authors' => 'Authors', 'entry' => 'Article icons', 'publication_date' => 'Date of publication', @@ -47,6 +48,13 @@ return array( 'no_limit' => 'Full Width', 'thin' => 'Narrow', ), + 'thumbnail' => array( + 'label' => 'Thumbnail', + 'none' => 'None', + 'portrait' => 'Portrait', + 'square' => 'Square', + 'landscape' => 'Landscape', + ), ), 'profile' => array( '_' => 'Profile management', diff --git a/app/i18n/es/conf.php b/app/i18n/es/conf.php index 7eca55278..1bacd8fba 100755 --- a/app/i18n/es/conf.php +++ b/app/i18n/es/conf.php @@ -25,6 +25,7 @@ return array( '_' => 'Visualización', 'icon' => array( 'bottom_line' => 'Línea inferior', + 'summary' => 'Summary', // TODO - Translation 'display_authors' => 'Authors', // TODO - Translation 'entry' => 'Iconos de artículos', 'publication_date' => 'Fecha de publicación', @@ -47,6 +48,13 @@ return array( 'no_limit' => 'Sin límite', 'thin' => 'Estrecho', ), + 'thumbnail' => array( + 'label' => 'Thumbnail', // TODO - Translation + 'none' => 'None', // TODO - Translation + 'portrait' => 'Portrait', // TODO - Translation + 'square' => 'Square', // TODO - Translation + 'landscape' => 'Landscape', // TODO - Translation + ), ), 'profile' => array( '_' => 'Administración de perfiles', diff --git a/app/i18n/fr/conf.php b/app/i18n/fr/conf.php index 464632637..d4a967412 100644 --- a/app/i18n/fr/conf.php +++ b/app/i18n/fr/conf.php @@ -25,6 +25,7 @@ return array( '_' => 'Affichage', 'icon' => array( 'bottom_line' => 'Ligne du bas', + 'summary' => 'Résumé', 'display_authors' => 'Auteurs', 'entry' => 'Icônes d’article', 'publication_date' => 'Date de publication', @@ -47,6 +48,13 @@ return array( 'no_limit' => 'Pas de limite', 'thin' => 'Fine', ), + 'thumbnail' => array( + 'label' => 'Miniature', + 'none' => 'Sans', + 'portrait' => 'Portrait', + 'square' => 'Carrée', + 'landscape' => 'Paysage', + ), ), 'profile' => array( '_' => 'Gestion du profil', diff --git a/app/i18n/he/conf.php b/app/i18n/he/conf.php index 8d09732b2..64f7d7f47 100644 --- a/app/i18n/he/conf.php +++ b/app/i18n/he/conf.php @@ -25,6 +25,7 @@ return array( '_' => 'תצוגה', 'icon' => array( 'bottom_line' => 'שורה תחתונה', + 'summary' => 'Summary', // TODO - Translation 'display_authors' => 'Authors', // TODO - Translation 'entry' => 'סמלילי מאמרים', 'publication_date' => 'תאריך הפרסום', @@ -47,6 +48,13 @@ return array( 'no_limit' => 'ללא הגבלה', 'thin' => 'צר', ), + 'thumbnail' => array( + 'label' => 'Thumbnail', // TODO - Translation + 'none' => 'None', // TODO - Translation + 'portrait' => 'Portrait', // TODO - Translation + 'square' => 'Square', // TODO - Translation + 'landscape' => 'Landscape', // TODO - Translation + ), ), 'profile' => array( '_' => 'Profile management', // TODO - Translation diff --git a/app/i18n/it/conf.php b/app/i18n/it/conf.php index 74abf6ec1..78bfd743e 100644 --- a/app/i18n/it/conf.php +++ b/app/i18n/it/conf.php @@ -25,6 +25,7 @@ return array( '_' => 'Visualizzazione', 'icon' => array( 'bottom_line' => 'Barra in fondo', + 'summary' => 'Summary', // TODO - Translation 'display_authors' => 'Authors', // TODO - Translation 'entry' => 'Icone degli articoli', 'publication_date' => 'Data di pubblicazione', @@ -47,6 +48,13 @@ return array( 'no_limit' => 'Nessun limite', 'thin' => 'Stretto', ), + 'thumbnail' => array( + 'label' => 'Thumbnail', // TODO - Translation + 'none' => 'None', // TODO - Translation + 'portrait' => 'Portrait', // TODO - Translation + 'square' => 'Square', // TODO - Translation + 'landscape' => 'Landscape', // TODO - Translation + ), ), 'profile' => array( '_' => 'Gestione profili', diff --git a/app/i18n/kr/conf.php b/app/i18n/kr/conf.php index 2d6f55d43..597107c6f 100644 --- a/app/i18n/kr/conf.php +++ b/app/i18n/kr/conf.php @@ -25,6 +25,7 @@ return array( '_' => '표시', 'icon' => array( 'bottom_line' => '하단', + 'summary' => 'Summary', // TODO - Translation 'display_authors' => 'Authors', // TODO - Translation 'entry' => '문서 아이콘', 'publication_date' => '발행일', @@ -47,6 +48,13 @@ return array( 'no_limit' => '제한 없음', 'thin' => '얇음', ), + 'thumbnail' => array( + 'label' => 'Thumbnail', // TODO - Translation + 'none' => 'None', // TODO - Translation + 'portrait' => 'Portrait', // TODO - Translation + 'square' => 'Square', // TODO - Translation + 'landscape' => 'Landscape', // TODO - Translation + ), ), 'profile' => array( '_' => '프로필 관리', diff --git a/app/i18n/nl/conf.php b/app/i18n/nl/conf.php index b6dea0184..c7b5bf441 100644 --- a/app/i18n/nl/conf.php +++ b/app/i18n/nl/conf.php @@ -25,6 +25,7 @@ return array( '_' => 'Opmaak', 'icon' => array( 'bottom_line' => 'Onderaan', + 'summary' => 'Summary', // TODO - Translation 'display_authors' => 'Auteurs', 'entry' => 'Artikel pictogrammen', 'publication_date' => 'Publicatie datum', @@ -47,6 +48,13 @@ return array( 'no_limit' => 'Geen limiet', 'thin' => 'Smal', ), + 'thumbnail' => array( + 'label' => 'Thumbnail', // TODO - Translation + 'none' => 'None', // TODO - Translation + 'portrait' => 'Portrait', // TODO - Translation + 'square' => 'Square', // TODO - Translation + 'landscape' => 'Landscape', // TODO - Translation + ), ), 'profile' => array( '_' => 'Profielbeheer', diff --git a/app/i18n/oc/conf.php b/app/i18n/oc/conf.php index 0a1444d71..e645a5b68 100644 --- a/app/i18n/oc/conf.php +++ b/app/i18n/oc/conf.php @@ -25,6 +25,7 @@ return array( '_' => 'Afichatge', 'icon' => array( 'bottom_line' => 'Linha enbàs', + 'summary' => 'Summary', // TODO - Translation 'display_authors' => 'Autors', 'entry' => 'Icònas d’article', 'publication_date' => 'Data de publicacion', @@ -47,6 +48,13 @@ return array( 'no_limit' => 'Cap de limit', 'thin' => 'Fina', ), + 'thumbnail' => array( + 'label' => 'Thumbnail', // TODO - Translation + 'none' => 'None', // TODO - Translation + 'portrait' => 'Portrait', // TODO - Translation + 'square' => 'Square', // TODO - Translation + 'landscape' => 'Landscape', // TODO - Translation + ), ), 'profile' => array( '_' => 'Gestion del perfil', diff --git a/app/i18n/pl/conf.php b/app/i18n/pl/conf.php index b67e76afb..c6e337320 100644 --- a/app/i18n/pl/conf.php +++ b/app/i18n/pl/conf.php @@ -25,6 +25,7 @@ return array( '_' => 'Wyświetlanie', 'icon' => array( 'bottom_line' => 'Dolny margines', + 'summary' => 'Summary', // TODO - Translation 'display_authors' => 'Autorzy', 'entry' => 'Ikony wiadomości', 'publication_date' => 'Data publikacji', @@ -47,6 +48,13 @@ return array( 'no_limit' => 'Pełna szerokość', 'thin' => 'Wąska', ), + 'thumbnail' => array( + 'label' => 'Thumbnail', // TODO - Translation + 'none' => 'None', // TODO - Translation + 'portrait' => 'Portrait', // TODO - Translation + 'square' => 'Square', // TODO - Translation + 'landscape' => 'Landscape', // TODO - Translation + ), ), 'profile' => array( '_' => 'Zarządzanie profilem', diff --git a/app/i18n/pt-br/conf.php b/app/i18n/pt-br/conf.php index 7c7c2e39f..3b10f07cd 100644 --- a/app/i18n/pt-br/conf.php +++ b/app/i18n/pt-br/conf.php @@ -25,6 +25,7 @@ return array( '_' => 'Exibição', 'icon' => array( 'bottom_line' => 'Linha inferior', + 'summary' => 'Summary', // TODO - Translation 'display_authors' => 'Autores', 'entry' => 'Ícones de artigos', 'publication_date' => 'Data da publicação', @@ -47,6 +48,13 @@ return array( 'no_limit' => 'Sem limite', 'thin' => 'Fino', ), + 'thumbnail' => array( + 'label' => 'Thumbnail', // TODO - Translation + 'none' => 'None', // TODO - Translation + 'portrait' => 'Portrait', // TODO - Translation + 'square' => 'Square', // TODO - Translation + 'landscape' => 'Landscape', // TODO - Translation + ), ), 'profile' => array( '_' => 'Gerenciamento de perfil', diff --git a/app/i18n/ru/conf.php b/app/i18n/ru/conf.php index e113fd44b..b4bbd658a 100644 --- a/app/i18n/ru/conf.php +++ b/app/i18n/ru/conf.php @@ -25,6 +25,7 @@ return array( '_' => 'Отображение', 'icon' => array( 'bottom_line' => 'Нижняя линия', + 'summary' => 'Summary', // TODO - Translation 'display_authors' => 'Авторы', 'entry' => 'Иконки статей', 'publication_date' => 'Дата публикации', @@ -47,6 +48,13 @@ return array( 'no_limit' => 'Во всю ширину', 'thin' => 'Узкое', ), + 'thumbnail' => array( + 'label' => 'Thumbnail', // TODO - Translation + 'none' => 'None', // TODO - Translation + 'portrait' => 'Portrait', // TODO - Translation + 'square' => 'Square', // TODO - Translation + 'landscape' => 'Landscape', // TODO - Translation + ), ), 'profile' => array( '_' => 'Настройки профиля', diff --git a/app/i18n/sk/conf.php b/app/i18n/sk/conf.php index bff87015f..1bd41da97 100644 --- a/app/i18n/sk/conf.php +++ b/app/i18n/sk/conf.php @@ -25,6 +25,7 @@ return array( '_' => 'Zobrazenie', 'icon' => array( 'bottom_line' => 'Spodný riadok', + 'summary' => 'Summary', // TODO - Translation 'display_authors' => 'Autori', 'entry' => 'Ikony článku', 'publication_date' => 'Dátum zverejnenia', @@ -47,6 +48,13 @@ return array( 'no_limit' => 'Bez obmedzenia', 'thin' => 'Úzka', ), + 'thumbnail' => array( + 'label' => 'Thumbnail', // TODO - Translation + 'none' => 'None', // TODO - Translation + 'portrait' => 'Portrait', // TODO - Translation + 'square' => 'Square', // TODO - Translation + 'landscape' => 'Landscape', // TODO - Translation + ), ), 'profile' => array( '_' => 'Správca profilu', diff --git a/app/i18n/tr/conf.php b/app/i18n/tr/conf.php index 0cfe9b0ff..6d51d4e42 100644 --- a/app/i18n/tr/conf.php +++ b/app/i18n/tr/conf.php @@ -25,6 +25,7 @@ return array( '_' => 'Görünüm', 'icon' => array( 'bottom_line' => 'Alt çizgi', + 'summary' => 'Summary', // TODO - Translation 'display_authors' => 'Yazarlar', 'entry' => 'Makale ikonları', 'publication_date' => 'Yayınlama Tarihi', @@ -47,6 +48,13 @@ return array( 'no_limit' => 'Sınırsız', 'thin' => 'Zayıf', ), + 'thumbnail' => array( + 'label' => 'Thumbnail', // TODO - Translation + 'none' => 'None', // TODO - Translation + 'portrait' => 'Portrait', // TODO - Translation + 'square' => 'Square', // TODO - Translation + 'landscape' => 'Landscape', // TODO - Translation + ), ), 'profile' => array( '_' => 'Profil yönetimi', diff --git a/app/i18n/zh-cn/conf.php b/app/i18n/zh-cn/conf.php index 03ca671db..75df77a45 100644 --- a/app/i18n/zh-cn/conf.php +++ b/app/i18n/zh-cn/conf.php @@ -25,6 +25,7 @@ return array( '_' => '显示', 'icon' => array( 'bottom_line' => '底栏', + 'summary' => 'Summary', // TODO - Translation 'display_authors' => '作者', 'entry' => '文章图标', 'publication_date' => '更新日期', @@ -47,6 +48,13 @@ return array( 'no_limit' => '无限制', 'thin' => '窄', ), + 'thumbnail' => array( + 'label' => 'Thumbnail', // TODO - Translation + 'none' => 'None', // TODO - Translation + 'portrait' => 'Portrait', // TODO - Translation + 'square' => 'Square', // TODO - Translation + 'landscape' => 'Landscape', // TODO - Translation + ), ), 'profile' => array( '_' => '用户管理', diff --git a/app/views/configure/display.phtml b/app/views/configure/display.phtml index 483793722..efeb189aa 100644 --- a/app/views/configure/display.phtml +++ b/app/views/configure/display.phtml @@ -75,6 +75,27 @@ </div> </div> + <?php $topline_thumbnail = FreshRSS_Context::$user_conf->topline_thumbnail; ?> + <div class="form-group"> + <label class="group-name" for="topline_thumbnail"><?= _t('conf.display.thumbnail.label') ?></label> + <div class="group-controls"> + <select name="topline_thumbnail" id="topline_thumbnail" required="" data-leave-validation="<?= $topline_thumbnail ?>"> + <option value="none" <?= $topline_thumbnail === 'none' ? 'selected="selected"' : '' ?>> + <?= _t('conf.display.thumbnail.none') ?> + </option> + <option value="portrait" <?= $topline_thumbnail === 'portrait' ? 'selected="selected"' : '' ?>> + <?= _t('conf.display.thumbnail.portrait') ?> + </option> + <option value="square" <?= $topline_thumbnail === 'square' ? 'selected="selected"' : '' ?>> + <?= _t('conf.display.thumbnail.square') ?> + </option> + <option value="landscape" <?= $topline_thumbnail === 'landscape' ? 'selected="selected"' : '' ?>> + <?= _t('conf.display.thumbnail.landscape') ?> + </option> + </select> + </div> + </div> + <div class="form-group"> <label class="group-name"><?= _t('conf.display.icon.entry') ?></label> <div class="group-controls"> @@ -86,6 +107,7 @@ <th title="<?= _t('gen.action.mark_favorite') ?>"><?= _i('bookmark') ?></th> <th><?= _t('conf.display.icon.related_tags') ?></th> <th><?= _t('conf.display.icon.sharing') ?></th> + <th><?= _t('conf.display.icon.summary') ?></th> <th><?= _t('conf.display.icon.display_authors') ?></th> <th><?= _t('conf.display.icon.publication_date') ?></th> <th><?= _i('link') ?></th> @@ -102,6 +124,9 @@ data-leave-validation="<?= FreshRSS_Context::$user_conf->topline_favorite ?>"/></td> <td><input type="checkbox" disabled="disabled" /></td> <td><input type="checkbox" disabled="disabled" /></td> + <td><input type="checkbox" name="topline_summary" value="1"<?= + FreshRSS_Context::$user_conf->topline_summary ? 'checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::$user_conf->topline_summary ?>"/></td> <td><input type="checkbox" name="topline_display_authors" value="1"<?= FreshRSS_Context::$user_conf->topline_display_authors ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->topline_display_authors ?>"/></td> @@ -125,6 +150,7 @@ FreshRSS_Context::$user_conf->bottomline_sharing ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->bottomline_sharing ?>"/></td> <td><input type="checkbox" disabled="disabled" /></td> + <td><input type="checkbox" disabled="disabled" /></td> <td><input type="checkbox" name="bottomline_date" value="1"<?= FreshRSS_Context::$user_conf->bottomline_date ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->bottomline_date ?>"/></td> diff --git a/app/views/helpers/index/normal/entry_header.phtml b/app/views/helpers/index/normal/entry_header.phtml index 7ab2ff70b..784a440a0 100644 --- a/app/views/helpers/index/normal/entry_header.phtml +++ b/app/views/helpers/index/normal/entry_header.phtml @@ -1,9 +1,12 @@ <?php $topline_read = FreshRSS_Context::$user_conf->topline_read; $topline_favorite = FreshRSS_Context::$user_conf->topline_favorite; + $topline_thumbnail = FreshRSS_Context::$user_conf->topline_thumbnail; + $topline_summary = FreshRSS_Context::$user_conf->topline_summary; $topline_display_authors = FreshRSS_Context::$user_conf->topline_display_authors; $topline_date = FreshRSS_Context::$user_conf->topline_date; $topline_link = FreshRSS_Context::$user_conf->topline_link; + $lazyload = FreshRSS_Context::$user_conf->lazyload; ?><ul class="horizontal-list flux_header"><?php if (FreshRSS_Auth::hasAccess()) { if ($topline_read) { @@ -29,8 +32,21 @@ } ?><li class="item website"><a href="<?= _url('index', 'index', 'get', 'f_' . $this->feed->id()) ?>" title="<?= _t('gen.action.filter') ?>"> <?php if (FreshRSS_Context::$user_conf->show_favicons): ?><img class="favicon" src="<?= $this->feed->favicon() ?>" alt="✇" loading="lazy" /><?php endif; ?> - <span><?= $this->feed->name() ?></span></a></li> - <li class="item title" dir="auto"><a target="_blank" rel="noreferrer" href="<?= $this->entry->link() ?>"><?= $this->entry->title() ?><?php + <span><?= $this->feed->name() ?></span></a> + </li> + + <?php + if ($topline_thumbnail !== 'none'): + ?><li class="item thumbnail <?= $topline_thumbnail ?> <?= $topline_summary ? '' : 'small' ?>"><?php + $thumbnail = $this->entry->thumbnail(); + if ($thumbnail != null): + ?><img src="<?= htmlspecialchars($thumbnail['url'], ENT_COMPAT, 'UTF-8') ?>"<?= $lazyload ? ' loading="lazy"' : '' ?><?= + empty($thumbnail['alt']) ? '' : ' alt="' . htmlspecialchars(strip_tags($thumbnail['alt']), ENT_COMPAT, 'UTF-8') . '"' ?> /><?php + endif; + ?></li><?php + endif; ?> + + <li class="item title<?= (($topline_thumbnail !== 'none') || $topline_summary) ? ' multiline' : '' ?>" dir="auto"><a target="_blank" rel="noreferrer" href="<?= $this->entry->link() ?>"><?= $this->entry->title() ?><?php if ($topline_display_authors): ?><span class="author"><?php $authors = $this->entry->authors(); @@ -43,6 +59,11 @@ } ?></span><?php endif; + if ($topline_summary): + ?><div class="summary"> + <?= mb_substr(strip_tags($this->entry->content()), 0, 500, 'UTF-8') ?> + </div><?php + endif; ?></a></li> <?php if ($topline_date) { ?><li class="item date"><time datetime="<?= $this->entry->machineReadableDate() ?>"><?= $this->entry->date() ?></time> </li><?php } ?> <?php if ($topline_link) { ?><li class="item link"><a target="_blank" rel="noreferrer" href="<?= $this->entry->link() ?>" title="<?= diff --git a/cli/i18n/ignore/en-us.php b/cli/i18n/ignore/en-us.php index 60c527887..206df6996 100644 --- a/cli/i18n/ignore/en-us.php +++ b/cli/i18n/ignore/en-us.php @@ -159,6 +159,8 @@ return array( 'conf.archiving.ttl', 'conf.display._', 'conf.display.icon.bottom_line', + 'conf.display.icon.thumbnail', + 'conf.display.icon.summary', 'conf.display.icon.display_authors', 'conf.display.icon.entry', 'conf.display.icon.publication_date', diff --git a/config-user.default.php b/config-user.default.php index 71ec4236b..d7831cee9 100644 --- a/config-user.default.php +++ b/config-user.default.php @@ -81,6 +81,8 @@ return array ( 'show_favicons' => true, 'topline_read' => true, 'topline_favorite' => true, + 'topline_thumbnail' => 'none', + 'topline_summary' => false, 'topline_display_authors' => false, 'topline_date' => true, 'topline_link' => true, diff --git a/p/themes/Origine-compact/origine-compact.css b/p/themes/Origine-compact/origine-compact.css index 562881815..24d9a2e63 100644 --- a/p/themes/Origine-compact/origine-compact.css +++ b/p/themes/Origine-compact/origine-compact.css @@ -878,6 +878,43 @@ a.btn, font-size: 0.8rem; } +.flux .item.thumbnail { + padding: 5px; + height: 50px; +} + +.flux .item.thumbnail.small { + height: 30px; +} + +.flux .item.thumbnail.portrait { + width: 38px; +} + +.flux .item.thumbnail.square { + width: 50px; +} + +.flux .item.thumbnail.landscape { + width: 80px; +} + +.flux .item.thumbnail.portrait.small { + width: 20px; +} + +.flux .item.thumbnail.square.small { + width: 30px; +} + +.flux .item.thumbnail.landscape.small { + width: 40px; +} + +.flux .item.title .summary { + max-height: 1.5em; +} + .flux .website .favicon { padding: 5px; } diff --git a/p/themes/Origine-compact/origine-compact.rtl.css b/p/themes/Origine-compact/origine-compact.rtl.css index 62c7c75bd..ccdcb697f 100644 --- a/p/themes/Origine-compact/origine-compact.rtl.css +++ b/p/themes/Origine-compact/origine-compact.rtl.css @@ -878,6 +878,43 @@ a.btn, font-size: 0.8rem; } +.flux .item.thumbnail { + padding: 5px; + height: 50px; +} + +.flux .item.thumbnail.small { + height: 30px; +} + +.flux .item.thumbnail.portrait { + width: 38px; +} + +.flux .item.thumbnail.square { + width: 50px; +} + +.flux .item.thumbnail.landscape { + width: 80px; +} + +.flux .item.thumbnail.portrait.small { + width: 20px; +} + +.flux .item.thumbnail.square.small { + width: 30px; +} + +.flux .item.thumbnail.landscape.small { + width: 40px; +} + +.flux .item.title .summary { + max-height: 1.5em; +} + .flux .website .favicon { padding: 5px; } diff --git a/p/themes/base-theme/template.css b/p/themes/base-theme/template.css index 77fc8b792..9c2ace776 100644 --- a/p/themes/base-theme/template.css +++ b/p/themes/base-theme/template.css @@ -723,11 +723,66 @@ input[type="search"] { position: absolute; } +.flux:not(.current):hover .item.title.multiline { + position: initial; +} + .flux .item.title a { color: #000; text-decoration: none; } +.flux .item.thumbnail { + line-height: 0; + padding: 10px; + height: 80px; +} + +.flux .item.thumbnail.small { + height: 40px; +} + +.flux .item.thumbnail.portrait { + width: 60px; +} + +.flux .item.thumbnail.square { + width: 80px; +} + +.flux .item.thumbnail.landscape { + width: 128px; +} + +.flux .item.thumbnail.portrait.small { + width: 30px; +} + +.flux .item.thumbnail.square.small { + width: 40px; +} + +.flux .item.thumbnail.landscape.small { + width: 64px; +} + +.flux .item.thumbnail img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.flux .item.title .summary { + max-height: 3em; + color: #666; + font-size: 0.9em; + line-height: 1.5em; + font-weight: normal; + white-space: initial; + overflow: hidden; + text-overflow: ellipsis; +} + .flux .item.title .author { padding-left: 1rem; color: #555; diff --git a/p/themes/base-theme/template.rtl.css b/p/themes/base-theme/template.rtl.css index 3e22c9393..db4c73294 100644 --- a/p/themes/base-theme/template.rtl.css +++ b/p/themes/base-theme/template.rtl.css @@ -723,11 +723,66 @@ input[type="search"] { position: absolute; } +.flux:not(.current):hover .item.title.multiline { + position: initial; +} + .flux .item.title a { color: #000; text-decoration: none; } +.flux .item.thumbnail { + line-height: 0; + padding: 10px; + height: 80px; +} + +.flux .item.thumbnail.small { + height: 40px; +} + +.flux .item.thumbnail.portrait { + width: 60px; +} + +.flux .item.thumbnail.square { + width: 80px; +} + +.flux .item.thumbnail.landscape { + width: 128px; +} + +.flux .item.thumbnail.portrait.small { + width: 30px; +} + +.flux .item.thumbnail.square.small { + width: 40px; +} + +.flux .item.thumbnail.landscape.small { + width: 64px; +} + +.flux .item.thumbnail img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.flux .item.title .summary { + max-height: 3em; + color: #666; + font-size: 0.9em; + line-height: 1.5em; + font-weight: normal; + white-space: initial; + overflow: hidden; + text-overflow: ellipsis; +} + .flux .item.title .author { padding-right: 1rem; color: #555; |
