From b323ed084620cac2222fe1c93ec05b9773eb81e6 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 16 Sep 2018 10:46:27 +0200 Subject: [PATCH] Improve authors (#2025) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Links for authors and multiple authors Favour ';' as a separator instead of ',' to better cope with multi-author scientific articles. Follow-up of https://github.com/FreshRSS/FreshRSS/pull/1997 , https://github.com/FreshRSS/FreshRSS/issues/1968, https://github.com/FreshRSS/FreshRSS/pull/2023 * Change i18n authors * Update layout * Unicode-compatible search Example for `author:Loïc` * author styling * Final details * Minor spacing --- app/Models/Category.php | 2 +- app/Models/Entry.php | 34 +++++++++++++-------- app/Models/Feed.php | 2 +- app/Models/Search.php | 8 ++--- app/i18n/cz/gen.php | 2 +- app/i18n/de/gen.php | 2 +- app/i18n/en/gen.php | 2 +- app/i18n/es/gen.php | 2 +- app/i18n/fr/gen.php | 2 +- app/i18n/he/gen.php | 2 +- app/i18n/it/gen.php | 2 +- app/i18n/kr/gen.php | 2 +- app/i18n/nl/gen.php | 2 +- app/i18n/pt-br/gen.php | 2 +- app/i18n/ru/gen.php | 2 +- app/i18n/tr/gen.php | 2 +- app/i18n/zh-cn/gen.php | 2 +- app/views/configure/display.phtml | 2 +- app/views/helpers/export/articles.phtml | 2 +- app/views/helpers/extension/configure.phtml | 2 +- app/views/index/normal.phtml | 17 ++++++++--- app/views/index/reader.phtml | 16 ++++++++-- app/views/index/rss.phtml | 18 ++++++++--- p/api/fever.php | 9 +++--- p/api/greader.php | 7 +++-- p/scripts/main.js | 6 ++-- 26 files changed, 98 insertions(+), 53 deletions(-) diff --git a/app/Models/Category.php b/app/Models/Category.php index 197faf942..796f39ce1 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -68,7 +68,7 @@ class FreshRSS_Category extends Minz_Model { $this->id = $value; } public function _name($value) { - $this->name = mb_strcut(trim($value), 0, 255, 'UTF-8'); + $this->name = trim($value); } public function _feeds($values) { if (!is_array($values)) { diff --git a/app/Models/Entry.php b/app/Models/Entry.php index 48a0b1bed..09c5d8bcf 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -10,7 +10,7 @@ class FreshRSS_Entry extends Minz_Model { private $id = 0; private $guid; private $title; - private $author; + private $authors; private $content; private $link; private $date; @@ -21,17 +21,16 @@ class FreshRSS_Entry extends Minz_Model { private $feed; private $tags; - public function __construct($feedId = '', $guid = '', $title = '', $author = '', $content = '', + public function __construct($feedId = '', $guid = '', $title = '', $authors = '', $content = '', $link = '', $pubdate = 0, $is_read = false, $is_favorite = false, $tags = '') { $this->_title($title); - $this->_author($author); + $this->_authors($authors); $this->_content($content); $this->_link($link); $this->_date($pubdate); $this->_isRead($is_read); $this->_isFavorite($is_favorite); $this->_feedId($feedId); - $tags = mb_strcut($tags, 0, 1023, 'UTF-8'); $this->_tags($tags); $this->_guid($guid); } @@ -45,8 +44,12 @@ class FreshRSS_Entry extends Minz_Model { public function title() { return $this->title; } - public function author() { - return $this->author === null ? '' : $this->author; + public function authors($asString = false) { + if ($asString) { + return $this->authors == null ? '' : ';' . implode('; ', $this->authors); + } else { + return $this->authors; + } } public function content() { return $this->content; @@ -88,7 +91,7 @@ class FreshRSS_Entry extends Minz_Model { } public function tags($asString = false) { if ($asString) { - return $this->tags == '' ? '' : '#' . implode(' #', $this->tags); + return $this->tags == null ? '' : '#' . implode(' #', $this->tags); } else { return $this->tags; } @@ -97,7 +100,7 @@ class FreshRSS_Entry extends Minz_Model { public function hash() { if ($this->hash === null) { //Do not include $this->date because it may be automatically generated when lacking - $this->hash = md5($this->link . $this->title . $this->author . $this->content . $this->tags(true)); + $this->hash = md5($this->link . $this->title . $this->authors(true) . $this->content . $this->tags(true)); } return $this->hash; } @@ -124,11 +127,18 @@ class FreshRSS_Entry extends Minz_Model { } public function _title($value) { $this->hash = null; - $this->title = mb_strcut($value, 0, 255, 'UTF-8'); + $this->title = $value; } - public function _author($value) { + public function _authors($value) { $this->hash = null; - $this->author = mb_strcut($value, 0, 255, 'UTF-8'); + if (!is_array($value)) { + if (strpos($value, ';') !== false) { + $value = preg_split('/\s*[;]\s*/', $value, -1, PREG_SPLIT_NO_EMPTY); + } else { + $value = preg_split('/\s*[,]\s*/', $value, -1, PREG_SPLIT_NO_EMPTY); + } + } + $this->authors = $value; } public function _content($value) { $this->hash = null; @@ -280,7 +290,7 @@ class FreshRSS_Entry extends Minz_Model { 'id' => $this->id(), 'guid' => $this->guid(), 'title' => $this->title(), - 'author' => $this->author(), + 'author' => $this->authors(true), 'content' => $this->content(), 'link' => $this->link(), 'date' => $this->date(true), diff --git a/app/Models/Feed.php b/app/Models/Feed.php index cc96cde44..d09577e6e 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -420,7 +420,7 @@ class FreshRSS_Feed extends Minz_Model { $author_names = ''; if (is_array($authors)) { foreach ($authors as $author) { - $author_names .= html_only_entity_decode(strip_tags($author->name == '' ? $author->email : $author->name)) . ', '; + $author_names .= html_only_entity_decode(strip_tags($author->name == '' ? $author->email : $author->name)) . '; '; } } $author_names = substr($author_names, 0, -2); diff --git a/app/Models/Search.php b/app/Models/Search.php index c338d63a1..c52e391fa 100644 --- a/app/Models/Search.php +++ b/app/Models/Search.php @@ -141,7 +141,7 @@ class FreshRSS_Search { $this->intitle = $matches['search']; $input = str_replace($matches[0], '', $input); } - if (preg_match_all('/\bintitle:(?P[\w+]*)/', $input, $matches)) { + if (preg_match_all('/\bintitle:(?P[^\s"]*)/', $input, $matches)) { $this->intitle = array_merge($this->intitle ? $this->intitle : array(), $matches['search']); $input = str_replace($matches[0], '', $input); } @@ -155,7 +155,7 @@ class FreshRSS_Search { $this->not_intitle = $matches['search']; $input = str_replace($matches[0], '', $input); } - if (preg_match_all('/[!-]intitle:(?P[\w+]*)/', $input, $matches)) { + if (preg_match_all('/[!-]intitle:(?P[^\s"]*)/', $input, $matches)) { $this->not_intitle = array_merge($this->not_intitle ? $this->not_intitle : array(), $matches['search']); $input = str_replace($matches[0], '', $input); } @@ -179,7 +179,7 @@ class FreshRSS_Search { $this->author = $matches['search']; $input = str_replace($matches[0], '', $input); } - if (preg_match_all('/\bauthor:(?P[\w+]*)/', $input, $matches)) { + if (preg_match_all('/\bauthor:(?P[^\s"]*)/', $input, $matches)) { $this->author = array_merge($this->author ? $this->author : array(), $matches['search']); $input = str_replace($matches[0], '', $input); } @@ -193,7 +193,7 @@ class FreshRSS_Search { $this->not_author = $matches['search']; $input = str_replace($matches[0], '', $input); } - if (preg_match_all('/[!-]author:(?P[\w+]*)/', $input, $matches)) { + if (preg_match_all('/[!-]author:(?P[^\s"]*)/', $input, $matches)) { $this->not_author = array_merge($this->not_author ? $this->not_author : array(), $matches['search']); $input = str_replace($matches[0], '', $input); } diff --git a/app/i18n/cz/gen.php b/app/i18n/cz/gen.php index 66c011da3..b9a65f210 100644 --- a/app/i18n/cz/gen.php +++ b/app/i18n/cz/gen.php @@ -181,7 +181,7 @@ return array( 'short' => array( 'attention' => 'Upozornění!', 'blank_to_disable' => 'Zakázat - ponechte prázdné', - 'by_author' => 'Od %s', + 'by_author' => 'Od:', 'by_default' => 'Výchozí', 'damn' => 'Sakra!', 'default_category' => 'Nezařazeno', diff --git a/app/i18n/de/gen.php b/app/i18n/de/gen.php index eb1e74ed6..26d109328 100644 --- a/app/i18n/de/gen.php +++ b/app/i18n/de/gen.php @@ -180,7 +180,7 @@ return array( 'short' => array( 'attention' => 'Achtung!', 'blank_to_disable' => 'Zum Deaktivieren frei lassen', - 'by_author' => 'Von %s', + 'by_author' => 'Von:', 'by_default' => 'standardmäßig', 'damn' => 'Verdammt!', 'default_category' => 'Unkategorisiert', diff --git a/app/i18n/en/gen.php b/app/i18n/en/gen.php index 34e81af2e..9f7da55a5 100644 --- a/app/i18n/en/gen.php +++ b/app/i18n/en/gen.php @@ -181,7 +181,7 @@ return array( 'short' => array( 'attention' => 'Warning!', 'blank_to_disable' => 'Leave blank to disable', - 'by_author' => 'By %s', + 'by_author' => 'By:', 'by_default' => 'By default', 'damn' => 'Blast!', 'default_category' => 'Uncategorized', diff --git a/app/i18n/es/gen.php b/app/i18n/es/gen.php index 4dc1145b2..fe3d62e2d 100755 --- a/app/i18n/es/gen.php +++ b/app/i18n/es/gen.php @@ -181,7 +181,7 @@ return array( 'short' => array( 'attention' => '¡Aviso!', 'blank_to_disable' => 'Deja en blanco para desactivar', - 'by_author' => 'Por %s', + 'by_author' => 'Por:', 'by_default' => 'Por defecto', 'damn' => '¡Córcholis!', 'default_category' => 'Sin categorizar', diff --git a/app/i18n/fr/gen.php b/app/i18n/fr/gen.php index 13e19283f..1e1cef590 100644 --- a/app/i18n/fr/gen.php +++ b/app/i18n/fr/gen.php @@ -181,7 +181,7 @@ return array( 'short' => array( 'attention' => 'Attention !', 'blank_to_disable' => 'Laissez vide pour désactiver', - 'by_author' => 'Par %s', + 'by_author' => 'Par :', 'by_default' => 'Par défaut', 'damn' => 'Arf !', 'default_category' => 'Sans catégorie', diff --git a/app/i18n/he/gen.php b/app/i18n/he/gen.php index a59f6b178..26b8f99e6 100644 --- a/app/i18n/he/gen.php +++ b/app/i18n/he/gen.php @@ -181,7 +181,7 @@ return array( 'short' => array( 'attention' => 'זהירות!', 'blank_to_disable' => 'יש להשאיר ריק על מנת לנטרל', - 'by_author' => 'מאת %s', + 'by_author' => 'מאת :', 'by_default' => 'ברירת מחדל', 'damn' => 'הו לא!', 'default_category' => 'ללא קטגוריה', diff --git a/app/i18n/it/gen.php b/app/i18n/it/gen.php index 2a90693f9..ab17441e7 100644 --- a/app/i18n/it/gen.php +++ b/app/i18n/it/gen.php @@ -181,7 +181,7 @@ return array( 'short' => array( 'attention' => 'Attenzione!', 'blank_to_disable' => 'Lascia vuoto per disabilitare', - 'by_author' => 'di %s', + 'by_author' => 'di:', 'by_default' => 'predefinito', 'damn' => 'Ops!', 'default_category' => 'Senza categoria', diff --git a/app/i18n/kr/gen.php b/app/i18n/kr/gen.php index e664eaa42..6a461bdac 100644 --- a/app/i18n/kr/gen.php +++ b/app/i18n/kr/gen.php @@ -181,7 +181,7 @@ return array( 'short' => array( 'attention' => '경고!', 'blank_to_disable' => '빈 칸으로 두면 비활성화', - 'by_author' => 'By %s', + 'by_author' => 'By:', 'by_default' => '기본값', 'damn' => '이런!', 'default_category' => '분류 없음', diff --git a/app/i18n/nl/gen.php b/app/i18n/nl/gen.php index ccbd86579..fdc4338c3 100644 --- a/app/i18n/nl/gen.php +++ b/app/i18n/nl/gen.php @@ -181,7 +181,7 @@ return array( 'short' => array( 'attention' => 'Attentie!', 'blank_to_disable' => 'Laat leeg om uit te zetten', - 'by_author' => 'Door %s', + 'by_author' => 'Door:', 'by_default' => 'Door standaard', 'damn' => 'Potverdorie!', 'default_category' => 'Niet ingedeeld', diff --git a/app/i18n/pt-br/gen.php b/app/i18n/pt-br/gen.php index 558482f07..59218597b 100644 --- a/app/i18n/pt-br/gen.php +++ b/app/i18n/pt-br/gen.php @@ -180,7 +180,7 @@ return array( 'short' => array( 'attention' => 'Atencão!', 'blank_to_disable' => 'Deixe em branco para desativar', - 'by_author' => 'Por %s', + 'by_author' => 'Por:', 'by_default' => 'Por padrão', 'damn' => 'Buumm!', 'default_category' => 'Sem categoria', diff --git a/app/i18n/ru/gen.php b/app/i18n/ru/gen.php index 911410a1c..6c8dd2adf 100644 --- a/app/i18n/ru/gen.php +++ b/app/i18n/ru/gen.php @@ -181,7 +181,7 @@ return array( 'short' => array( 'attention' => 'Warning!', 'blank_to_disable' => 'Leave blank to disable', - 'by_author' => 'By %s', + 'by_author' => 'By:', 'by_default' => 'By default', 'damn' => 'Damn!', 'default_category' => 'Uncategorized', diff --git a/app/i18n/tr/gen.php b/app/i18n/tr/gen.php index 2e1761517..b8dc18c01 100644 --- a/app/i18n/tr/gen.php +++ b/app/i18n/tr/gen.php @@ -181,7 +181,7 @@ return array( 'short' => array( 'attention' => 'Tehlike!', 'blank_to_disable' => 'Devredışı bırakmak için boş bırakın', - 'by_author' => '%s tarafından', + 'by_author' => 'Tarafından:', 'by_default' => 'Öntanımlı', 'damn' => 'Hay aksi!', 'default_category' => 'Kategorisiz', diff --git a/app/i18n/zh-cn/gen.php b/app/i18n/zh-cn/gen.php index 4ea2d73ab..078e1d378 100644 --- a/app/i18n/zh-cn/gen.php +++ b/app/i18n/zh-cn/gen.php @@ -181,7 +181,7 @@ return array( 'short' => array( 'attention' => '警告!', 'blank_to_disable' => '留空以禁用', - 'by_author' => '作者 %s', + 'by_author' => '作者', 'by_default' => '默认', 'damn' => '错误!', 'default_category' => '未分类', diff --git a/app/views/configure/display.phtml b/app/views/configure/display.phtml index 414fd2cd6..132b9536c 100644 --- a/app/views/configure/display.phtml +++ b/app/views/configure/display.phtml @@ -39,7 +39,7 @@
-
+
diff --git a/app/views/helpers/export/articles.phtml b/app/views/helpers/export/articles.phtml index 75651483a..b8958f527 100644 --- a/app/views/helpers/export/articles.phtml +++ b/app/views/helpers/export/articles.phtml @@ -34,7 +34,7 @@ foreach ($this->entriesRaw as $entryRaw) { 'id' => $entry->guid(), 'categories' => array_values($entry->tags()), 'title' => $entry->title(), - 'author' => $entry->author(), + 'author' => $entry->authors(true), //TODO: Make an array like tags? 'published' => $entry->date(true), 'updated' => $entry->date(true), 'alternate' => array(array( diff --git a/app/views/helpers/extension/configure.phtml b/app/views/helpers/extension/configure.phtml index 95d968aba..cde872aa0 100644 --- a/app/views/helpers/extension/configure.phtml +++ b/app/views/helpers/extension/configure.phtml @@ -5,7 +5,7 @@ : _t('admin.extensions.disabled'); ?> -

extension->getDescription(); ?> — extension->getAuthor()); ?>

+

extension->getDescription(); ?> — extension->getAuthor(); ?>

entries)) { ?>

entry->title(); ?>

- entry->author(); - echo $author != '' ? '
' . _t('gen.short.by_author', $author) . '
' : '', - $lazyload && $hidePosts ? lazyimg($this->entry->content()) : $this->entry->content(); +
entry->authors(); + if (is_array($authors)): + $first = true; + foreach ($authors as $author): + echo $first ? _t('gen.short.by_author') . ' ' : '· '; + $first = false; + ?> + + +
entry->content()) : $this->entry->content(); ?>
entries)) {

title(); ?>

author(); - echo $author != '' ? _t('gen.short.by_author', $author) . ' — ' : '', - $item->date(); + $authors = $item->authors(); + if (is_array($authors)): + $first = true; + foreach ($authors as $author): + echo $first ? _t('gen.short.by_author') . ' ' : '· '; + $first = false; + ?> + + date(); ?>
content(); ?> diff --git a/app/views/index/rss.phtml b/app/views/index/rss.phtml index 86074517c..104e03d15 100755 --- a/app/views/index/rss.phtml +++ b/app/views/index/rss.phtml @@ -13,10 +13,20 @@ foreach ($this->entries as $item) { <?php echo $item->title(); ?> link(); ?> - author(); ?> - - - + authors(); + if (is_array($authors)) { + foreach ($authors as $author) { + echo "\t\t\t" , '', $author, '', "\n"; + } + } + $categories = $item->tags(); + if (is_array($categories)) { + foreach ($categories as $category) { + echo "\t\t\t" , '', $category, '', "\n"; + } + } + ?> content(); ?>]]> diff --git a/p/api/fever.php b/p/api/fever.php index 55baa6d16..abbade768 100644 --- a/p/api/fever.php +++ b/p/api/fever.php @@ -3,6 +3,7 @@ * Fever API for FreshRSS * Version 0.1 * Author: Kevin Papst / https://github.com/kevinpapst + * Documentation: https://feedafever.com/api * * Inspired by: * TinyTinyRSS Fever API plugin @dasmurphy @@ -63,7 +64,7 @@ class FeverDAO extends Minz_ModelPdo $sql = 'SELECT id, guid, title, author, ' . ($entryDAO->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') - . ', link, date, is_read, is_favorite, id_feed, tags ' + . ', link, date, is_read, is_favorite, id_feed ' . 'FROM `' . $this->prefix . 'entry` WHERE'; if (!empty($entry_ids)) { @@ -495,17 +496,17 @@ class FeverAPI // Load list of extensions and enable the "system" ones. Minz_ExtensionManager::init(); - foreach($entries as $item) { + foreach ($entries as $item) { /** @var FreshRSS_Entry $entry */ $entry = Minz_ExtensionManager::callHook('entry_before_display', $item); - if (is_null($entry)) { + if ($entry == null) { continue; } $items[] = array( 'id' => $entry->id(), 'feed_id' => $entry->feed(false), 'title' => $entry->title(), - 'author' => $entry->author(), + 'author' => $entry->authors(true), 'html' => $entry->content(), 'url' => $entry->link(), 'is_saved' => $entry->isFavorite() ? 1 : 0, diff --git a/p/api/greader.php b/p/api/greader.php index 4affc2826..f5b84f7a1 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -18,6 +18,7 @@ Server-side API compatible with Google Reader API layer 2 * https://github.com/ericmann/gReader-Library/blob/master/greader.class.php * https://github.com/devongovett/reader * https://github.com/theoldreader/api +* https://www.inoreader.com/developers/ */ require(__DIR__ . '/../../constants.php'); @@ -471,6 +472,7 @@ function entriesToArray($entries) { 'categories' => array( 'user/-/state/com.google/reading-list', 'user/-/label/' . $c_name, + //TODO: Add other tags ), 'origin' => array( 'streamId' => 'feed/' . $f_id, @@ -478,8 +480,9 @@ function entriesToArray($entries) { //'htmlUrl' => $line['f_website'], ), ); - if ($entry->author() != '') { - $item['author'] = $entry->author(); + $author = $entry->authors(true); + if ($author != '') { + $item['author'] = $author; } if ($entry->isRead()) { $item['categories'][] = 'user/-/state/com.google/read'; diff --git a/p/scripts/main.js b/p/scripts/main.js index f8d20767a..6ab256065 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -731,7 +731,7 @@ function init_shortcuts() { function init_stream(divStream) { divStream.on('click', '.flux_header,.flux_content', function (e) { //flux_toggle - if ($(e.target).closest('.content, .item.website, .item.link').length > 0) { + if ($(e.target).closest('.keep_unread, .content, .item.website, .item.link, .dropdown-menu').length > 0) { return; } if (!context.sides_close_article && $(e.target).is('div.flux_content')) { @@ -788,7 +788,9 @@ function init_stream(divStream) { }); divStream.on('click', '.flux .content a', function () { - $(this).attr('target', '_blank').attr('rel', 'noreferrer'); + if (!$(this).closest('div').hasClass('author')) { + $(this).attr('target', '_blank').attr('rel', 'noreferrer'); + } }); if (context.auto_mark_site) {