1
0
Fork 0
mirror of https://github.com/wallabag/wallabag.git synced 2025-07-27 17:28:39 +00:00

Merge remote-tracking branch 'origin/master' into 2.4

# Conflicts:
#	web/wallassets/baggy.js
#	web/wallassets/manifest.json
#	web/wallassets/material.css
#	web/wallassets/material.js
This commit is contained in:
Jeremy Benoist 2018-11-25 09:58:18 +01:00
commit b878be4cc9
No known key found for this signature in database
GPG key ID: BCA73962457ACC3C
40 changed files with 802 additions and 136 deletions

View file

@ -53,6 +53,7 @@ class ContentProxy
if ((empty($content) || false === $this->validateContent($content)) && false === $disableContentUpdate) {
$fetchedContent = $this->graby->fetchContent($url);
$fetchedContent['title'] = $this->sanitizeContentTitle($fetchedContent['title'], $fetchedContent['content_type']);
// when content is imported, we have information in $content
// in case fetching content goes bad, we'll keep the imported information instead of overriding them
@ -65,6 +66,13 @@ class ContentProxy
// so we'll be able to refetch it in the future
$content['url'] = !empty($content['url']) ? $content['url'] : $url;
// In one case (at least in tests), url is empty here
// so we set it using $url provided in the updateEntry call.
// Not sure what are the other possible cases where this property is empty
if (empty($entry->getUrl()) && !empty($url)) {
$entry->setUrl($url);
}
$this->stockEntry($entry, $content);
}
@ -176,6 +184,59 @@ class ContentProxy
$entry->setTitle($path);
}
/**
* Try to sanitize the title of the fetched content from wrong character encodings and invalid UTF-8 character.
*
* @param $title
* @param $contentType
*
* @return string
*/
private function sanitizeContentTitle($title, $contentType)
{
if ('application/pdf' === $contentType) {
$title = $this->convertPdfEncodingToUTF8($title);
}
return $this->sanitizeUTF8Text($title);
}
/**
* If the title from the fetched content comes from a PDF, then its very possible that the character encoding is not
* UTF-8. This methods tries to identify the character encoding and translate the title to UTF-8.
*
* @param $title
*
* @return string (maybe contains invalid UTF-8 character)
*/
private function convertPdfEncodingToUTF8($title)
{
// first try UTF-8 because its easier to detect its present/absence
foreach (['UTF-8', 'UTF-16BE', 'WINDOWS-1252'] as $encoding) {
if (mb_check_encoding($title, $encoding)) {
return mb_convert_encoding($title, 'UTF-8', $encoding);
}
}
return $title;
}
/**
* Remove invalid UTF-8 characters from the given string.
*
* @param string $rawText
*
* @return string
*/
private function sanitizeUTF8Text($rawText)
{
if (mb_check_encoding($rawText, 'UTF-8')) {
return $rawText;
}
return iconv('UTF-8', 'UTF-8//IGNORE', $rawText);
}
/**
* Stock entry with fetched or imported content.
* Will fall back to OpenGraph data if available.
@ -185,7 +246,7 @@ class ContentProxy
*/
private function stockEntry(Entry $entry, array $content)
{
$entry->setUrl($content['url']);
$this->updateOriginUrl($entry, $content['url']);
$this->setEntryDomainName($entry);
@ -251,6 +312,115 @@ class ContentProxy
}
}
/**
* Update the origin_url field when a redirection occurs
* This field is set if it is empty and new url does not match ignore list.
*
* @param Entry $entry
* @param string $url
*/
private function updateOriginUrl(Entry $entry, $url)
{
if (empty($url) || $entry->getUrl() === $url) {
return false;
}
$parsed_entry_url = parse_url($entry->getUrl());
$parsed_content_url = parse_url($url);
/**
* The following part computes the list of part changes between two
* parse_url arrays.
*
* As array_diff_assoc only computes changes to go from the left array
* to the right one, we make two differents arrays to have both
* directions. We merge these two arrays and sort keys before passing
* the result to the switch.
*
* The resulting array gives us all changing parts between the two
* urls: scheme, host, path, query and/or fragment.
*/
$diff_ec = array_diff_assoc($parsed_entry_url, $parsed_content_url);
$diff_ce = array_diff_assoc($parsed_content_url, $parsed_entry_url);
$diff = array_merge($diff_ec, $diff_ce);
$diff_keys = array_keys($diff);
sort($diff_keys);
if ($this->ignoreUrl($entry->getUrl())) {
$entry->setUrl($url);
return false;
}
/**
* This switch case lets us apply different behaviors according to
* changing parts of urls.
*
* As $diff_keys is an array, we provide arrays as cases. ['path'] means
* 'only the path is different between the two urls' whereas
* ['fragment', 'query'] means 'only fragment and query string parts are
* different between the two urls'.
*
* Note that values in $diff_keys are sorted.
*/
switch ($diff_keys) {
case ['path']:
if (($parsed_entry_url['path'] . '/' === $parsed_content_url['path']) // diff is trailing slash, we only replace the url of the entry
|| ($url === urldecode($entry->getUrl()))) { // we update entry url if new url is a decoded version of it, see EntryRepository#findByUrlAndUserId
$entry->setUrl($url);
}
break;
case ['scheme']:
$entry->setUrl($url);
break;
case ['fragment']:
// noop
break;
default:
if (empty($entry->getOriginUrl())) {
$entry->setOriginUrl($entry->getUrl());
}
$entry->setUrl($url);
break;
}
}
/**
* Check entry url against an ignore list to replace with content url.
*
* XXX: move the ignore list in the database to let users handle it
*
* @param string $url url to test
*
* @return bool true if url matches ignore list otherwise false
*/
private function ignoreUrl($url)
{
$ignored_hosts = ['feedproxy.google.com', 'feeds.reuters.com'];
$ignored_patterns = ['https?://www\.lemonde\.fr/tiny.*'];
$parsed_url = parse_url($url);
$filtered = array_filter($ignored_hosts, function ($var) use ($parsed_url) {
return $var === $parsed_url['host'];
});
if ([] !== $filtered) {
return true;
}
$filtered = array_filter($ignored_patterns, function ($var) use ($url) {
return preg_match("`$var`i", $url);
});
if ([] !== $filtered) {
return true;
}
return false;
}
/**
* Validate that the given content has at least a title, an html and a url.
*

View file

@ -157,8 +157,8 @@ config:
# not_equal_to: 'Not equal to...'
# or: 'One rule OR another'
# and: 'One rule AND another'
# matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
# matches: 'Tests that a <i>subject</i> matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
entry:
# default_title: 'Title of the entry'

View file

@ -72,9 +72,9 @@ config:
300_word: 'I read ~300 words per minute'
400_word: 'I read ~400 words per minute'
action_mark_as_read:
label: 'Where do you want to be redirected to after marking an article as read?'
redirect_homepage: 'To the homepage'
redirect_current_page: 'To the current page'
label: 'What to do after removing, starring or marking as read an article?'
redirect_homepage: 'Go to the homepage'
redirect_current_page: 'Stay on the current page'
pocket_consumer_key_label: Consumer key for Pocket to import contents
android_configuration: Configure your Android application
android_instruction: "Touch here to prefill your Android application"
@ -157,8 +157,8 @@ config:
not_equal_to: 'Not equal to...'
or: 'One rule OR another'
and: 'One rule AND another'
matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
matches: 'Tests that a <i>subject</i> matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
entry:
default_title: 'Title of the entry'

View file

@ -158,7 +158,7 @@ config:
or: 'Una regla U otra'
and: 'Una regla Y la otra'
matches: 'Prueba si un <i>sujeto</i> corresponde a una <i>búsqueda</i> (insensible a mayusculas).<br />Ejemplo : <code>title matches "fútbol"</code>'
# notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
entry:
default_title: 'Título del artículo'

View file

@ -157,8 +157,8 @@ config:
# not_equal_to: 'Not equal to...'
# or: 'One rule OR another'
# and: 'One rule AND another'
# matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
# matches: 'Tests that a <i>subject</i> matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
entry:
# default_title: 'Title of the entry'

View file

@ -72,9 +72,9 @@ config:
300_word: "Je lis environ 300 mots par minute"
400_word: "Je lis environ 400 mots par minute"
action_mark_as_read:
label: "Où souhaitez-vous être redirigé après avoir marqué un article comme lu ?"
redirect_homepage: "À la page daccueil"
redirect_current_page: "À la page courante"
label: "Que faire lorsqu'un article est supprimé, marqué comme lu ou marqué comme favoris ?"
redirect_homepage: "Retourner à la page daccueil"
redirect_current_page: "Rester sur la page actuelle"
pocket_consumer_key_label: "Clé dauthentification Pocket pour importer les données"
android_configuration: "Configurez votre application Android"
android_instruction: "Appuyez ici pour préremplir votre application Android"

View file

@ -158,7 +158,7 @@ config:
or: "Una regola O un'altra"
and: "Una regola E un'altra"
matches: 'Verifica che un <i>oggetto</i> risulti in una <i>ricerca</i> (case-insensitive).<br />Esempio: <code>titolo contiene "football"</code>'
# notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
entry:
default_title: "Titolo del contenuto"

View file

@ -158,7 +158,7 @@ config:
or: 'Uma regra OU outra'
and: 'Uma regra E outra'
matches: 'Testa que um <i>assunto</i> corresponde a uma <i>pesquisa</i> (maiúscula ou minúscula).<br />Exemplo: <code>título corresponde a "futebol"</code>'
# notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
entry:
default_title: 'Título da entrada'

View file

@ -157,8 +157,8 @@ config:
# not_equal_to: 'Not equal to...'
# or: 'One rule OR another'
# and: 'One rule AND another'
# matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
# matches: 'Tests that a <i>subject</i> matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
entry:
# default_title: 'Title of the entry'

View file

@ -157,8 +157,8 @@ config:
not_equal_to: 'Eşit değildir…'
or: 'Bir kural veya birbaşkası'
and: 'Bir kural ve diğeri'
# matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
# matches: 'Tests that a <i>subject</i> matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
entry:
default_title: 'Makalenin başlığı'

View file

@ -38,6 +38,8 @@
<link rel="shortcut icon" type="image/x-icon" href="{{ asset('wallassets/themes/_global/img/appicon/favicon.ico') }}">
<link rel="manifest" href="{{ asset('manifest.json') }}">
{% block css %}
{% endblock %}
{% block scripts %}

View file

@ -57,7 +57,7 @@
</div>
<ul class="tools links">
<li><a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.list.original_article'|trans }} : {{ entry.title|e }}"><span>{{ entry.domainName|removeWww }}</span></a></li>
<li><a href="{{ entry.url|e }}" target="_blank" rel="noopener" title="{{ 'entry.list.original_article'|trans }} : {{ entry.title|e }}"><span>{{ entry.domainName|removeWww }}</span></a></li>
<li><a title="{{ 'entry.list.toogle_as_read'|trans }}" class="tool icon {% if entry.isArchived == 0 %}archive-off{% else %}archive{% endif %}" href="{{ path('archive_entry', { 'id': entry.id }) }}"><i class="material-icons md-24 vertical-align-middle">check</i><span>{{ 'entry.list.toogle_as_read'|trans }}</span></a></li>
<li><a title="{{ 'entry.list.toogle_as_star'|trans }}" class="tool icon {% if entry.isStarred == 0 %}fav-off{% else %}fav{% endif %}" href="{{ path('star_entry', { 'id': entry.id }) }}"><i class="material-icons md-24 vertical-align-middle">star_rate</i><span>{{ 'entry.list.toogle_as_star'|trans }}</span></a></li>
<li><a title="{{ 'entry.list.delete'|trans }}" class="tool icon" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')" href="{{ path('delete_entry', { 'id': entry.id }) }}"><i class="material-icons md-24 vertical-align-middle">delete</i><span>{{ 'entry.list.delete'|trans }}</span></a></li>

View file

@ -11,7 +11,7 @@
<div id="article_toolbar">
<ul class="links">
<li class="topPosF"><a href="#top" title="{{ 'entry.view.left_menu.back_to_top'|trans }}" class="tool top icon icon-arrow-up-thick"><span>{{ 'entry.view.left_menu.set_as_read'|trans }}</span></a></li>
<li><a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e }}" class="tool link icon icon-link original"><span>{{ entry.domainName|removeWww }}</span></a></li>
<li><a href="{{ entry.url|e }}" target="_blank" rel="noopener" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e }}" class="tool link icon icon-link original"><span>{{ entry.domainName|removeWww }}</span></a></li>
<li><a title="{{ 'entry.view.left_menu.re_fetch_content'|trans }}" class="tool icon icon-reload" href="{{ path('reload_entry', { 'id': entry.id }) }}"><span>{{ 'entry.view.left_menu.re_fetch_content'|trans }}</span></a></li>
{% set markAsReadLabel = 'entry.view.left_menu.set_as_unread' %}
@ -27,13 +27,13 @@
<li><a href="{{ path('share', {'id': entry.id }) }}" target="_blank" class="tool icon icon-eye" title="{{ 'entry.view.left_menu.public_link'|trans }}"><span>{{ 'entry.view.left_menu.public_link'|trans }}</span></a></li>
<li><a href="{{ path('delete_share', {'id': entry.id }) }}" class="tool icon icon-no-eye" title="{{ 'entry.view.left_menu.delete_public_link'|trans }}"><span>{{ 'entry.view.left_menu.delete_public_link'|trans }}</span></a></li>
{% endif %}
{% if craue_setting('share_twitter') %}<li><a href="https://twitter.com/home?status={{entry.title|url_encode}}%20{{ entry.url|url_encode }}%20via%20@wallabagapp" target="_blank" class="tool twitter icon icon-twitter" title="Tweet"><span>Tweet</span></a></li>{% endif %}
{% if craue_setting('share_twitter') %}<li><a href="https://twitter.com/home?status={{entry.title|url_encode}}%20{{ entry.url|url_encode }}%20via%20@wallabagapp" target="_blank" rel="noopener" class="tool twitter icon icon-twitter" title="Tweet"><span>Tweet</span></a></li>{% endif %}
{% if craue_setting('share_mail') %}<li><a href="mailto:?subject={{ entry.title|url_encode }}&amp;body={{ entry.url|url_encode }}%20via%20@wallabagapp" class="tool email icon icon-mail" title="Email"><span>Email</span></a></li>{% endif %}
{% if craue_setting('share_shaarli') %}<li><a href="{{ craue_setting('shaarli_url') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}&amp;tags={{ entry.tags|join(',')|url_encode }}{% if craue_setting('shaarli_share_origin_url') %}&amp;original_url={{ entry.originUrl|url_encode }}{% endif %}" target="_blank" class="tool icon-image icon-image--shaarli" title="shaarli"><span>shaarli</span></a></li>{% endif %}
{% if craue_setting('share_scuttle') %}<li><a href="{{ craue_setting('scuttle_url') }}/bookmarks.php?action=add&amp;address={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}&amp;tags={{ entry.tags|join(',')|url_encode }}" target="_blank" class="tool icon-image icon-image--scuttle" title="scuttle"><span>scuttle</span></a></li>{% endif %}
{% if craue_setting('share_diaspora') %}<li><a href="{{ craue_setting('diaspora_url') }}/bookmarklet?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}&notes=&v=1&noui=1&jump=doclose" target="_blank" class="tool diaspora icon-image icon-image--diaspora" title="diaspora"><span>diaspora</span></a></li>{% endif %}
{% if craue_setting('share_unmark') %}<li><a href="{{ craue_setting('unmark_url') }}/mark/add?url={{ entry.url|url_encode }}&amp;title={{entry.title|url_encode}}&amp;v=6" target="_blank" class="tool unmark icon-image icon-image--unmark" title="unmark"><span>unmark.it</span></a></li>{% endif %}
{% if craue_setting('carrot') %}<li><a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}" class="tool carrot icon-image icon-image--carrot" target="_blank" title="carrot"><span>Carrot</span></a></li>{% endif %}
{% if craue_setting('share_shaarli') %}<li><a href="{{ craue_setting('shaarli_url') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}&amp;tags={{ entry.tags|join(',')|url_encode }}{% if craue_setting('shaarli_share_origin_url') %}&amp;original_url={{ entry.originUrl|url_encode }}{% endif %}" target="_blank" rel="noopener" class="tool icon-image icon-image--shaarli" title="shaarli"><span>shaarli</span></a></li>{% endif %}
{% if craue_setting('share_scuttle') %}<li><a href="{{ craue_setting('scuttle_url') }}/bookmarks.php?action=add&amp;address={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}&amp;tags={{ entry.tags|join(',')|url_encode }}" target="_blank" rel="noopener" class="tool icon-image icon-image--scuttle" title="scuttle"><span>scuttle</span></a></li>{% endif %}
{% if craue_setting('share_diaspora') %}<li><a href="{{ craue_setting('diaspora_url') }}/bookmarklet?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}&notes=&v=1&noui=1&jump=doclose" target="_blank" rel="noopener" class="tool diaspora icon-image icon-image--diaspora" title="diaspora"><span>diaspora</span></a></li>{% endif %}
{% if craue_setting('share_unmark') %}<li><a href="{{ craue_setting('unmark_url') }}/mark/add?url={{ entry.url|url_encode }}&amp;title={{entry.title|url_encode}}&amp;v=6" target="_blank" rel="noopener" class="tool unmark icon-image icon-image--unmark" title="unmark"><span>unmark.it</span></a></li>{% endif %}
{% if craue_setting('carrot') %}<li><a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}" class="tool carrot icon-image icon-image--carrot" target="_blank" rel="noopener" title="carrot"><span>Carrot</span></a></li>{% endif %}
{% if craue_setting('show_printlink') %}<li><a title="{{ 'entry.view.left_menu.print'|trans }}" class="tool icon icon-print" href="javascript: window.print();"><span>{{ 'entry.view.left_menu.print'|trans }}</span></a></li>{% endif %}
{% if craue_setting('export_epub') %}<li><a href="{{ path('export_entry', { 'id': entry.id, 'format': 'epub' }) }}" title="Generate ePub file">EPUB</a></li>{% endif %}
{% if craue_setting('export_mobi') %}<li><a href="{{ path('export_entry', { 'id': entry.id, 'format': 'mobi' }) }}" title="Generate Mobi file">MOBI</a></li>{% endif %}
@ -74,7 +74,7 @@
{% if entry.originUrl is not empty %}
<i class="material-icons" title="{{ 'entry.view.provided_by'|trans }}">launch</i>
<a href="{{ entry.originUrl|e }}" target="_blank" class="tool">
<a href="{{ entry.originUrl|e }}" target="_blank" rel="noopener" class="tool">
{{ entry.originUrl|striptags|removeSchemeAndWww|truncate(32) }}
</a>
{% endif %}

View file

@ -27,7 +27,7 @@
<body>
<header class="block">
<h1>{{ entry.title|e|raw }}</h1>
<a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e|raw }}" class="tool">{{ entry.domainName|removeWww }}</a>
<a href="{{ entry.url|e }}" target="_blank" rel="noopener" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e|raw }}" class="tool">{{ entry.domainName|removeWww }}</a>
<p class="shared-by">{{ "entry.public.shared_by_wallabag"|trans({'%wallabag_instance%': url('homepage'), '%username%': entry.user.username})|raw }}.</p>
{% if entry.previewPicture is not null %}
<img class="preview" src="{{ entry.previewPicture }}" alt="{{ entry.title|striptags|e('html_attr') }}" />

View file

@ -28,18 +28,18 @@
<div class="col s12">
<h5>{{ 'howto.top_menu.browser_addons'|trans }}</h5>
<ul>
<li><a href="{{ addonsUrl.firefox }}" target="_blank">{{ 'howto.browser_addons.firefox'|trans }}</a></li>
<li><a href="{{ addonsUrl.chrome }}" target="_blank">{{ 'howto.browser_addons.chrome'|trans }}</a></li>
<li><a href="{{ addonsUrl.opera }}" target="_blank">{{ 'howto.browser_addons.opera'|trans }}</a></li>
<li><a href="{{ addonsUrl.firefox }}" target="_blank" rel="noopener">{{ 'howto.browser_addons.firefox'|trans }}</a></li>
<li><a href="{{ addonsUrl.chrome }}" target="_blank" rel="noopener">{{ 'howto.browser_addons.chrome'|trans }}</a></li>
<li><a href="{{ addonsUrl.opera }}" target="_blank" rel="noopener">{{ 'howto.browser_addons.opera'|trans }}</a></li>
</ul>
</div>
<div class="col s12">
<h5>{{ 'howto.top_menu.mobile_apps'|trans }}</h5>
<ul>
<li>Android: <a href="{{ addonsUrl.f_droid }}" target="_blank">{{ 'howto.mobile_apps.android.via_f_droid'|trans }}</a> / <a href="{{ addonsUrl.google_play }}" target="_blank">{{ 'howto.mobile_apps.android.via_google_play'|trans }}</a></li>
<li>Android: <a href="{{ addonsUrl.f_droid }}" target="_blank" rel="noopener">{{ 'howto.mobile_apps.android.via_f_droid'|trans }}</a> / <a href="{{ addonsUrl.google_play }}" target="_blank" rel="noopener">{{ 'howto.mobile_apps.android.via_google_play'|trans }}</a></li>
<li>iOS: <a href="{{ addonsUrl.ios }}" target="_blank">{{ 'howto.mobile_apps.ios'|trans }}</a></li>
<li>Windows Phone: <a href="{{ addonsUrl.windows }}" target="_blank">{{ 'howto.mobile_apps.windows'|trans }}</a></li>
<li>Windows Phone: <a href="{{ addonsUrl.windows }}" target="_blank" rel="noopener">{{ 'howto.mobile_apps.windows'|trans }}</a></li>
</ul>
</div>

View file

@ -1,7 +0,0 @@
<div class="card">
<div class="card-body">
{% include "@WallabagCore/themes/material/Entry/Card/_content.html.twig" with {'entry': entry} only %}
</div>
{% include "@WallabagCore/themes/material/Entry/_card_actions.html.twig" with {'entry': entry} only %}
</div>

View file

@ -7,7 +7,8 @@
{% endfor %}
</ul>
<a href="{{ path('view', { 'id': entry.id }) }}">
<span class="preview" style="background-image: url({{ entry.previewPicture }})"></span>
{% set previewClassModifier = entry.previewPicture ? '' : ' preview--default' %}
<span class="preview{{ previewClassModifier }}" style="background-image: url({{ entry.previewPicture | default(asset('wallassets/themes/_global/img/logo-square.svg')) }})"></span>
</a>
</div>
{% include "@WallabagCore/themes/material/Entry/Card/_content.html.twig" with {'entry': entry, 'withPreview': true} only %}

View file

@ -39,11 +39,9 @@
<li id="entry-{{ entry.id|e }}" class="col {% if listMode == 0 %}l3 m6{% else %}collection-item{% endif %} s12">
{% if listMode == 1 %}
{% include "@WallabagCore/themes/material/Entry/_card_list.html.twig" with {'entry': entry} only %}
{% elseif entry.previewPicture is null %}
{% include "@WallabagCore/themes/material/Entry/_card_no_preview.html.twig" with {'entry': entry} only %}
{% elseif not entry.previewPicture is null and entry.mimetype starts with 'image/' %}
{% include "@WallabagCore/themes/material/Entry/_card_full_image.html.twig" with {'entry': entry} only %}
{% elseif not entry.previewPicture is null %}
{% else %}
{% include "@WallabagCore/themes/material/Entry/_card_preview.html.twig" with {'entry': entry} only %}
{% endif %}
</li>

View file

@ -46,7 +46,7 @@
</li>
<li class="bold border-bottom hide-on-med-and-down">
<a class="waves-effect collapsible-header original" href="{{ entry.url|e }}" target="_blank">
<a class="waves-effect collapsible-header original" href="{{ entry.url|e }}" target="_blank" rel="noopener">
<i class="material-icons small">link</i>
<span>{{ 'entry.view.left_menu.view_original_article'|trans }}</span>
</a>
@ -127,42 +127,42 @@
{% endif %}
{% if craue_setting('share_twitter') %}
<li>
<a href="https://twitter.com/home?status={{entry.title|striptags|url_encode}}%20{{ entry.url|url_encode }}%20via%20@wallabagapp" target="_blank" class="tool icon-twitter" title="twitter">
<a href="https://twitter.com/home?status={{entry.title|striptags|url_encode}}%20{{ entry.url|url_encode }}%20via%20@wallabagapp" target="_blank" rel="noopener" class="tool icon-twitter" title="twitter">
<span>twitter</span>
</a>
</li>
{% endif %}
{% if craue_setting('share_shaarli') %}
<li>
<a href="{{ craue_setting('shaarli_url') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|striptags|url_encode }}&amp;tags={{ entry.tags|join(',')|striptags|url_encode }}{% if craue_setting('shaarli_share_origin_url') %}&amp;original_url={{ entry.originUrl|url_encode }}{% endif %}" target="_blank" title="shaarli" class="tool icon-image shaarli">
<a href="{{ craue_setting('shaarli_url') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|striptags|url_encode }}&amp;tags={{ entry.tags|join(',')|striptags|url_encode }}{% if craue_setting('shaarli_share_origin_url') %}&amp;original_url={{ entry.originUrl|url_encode }}{% endif %}" target="_blank" rel="noopener" title="shaarli" class="tool icon-image shaarli">
<span>shaarli</span>
</a>
</li>
{% endif %}
{% if craue_setting('share_scuttle') %}
<li>
<a href="{{ craue_setting('scuttle_url') }}/bookmarks.php?action=add&amp;address={{ entry.url|url_encode }}&amp;title={{ entry.title|striptags|url_encode }}&amp;tags={{ entry.tags|join(',')|striptags|url_encode }}" target="_blank" title="scuttle" class="tool icon-image scuttle">
<a href="{{ craue_setting('scuttle_url') }}/bookmarks.php?action=add&amp;address={{ entry.url|url_encode }}&amp;title={{ entry.title|striptags|url_encode }}&amp;tags={{ entry.tags|join(',')|striptags|url_encode }}" target="_blank" rel="noopener" title="scuttle" class="tool icon-image scuttle">
<span>scuttle</span>
</a>
</li>
{% endif %}
{% if craue_setting('share_diaspora') %}
<li>
<a href="{{ craue_setting('diaspora_url') }}/bookmarklet?url={{ entry.url|url_encode }}&amp;title={{ entry.title|striptags|url_encode }}&amp;notes=&amp;v=1&amp;noui=1&amp;jump=doclose" target="_blank" class="tool icon-image diaspora" title="diaspora">
<a href="{{ craue_setting('diaspora_url') }}/bookmarklet?url={{ entry.url|url_encode }}&amp;title={{ entry.title|striptags|url_encode }}&amp;notes=&amp;v=1&amp;noui=1&amp;jump=doclose" target="_blank" rel="noopener" class="tool icon-image diaspora" title="diaspora">
<span>diaspora*</span>
</a>
</li>
{% endif %}
{% if craue_setting('share_unmark') %}
<li>
<a href="{{ craue_setting('unmark_url') }}/mark/add?url={{ entry.url|url_encode }}&amp;title={{entry.title|striptags|url_encode}}&amp;v=6" target="_blank" class="tool icon-image unmark" title="unmark">
<a href="{{ craue_setting('unmark_url') }}/mark/add?url={{ entry.url|url_encode }}&amp;title={{entry.title|striptags|url_encode}}&amp;v=6" target="_blank" rel="noopener" class="tool icon-image unmark" title="unmark">
<span>unmark.it</span>
</a>
</li>
{% endif %}
{% if craue_setting('carrot') %}
<li>
<a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&amp;title={{ entry.title|striptags|url_encode }}" target="_blank" title="carrot" class="tool icon-image carrot">
<a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&amp;title={{ entry.title|striptags|url_encode }}" target="_blank" rel="noopener" title="carrot" class="tool icon-image carrot">
<span>Carrot</span>
</a>
</li>
@ -251,7 +251,7 @@
{% endif %}
<li>
<i class="material-icons link">link</i>
<a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|striptags }}" class="tool">
<a href="{{ entry.url|e }}" target="_blank" rel="noopener" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|striptags }}" class="tool">
{{ entry.domainName|removeWww }}
</a>
</li>
@ -262,7 +262,7 @@
{% if entry.originUrl is not empty %}
<li>
<i class="material-icons" title="{{ 'entry.view.provided_by'|trans }}">launch</i>
<a href="{{ entry.originUrl|e }}" target="_blank" class="tool">
<a href="{{ entry.originUrl|e }}" target="_blank" rel="noopener" class="tool">
{{ entry.originUrl|striptags|removeSchemeAndWww|truncate(32) }}
</a>
</li>

View file

@ -143,7 +143,7 @@
</div>
<div class="col s12 l4">
<p class="footer-text">
{{ 'footer.wallabag.powered_by'|trans }} <a target="_blank" href="https://wallabag.org" class="grey-text text-lighten-4">wallabag</a>
{{ 'footer.wallabag.powered_by'|trans }} <a target="_blank" rel="noopener" href="https://wallabag.org" class="grey-text text-lighten-4">wallabag</a>
<a class="grey-text text-lighten-4" href="{{ path('about') }}">{{ 'footer.wallabag.about'|trans|lower }}</a>
</p>
</div>