aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Wallabag/ApiBundle/Controller/EntryRestController.php31
-rw-r--r--src/Wallabag/ApiBundle/Controller/TagRestController.php22
-rw-r--r--src/Wallabag/ApiBundle/Controller/WallabagRestController.php20
-rw-r--r--src/Wallabag/ApiBundle/Entity/Client.php2
-rw-r--r--src/Wallabag/ApiBundle/Repository/ClientRepository.php19
-rw-r--r--src/Wallabag/CoreBundle/Command/InstallCommand.php3
-rw-r--r--src/Wallabag/CoreBundle/Controller/ExportController.php5
-rw-r--r--src/Wallabag/CoreBundle/DataFixtures/EntryFixtures.php195
-rw-r--r--src/Wallabag/CoreBundle/DataFixtures/TagFixtures.php43
-rw-r--r--src/Wallabag/CoreBundle/Entity/SiteCredential.php17
-rw-r--r--src/Wallabag/CoreBundle/Helper/EntriesExport.php100
-rw-r--r--src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php1
-rw-r--r--src/Wallabag/CoreBundle/Repository/EntryRepository.php6
-rw-r--r--src/Wallabag/CoreBundle/Repository/TagRepository.php53
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.da.yml6
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.de.yml6
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.en.yml6
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.es.yml6
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml6
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml6
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.it.yml6
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml6
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml8
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml6
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml6
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.ru.yml9
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.th.yml6
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml6
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig4
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig3
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_list.html.twig11
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig4
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig4
-rw-r--r--src/Wallabag/CoreBundle/Tools/Utils.php7
-rw-r--r--src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php7
-rw-r--r--src/Wallabag/ImportBundle/Import/AbstractImport.php17
-rw-r--r--src/Wallabag/ImportBundle/Import/BrowserImport.php4
-rw-r--r--src/Wallabag/ImportBundle/Import/ChromeImport.php12
-rw-r--r--src/Wallabag/ImportBundle/Import/FirefoxImport.php12
-rw-r--r--src/Wallabag/ImportBundle/Import/InstapaperImport.php12
-rw-r--r--src/Wallabag/ImportBundle/Import/PinboardImport.php12
-rw-r--r--src/Wallabag/ImportBundle/Import/PocketImport.php12
-rw-r--r--src/Wallabag/ImportBundle/Import/ReadabilityImport.php12
-rw-r--r--src/Wallabag/ImportBundle/Import/WallabagImport.php12
-rw-r--r--src/Wallabag/UserBundle/Resources/views/layout.html.twig2
45 files changed, 537 insertions, 216 deletions
diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
index 5882aaee..f792aaf2 100644
--- a/src/Wallabag/ApiBundle/Controller/EntryRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
@@ -9,6 +9,7 @@ use Nelmio\ApiDocBundle\Annotation\ApiDoc;
9use Symfony\Component\HttpFoundation\JsonResponse; 9use Symfony\Component\HttpFoundation\JsonResponse;
10use Symfony\Component\HttpFoundation\Request; 10use Symfony\Component\HttpFoundation\Request;
11use Symfony\Component\HttpFoundation\Response; 11use Symfony\Component\HttpFoundation\Response;
12use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
12use Symfony\Component\HttpKernel\Exception\HttpException; 13use Symfony\Component\HttpKernel\Exception\HttpException;
13use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 14use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
14use Wallabag\CoreBundle\Entity\Entry; 15use Wallabag\CoreBundle\Entity\Entry;
@@ -98,24 +99,28 @@ class EntryRestController extends WallabagRestController
98 $isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive'); 99 $isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive');
99 $isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred'); 100 $isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred');
100 $isPublic = (null === $request->query->get('public')) ? null : (bool) $request->query->get('public'); 101 $isPublic = (null === $request->query->get('public')) ? null : (bool) $request->query->get('public');
101 $sort = $request->query->get('sort', 'created'); 102 $sort = strtolower($request->query->get('sort', 'created'));
102 $order = $request->query->get('order', 'desc'); 103 $order = strtolower($request->query->get('order', 'desc'));
103 $page = (int) $request->query->get('page', 1); 104 $page = (int) $request->query->get('page', 1);
104 $perPage = (int) $request->query->get('perPage', 30); 105 $perPage = (int) $request->query->get('perPage', 30);
105 $tags = \is_array($request->query->get('tags')) ? '' : (string) $request->query->get('tags', ''); 106 $tags = \is_array($request->query->get('tags')) ? '' : (string) $request->query->get('tags', '');
106 $since = $request->query->get('since', 0); 107 $since = $request->query->get('since', 0);
107 108
108 /** @var \Pagerfanta\Pagerfanta $pager */ 109 try {
109 $pager = $this->get('wallabag_core.entry_repository')->findEntries( 110 /** @var \Pagerfanta\Pagerfanta $pager */
110 $this->getUser()->getId(), 111 $pager = $this->get('wallabag_core.entry_repository')->findEntries(
111 $isArchived, 112 $this->getUser()->getId(),
112 $isStarred, 113 $isArchived,
113 $isPublic, 114 $isStarred,
114 $sort, 115 $isPublic,
115 $order, 116 $sort,
116 $since, 117 $order,
117 $tags 118 $since,
118 ); 119 $tags
120 );
121 } catch (\Exception $e) {
122 throw new BadRequestHttpException($e->getMessage());
123 }
119 124
120 $pager->setMaxPerPage($perPage); 125 $pager->setMaxPerPage($perPage);
121 $pager->setCurrentPage($page); 126 $pager->setCurrentPage($page);
diff --git a/src/Wallabag/ApiBundle/Controller/TagRestController.php b/src/Wallabag/ApiBundle/Controller/TagRestController.php
index c6d6df6a..f3498f55 100644
--- a/src/Wallabag/ApiBundle/Controller/TagRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/TagRestController.php
@@ -46,12 +46,14 @@ class TagRestController extends WallabagRestController
46 $this->validateAuthentication(); 46 $this->validateAuthentication();
47 $label = $request->get('tag', ''); 47 $label = $request->get('tag', '');
48 48
49 $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label); 49 $tags = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findByLabelsAndUser([$label], $this->getUser()->getId());
50 50
51 if (empty($tag)) { 51 if (empty($tags)) {
52 throw $this->createNotFoundException('Tag not found'); 52 throw $this->createNotFoundException('Tag not found');
53 } 53 }
54 54
55 $tag = $tags[0];
56
55 $this->getDoctrine() 57 $this->getDoctrine()
56 ->getRepository('WallabagCoreBundle:Entry') 58 ->getRepository('WallabagCoreBundle:Entry')
57 ->removeTag($this->getUser()->getId(), $tag); 59 ->removeTag($this->getUser()->getId(), $tag);
@@ -80,15 +82,7 @@ class TagRestController extends WallabagRestController
80 82
81 $tagsLabels = $request->get('tags', ''); 83 $tagsLabels = $request->get('tags', '');
82 84
83 $tags = []; 85 $tags = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findByLabelsAndUser(explode(',', $tagsLabels), $this->getUser()->getId());
84
85 foreach (explode(',', $tagsLabels) as $tagLabel) {
86 $tagEntity = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($tagLabel);
87
88 if (!empty($tagEntity)) {
89 $tags[] = $tagEntity;
90 }
91 }
92 86
93 if (empty($tags)) { 87 if (empty($tags)) {
94 throw $this->createNotFoundException('Tags not found'); 88 throw $this->createNotFoundException('Tags not found');
@@ -120,6 +114,12 @@ class TagRestController extends WallabagRestController
120 { 114 {
121 $this->validateAuthentication(); 115 $this->validateAuthentication();
122 116
117 $tagFromDb = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findByLabelsAndUser([$tag->getLabel()], $this->getUser()->getId());
118
119 if (empty($tagFromDb)) {
120 throw $this->createNotFoundException('Tag not found');
121 }
122
123 $this->getDoctrine() 123 $this->getDoctrine()
124 ->getRepository('WallabagCoreBundle:Entry') 124 ->getRepository('WallabagCoreBundle:Entry')
125 ->removeTag($this->getUser()->getId(), $tag); 125 ->removeTag($this->getUser()->getId(), $tag);
diff --git a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
index 7d8cfbba..3c7ad0cf 100644
--- a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
@@ -14,6 +14,8 @@ class WallabagRestController extends FOSRestController
14 * 14 *
15 * @ApiDoc() 15 * @ApiDoc()
16 * 16 *
17 * @deprecated Should use info endpoint instead
18 *
17 * @return JsonResponse 19 * @return JsonResponse
18 */ 20 */
19 public function getVersionAction() 21 public function getVersionAction()
@@ -24,6 +26,24 @@ class WallabagRestController extends FOSRestController
24 return (new JsonResponse())->setJson($json); 26 return (new JsonResponse())->setJson($json);
25 } 27 }
26 28
29 /**
30 * Retrieve information about the wallabag instance.
31 *
32 * @ApiDoc()
33 *
34 * @return JsonResponse
35 */
36 public function getInfoAction()
37 {
38 $info = [
39 'appname' => 'wallabag',
40 'version' => $this->container->getParameter('wallabag_core.version'),
41 'allowed_registration' => $this->container->getParameter('wallabag_user.registration_enabled'),
42 ];
43
44 return (new JsonResponse())->setJson($this->get('jms_serializer')->serialize($info, 'json'));
45 }
46
27 protected function validateAuthentication() 47 protected function validateAuthentication()
28 { 48 {
29 if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) { 49 if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
diff --git a/src/Wallabag/ApiBundle/Entity/Client.php b/src/Wallabag/ApiBundle/Entity/Client.php
index e6f98f98..78349820 100644
--- a/src/Wallabag/ApiBundle/Entity/Client.php
+++ b/src/Wallabag/ApiBundle/Entity/Client.php
@@ -11,7 +11,7 @@ use Wallabag\UserBundle\Entity\User;
11 11
12/** 12/**
13 * @ORM\Table("oauth2_clients") 13 * @ORM\Table("oauth2_clients")
14 * @ORM\Entity 14 * @ORM\Entity(repositoryClass="Wallabag\ApiBundle\Repository\ClientRepository")
15 */ 15 */
16class Client extends BaseClient 16class Client extends BaseClient
17{ 17{
diff --git a/src/Wallabag/ApiBundle/Repository/ClientRepository.php b/src/Wallabag/ApiBundle/Repository/ClientRepository.php
new file mode 100644
index 00000000..fc14262e
--- /dev/null
+++ b/src/Wallabag/ApiBundle/Repository/ClientRepository.php
@@ -0,0 +1,19 @@
1<?php
2
3namespace Wallabag\ApiBundle\Repository;
4
5use Doctrine\ORM\EntityRepository;
6
7class ClientRepository extends EntityRepository
8{
9 public function findOneBy(array $criteria, array $orderBy = null)
10 {
11 if (!empty($criteria['id'])) {
12 // cast client id to be an integer to avoid postgres error:
13 // "invalid input syntax for integer"
14 $criteria['id'] = (int) $criteria['id'];
15 }
16
17 return parent::findOneBy($criteria, $orderBy);
18 }
19}
diff --git a/src/Wallabag/CoreBundle/Command/InstallCommand.php b/src/Wallabag/CoreBundle/Command/InstallCommand.php
index 3c76545c..49c84178 100644
--- a/src/Wallabag/CoreBundle/Command/InstallCommand.php
+++ b/src/Wallabag/CoreBundle/Command/InstallCommand.php
@@ -94,8 +94,9 @@ class InstallCommand extends ContainerAwareCommand
94 $status = '<info>OK!</info>'; 94 $status = '<info>OK!</info>';
95 $help = ''; 95 $help = '';
96 96
97 $conn = $this->getContainer()->get('doctrine')->getManager()->getConnection();
98
97 try { 99 try {
98 $conn = $this->getContainer()->get('doctrine')->getManager()->getConnection();
99 $conn->connect(); 100 $conn->connect();
100 } catch (\Exception $e) { 101 } catch (\Exception $e) {
101 if (false === strpos($e->getMessage(), 'Unknown database') 102 if (false === strpos($e->getMessage(), 'Unknown database')
diff --git a/src/Wallabag/CoreBundle/Controller/ExportController.php b/src/Wallabag/CoreBundle/Controller/ExportController.php
index 0d2b15c5..9ff35ff5 100644
--- a/src/Wallabag/CoreBundle/Controller/ExportController.php
+++ b/src/Wallabag/CoreBundle/Controller/ExportController.php
@@ -58,6 +58,7 @@ class ExportController extends Controller
58 $method = ucfirst($category); 58 $method = ucfirst($category);
59 $methodBuilder = 'getBuilderFor' . $method . 'ByUser'; 59 $methodBuilder = 'getBuilderFor' . $method . 'ByUser';
60 $repository = $this->get('wallabag_core.entry_repository'); 60 $repository = $this->get('wallabag_core.entry_repository');
61 $title = $method;
61 62
62 if ('tag_entries' === $category) { 63 if ('tag_entries' === $category) {
63 $tag = $this->get('wallabag_core.tag_repository')->findOneBySlug($request->query->get('tag')); 64 $tag = $this->get('wallabag_core.tag_repository')->findOneBySlug($request->query->get('tag'));
@@ -66,6 +67,8 @@ class ExportController extends Controller
66 $this->getUser()->getId(), 67 $this->getUser()->getId(),
67 $tag->getId() 68 $tag->getId()
68 ); 69 );
70
71 $title = 'Tag ' . $tag->getLabel();
69 } else { 72 } else {
70 $entries = $repository 73 $entries = $repository
71 ->$methodBuilder($this->getUser()->getId()) 74 ->$methodBuilder($this->getUser()->getId())
@@ -76,7 +79,7 @@ class ExportController extends Controller
76 try { 79 try {
77 return $this->get('wallabag_core.helper.entries_export') 80 return $this->get('wallabag_core.helper.entries_export')
78 ->setEntries($entries) 81 ->setEntries($entries)
79 ->updateTitle($method) 82 ->updateTitle($title)
80 ->updateAuthor($method) 83 ->updateAuthor($method)
81 ->exportAs($format); 84 ->exportAs($format);
82 } catch (\InvalidArgumentException $e) { 85 } catch (\InvalidArgumentException $e) {
diff --git a/src/Wallabag/CoreBundle/DataFixtures/EntryFixtures.php b/src/Wallabag/CoreBundle/DataFixtures/EntryFixtures.php
index 0bd58487..024fcfdc 100644
--- a/src/Wallabag/CoreBundle/DataFixtures/EntryFixtures.php
+++ b/src/Wallabag/CoreBundle/DataFixtures/EntryFixtures.php
@@ -15,97 +15,112 @@ class EntryFixtures extends Fixture implements DependentFixtureInterface
15 */ 15 */
16 public function load(ObjectManager $manager) 16 public function load(ObjectManager $manager)
17 { 17 {
18 $entry1 = new Entry($this->getReference('admin-user')); 18 $entries = [
19 $entry1->setUrl('http://0.0.0.0/entry1'); 19 'entry1' => [
20 $entry1->setReadingTime(11); 20 'user' => 'admin-user',
21 $entry1->setDomainName('domain.io'); 21 'url' => 'http://0.0.0.0/entry1',
22 $entry1->setMimetype('text/html'); 22 'reading_time' => 11,
23 $entry1->setTitle('test title entry1'); 23 'domain' => 'domain.io',
24 $entry1->setContent('This is my content /o/'); 24 'mime' => 'text/html',
25 $entry1->setLanguage('en'); 25 'title' => 'test title entry1',
26 26 'content' => 'This is my content /o/',
27 $entry1->addTag($this->getReference('foo-tag')); 27 'language' => 'en',
28 $entry1->addTag($this->getReference('baz-tag')); 28 'tags' => ['foo-tag', 'baz-tag'],
29 29 ],
30 $manager->persist($entry1); 30 'entry2' => [
31 31 'user' => 'admin-user',
32 $this->addReference('entry1', $entry1); 32 'url' => 'http://0.0.0.0/entry2',
33 33 'reading_time' => 1,
34 $entry2 = new Entry($this->getReference('admin-user')); 34 'domain' => 'domain.io',
35 $entry2->setUrl('http://0.0.0.0/entry2'); 35 'mime' => 'text/html',
36 $entry2->setReadingTime(1); 36 'title' => 'test title entry2',
37 $entry2->setDomainName('domain.io'); 37 'content' => 'This is my content /o/',
38 $entry2->setMimetype('text/html'); 38 'origin' => 'ftp://oneftp.tld',
39 $entry2->setTitle('test title entry2'); 39 'language' => 'fr',
40 $entry2->setContent('This is my content /o/'); 40 ],
41 $entry2->setOriginUrl('ftp://oneftp.tld'); 41 'entry3' => [
42 $entry2->setLanguage('fr'); 42 'user' => 'bob-user',
43 43 'url' => 'http://0.0.0.0/entry3',
44 $manager->persist($entry2); 44 'reading_time' => 1,
45 45 'domain' => 'domain.io',
46 $this->addReference('entry2', $entry2); 46 'mime' => 'text/html',
47 47 'title' => 'test title entry3',
48 $entry3 = new Entry($this->getReference('bob-user')); 48 'content' => 'This is my content /o/',
49 $entry3->setUrl('http://0.0.0.0/entry3'); 49 'language' => 'en',
50 $entry3->setReadingTime(1); 50 'tags' => ['foo-tag', 'bar-tag', 'bob-tag'],
51 $entry3->setDomainName('domain.io'); 51 ],
52 $entry3->setMimetype('text/html'); 52 'entry4' => [
53 $entry3->setTitle('test title entry3'); 53 'user' => 'admin-user',
54 $entry3->setContent('This is my content /o/'); 54 'url' => 'http://0.0.0.0/entry4',
55 $entry3->setLanguage('en'); 55 'reading_time' => 12,
56 56 'domain' => 'domain.io',
57 $entry3->addTag($this->getReference('foo-tag')); 57 'mime' => 'text/html',
58 $entry3->addTag($this->getReference('bar-tag')); 58 'title' => 'test title entry4',
59 59 'content' => 'This is my content /o/',
60 $manager->persist($entry3); 60 'language' => 'en',
61 61 'tags' => ['foo-tag', 'bar-tag'],
62 $this->addReference('entry3', $entry3); 62 ],
63 63 'entry5' => [
64 $entry4 = new Entry($this->getReference('admin-user')); 64 'user' => 'admin-user',
65 $entry4->setUrl('http://0.0.0.0/entry4'); 65 'url' => 'http://0.0.0.0/entry5',
66 $entry4->setReadingTime(12); 66 'reading_time' => 12,
67 $entry4->setDomainName('domain.io'); 67 'domain' => 'domain.io',
68 $entry4->setMimetype('text/html'); 68 'mime' => 'text/html',
69 $entry4->setTitle('test title entry4'); 69 'title' => 'test title entry5',
70 $entry4->setContent('This is my content /o/'); 70 'content' => 'This is my content /o/',
71 $entry4->setLanguage('en'); 71 'language' => 'fr',
72 72 'starred' => true,
73 $entry4->addTag($this->getReference('foo-tag')); 73 'preview' => 'http://0.0.0.0/image.jpg',
74 $entry4->addTag($this->getReference('bar-tag')); 74 ],
75 75 'entry6' => [
76 $manager->persist($entry4); 76 'user' => 'admin-user',
77 77 'url' => 'http://0.0.0.0/entry6',
78 $this->addReference('entry4', $entry4); 78 'reading_time' => 12,
79 79 'domain' => 'domain.io',
80 $entry5 = new Entry($this->getReference('admin-user')); 80 'mime' => 'text/html',
81 $entry5->setUrl('http://0.0.0.0/entry5'); 81 'title' => 'test title entry6',
82 $entry5->setReadingTime(12); 82 'content' => 'This is my content /o/',
83 $entry5->setDomainName('domain.io'); 83 'language' => 'de',
84 $entry5->setMimetype('text/html'); 84 'archived' => true,
85 $entry5->setTitle('test title entry5'); 85 'tags' => ['bar-tag'],
86 $entry5->setContent('This is my content /o/'); 86 ],
87 $entry5->setStarred(true); 87 ];
88 $entry5->setLanguage('fr');
89 $entry5->setPreviewPicture('http://0.0.0.0/image.jpg');
90
91 $manager->persist($entry5);
92
93 $this->addReference('entry5', $entry5);
94
95 $entry6 = new Entry($this->getReference('admin-user'));
96 $entry6->setUrl('http://0.0.0.0/entry6');
97 $entry6->setReadingTime(12);
98 $entry6->setDomainName('domain.io');
99 $entry6->setMimetype('text/html');
100 $entry6->setTitle('test title entry6');
101 $entry6->setContent('This is my content /o/');
102 $entry6->updateArchived(true);
103 $entry6->setLanguage('de');
104 $entry6->addTag($this->getReference('bar-tag'));
105
106 $manager->persist($entry6);
107 88
108 $this->addReference('entry6', $entry6); 89 foreach ($entries as $reference => $item) {
90 $entry = new Entry($this->getReference($item['user']));
91 $entry->setUrl($item['url']);
92 $entry->setReadingTime($item['reading_time']);
93 $entry->setDomainName($item['domain']);
94 $entry->setMimetype($item['mime']);
95 $entry->setTitle($item['title']);
96 $entry->setContent($item['content']);
97 $entry->setLanguage($item['language']);
98
99 if (isset($item['tags'])) {
100 foreach ($item['tags'] as $tag) {
101 $entry->addTag($this->getReference($tag));
102 }
103 }
104
105 if (isset($item['origin'])) {
106 $entry->setOriginUrl($item['origin']);
107 }
108
109 if (isset($item['starred'])) {
110 $entry->setStarred($item['starred']);
111 }
112
113 if (isset($item['archived'])) {
114 $entry->setArchived($item['archived']);
115 }
116
117 if (isset($item['preview'])) {
118 $entry->setPreviewPicture($item['preview']);
119 }
120
121 $manager->persist($entry);
122 $this->addReference($reference, $entry);
123 }
109 124
110 $manager->flush(); 125 $manager->flush();
111 } 126 }
diff --git a/src/Wallabag/CoreBundle/DataFixtures/TagFixtures.php b/src/Wallabag/CoreBundle/DataFixtures/TagFixtures.php
index d78dd0b8..58a0d799 100644
--- a/src/Wallabag/CoreBundle/DataFixtures/TagFixtures.php
+++ b/src/Wallabag/CoreBundle/DataFixtures/TagFixtures.php
@@ -13,33 +13,22 @@ class TagFixtures extends Fixture
13 */ 13 */
14 public function load(ObjectManager $manager) 14 public function load(ObjectManager $manager)
15 { 15 {
16 $tag1 = new Tag(); 16 $tags = [
17 $tag1->setLabel('foo bar'); 17 'foo-bar-tag' => 'foo bar', //tag used for EntryControllerTest
18 18 'bar-tag' => 'bar',
19 $manager->persist($tag1); 19 'baz-tag' => 'baz', // tag used for ExportControllerTest
20 20 'foo-tag' => 'foo',
21 $this->addReference('foo-bar-tag', $tag1); 21 'bob-tag' => 'bob', // tag used for TagRestControllerTest
22 22 ];
23 $tag2 = new Tag(); 23
24 $tag2->setLabel('bar'); 24 foreach ($tags as $reference => $label) {
25 25 $tag = new Tag();
26 $manager->persist($tag2); 26 $tag->setLabel($label);
27 27
28 $this->addReference('bar-tag', $tag2); 28 $manager->persist($tag);
29 29
30 $tag3 = new Tag(); 30 $this->addReference($reference, $tag);
31 $tag3->setLabel('baz'); 31 }
32
33 $manager->persist($tag3);
34
35 $this->addReference('baz-tag', $tag3);
36
37 $tag4 = new Tag();
38 $tag4->setLabel('foo');
39
40 $manager->persist($tag4);
41
42 $this->addReference('foo-tag', $tag4);
43 32
44 $manager->flush(); 33 $manager->flush();
45 } 34 }
diff --git a/src/Wallabag/CoreBundle/Entity/SiteCredential.php b/src/Wallabag/CoreBundle/Entity/SiteCredential.php
index ac714359..dee48fd5 100644
--- a/src/Wallabag/CoreBundle/Entity/SiteCredential.php
+++ b/src/Wallabag/CoreBundle/Entity/SiteCredential.php
@@ -60,6 +60,13 @@ class SiteCredential
60 private $createdAt; 60 private $createdAt;
61 61
62 /** 62 /**
63 * @var \DateTime
64 *
65 * @ORM\Column(name="updated_at", type="datetime")
66 */
67 private $updatedAt;
68
69 /**
63 * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="siteCredentials") 70 * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="siteCredentials")
64 */ 71 */
65 private $user; 72 private $user;
@@ -179,6 +186,16 @@ class SiteCredential
179 } 186 }
180 187
181 /** 188 /**
189 * Get updatedAt.
190 *
191 * @return \DateTime
192 */
193 public function getUpdatedAt()
194 {
195 return $this->updatedAt;
196 }
197
198 /**
182 * @return User 199 * @return User
183 */ 200 */
184 public function getUser() 201 public function getUser()
diff --git a/src/Wallabag/CoreBundle/Helper/EntriesExport.php b/src/Wallabag/CoreBundle/Helper/EntriesExport.php
index cbf1037b..64591687 100644
--- a/src/Wallabag/CoreBundle/Helper/EntriesExport.php
+++ b/src/Wallabag/CoreBundle/Helper/EntriesExport.php
@@ -85,7 +85,7 @@ class EntriesExport
85 public function updateAuthor($method) 85 public function updateAuthor($method)
86 { 86 {
87 if ('entry' !== $method) { 87 if ('entry' !== $method) {
88 $this->author = $method . ' authors'; 88 $this->author = 'Various authors';
89 89
90 return $this; 90 return $this;
91 } 91 }
@@ -150,8 +150,6 @@ class EntriesExport
150 */ 150 */
151 151
152 $book->setTitle($this->title); 152 $book->setTitle($this->title);
153 // Could also be the ISBN number, prefered for published books, or a UUID.
154 $book->setIdentifier($this->title, EPub::IDENTIFIER_URI);
155 // Not needed, but included for the example, Language is mandatory, but EPub defaults to "en". Use RFC3066 Language codes, such as "en", "da", "fr" etc. 153 // Not needed, but included for the example, Language is mandatory, but EPub defaults to "en". Use RFC3066 Language codes, such as "en", "da", "fr" etc.
156 $book->setLanguage($this->language); 154 $book->setLanguage($this->language);
157 $book->setDescription('Some articles saved on my wallabag'); 155 $book->setDescription('Some articles saved on my wallabag');
@@ -174,27 +172,49 @@ class EntriesExport
174 $book->setCoverImage('Cover.png', file_get_contents($this->logoPath), 'image/png'); 172 $book->setCoverImage('Cover.png', file_get_contents($this->logoPath), 'image/png');
175 } 173 }
176 174
175 $entryIds = [];
176 $entryCount = \count($this->entries);
177 $i = 0;
178
177 /* 179 /*
178 * Adding actual entries 180 * Adding actual entries
179 */ 181 */
180 182
181 // set tags as subjects 183 // set tags as subjects
182 foreach ($this->entries as $entry) { 184 foreach ($this->entries as $entry) {
185 ++$i;
183 foreach ($entry->getTags() as $tag) { 186 foreach ($entry->getTags() as $tag) {
184 $book->setSubject($tag->getLabel()); 187 $book->setSubject($tag->getLabel());
185 } 188 }
189 $filename = sha1($entry->getTitle());
186 190
187 // the reader in Kobo Devices doesn't likes special caracters 191 $publishedBy = $entry->getPublishedBy();
188 // in filenames, we limit to A-z/0-9 192 $authors = $this->translator->trans('export.unknown');
189 $filename = preg_replace('/[^A-Za-z0-9\-]/', '', $entry->getTitle()); 193 if (!empty($publishedBy)) {
194 $authors = implode(',', $publishedBy);
195 }
190 196
191 $titlepage = $content_start . '<h1>' . $entry->getTitle() . '</h1>' . $this->getExportInformation('PHPePub') . $bookEnd; 197 $titlepage = $content_start .
192 $book->addChapter('Title', 'Title.html', $titlepage, true, EPub::EXTERNAL_REF_ADD); 198 '<h1>' . $entry->getTitle() . '</h1>' .
199 '<dl>' .
200 '<dt>' . $this->translator->trans('entry.view.published_by') . '</dt><dd>' . $authors . '</dd>' .
201 '<dt>' . $this->translator->trans('entry.metadata.reading_time') . '</dt><dd>' . $this->translator->trans('entry.metadata.reading_time_minutes_short', ['%readingTime%' => $entry->getReadingTime()]) . '</dd>' .
202 '<dt>' . $this->translator->trans('entry.metadata.added_on') . '</dt><dd>' . $entry->getCreatedAt()->format('Y-m-d') . '</dd>' .
203 '<dt>' . $this->translator->trans('entry.metadata.address') . '</dt><dd><a href="' . $entry->getUrl() . '">' . $entry->getUrl() . '</a></dd>' .
204 '</dl>' .
205 $bookEnd;
206 $book->addChapter("Entry {$i} of {$entryCount}", "{$filename}_cover.html", $titlepage, true, EPub::EXTERNAL_REF_ADD);
193 $chapter = $content_start . $entry->getContent() . $bookEnd; 207 $chapter = $content_start . $entry->getContent() . $bookEnd;
194 $book->addChapter($entry->getTitle(), htmlspecialchars($filename) . '.html', $chapter, true, EPub::EXTERNAL_REF_ADD); 208
209 $entryIds[] = $entry->getId();
210 $book->addChapter($entry->getTitle(), "{$filename}.html", $chapter, true, EPub::EXTERNAL_REF_ADD);
195 } 211 }
196 212
197 $book->buildTOC(); 213 $book->addChapter('Notices', 'Cover2.html', $content_start . $this->getExportInformation('PHPePub') . $bookEnd);
214
215 // Could also be the ISBN number, prefered for published books, or a UUID.
216 $hash = sha1(sprintf('%s:%s', $this->wallabagUrl, implode(',', $entryIds)));
217 $book->setIdentifier(sprintf('urn:wallabag:%s', $hash), EPub::IDENTIFIER_URI);
198 218
199 return Response::create( 219 return Response::create(
200 $book->getBook(), 220 $book->getBook(),
@@ -202,7 +222,7 @@ class EntriesExport
202 [ 222 [
203 'Content-Description' => 'File Transfer', 223 'Content-Description' => 'File Transfer',
204 'Content-type' => 'application/epub+zip', 224 'Content-type' => 'application/epub+zip',
205 'Content-Disposition' => 'attachment; filename="' . $this->title . '.epub"', 225 'Content-Disposition' => 'attachment; filename="' . $this->getSanitizedFilename() . '.epub"',
206 'Content-Transfer-Encoding' => 'binary', 226 'Content-Transfer-Encoding' => 'binary',
207 ] 227 ]
208 ); 228 );
@@ -244,9 +264,6 @@ class EntriesExport
244 } 264 }
245 $mobi->setContentProvider($content); 265 $mobi->setContentProvider($content);
246 266
247 // the browser inside Kindle Devices doesn't likes special caracters either, we limit to A-z/0-9
248 $this->title = preg_replace('/[^A-Za-z0-9\-]/', '', $this->title);
249
250 return Response::create( 267 return Response::create(
251 $mobi->toString(), 268 $mobi->toString(),
252 200, 269 200,
@@ -254,7 +271,7 @@ class EntriesExport
254 'Accept-Ranges' => 'bytes', 271 'Accept-Ranges' => 'bytes',
255 'Content-Description' => 'File Transfer', 272 'Content-Description' => 'File Transfer',
256 'Content-type' => 'application/x-mobipocket-ebook', 273 'Content-type' => 'application/x-mobipocket-ebook',
257 'Content-Disposition' => 'attachment; filename="' . $this->title . '.mobi"', 274 'Content-Disposition' => 'attachment; filename="' . $this->getSanitizedFilename() . '.mobi"',
258 'Content-Transfer-Encoding' => 'binary', 275 'Content-Transfer-Encoding' => 'binary',
259 ] 276 ]
260 ); 277 );
@@ -279,14 +296,6 @@ class EntriesExport
279 $pdf->SetKeywords('wallabag'); 296 $pdf->SetKeywords('wallabag');
280 297
281 /* 298 /*
282 * Front page
283 */
284 $pdf->AddPage();
285 $intro = '<h1>' . $this->title . '</h1>' . $this->getExportInformation('tcpdf');
286
287 $pdf->writeHTMLCell(0, 0, '', '', $intro, 0, 1, 0, true, '', true);
288
289 /*
290 * Adding actual entries 299 * Adding actual entries
291 */ 300 */
292 foreach ($this->entries as $entry) { 301 foreach ($this->entries as $entry) {
@@ -294,6 +303,22 @@ class EntriesExport
294 $pdf->SetKeywords($tag->getLabel()); 303 $pdf->SetKeywords($tag->getLabel());
295 } 304 }
296 305
306 $publishedBy = $entry->getPublishedBy();
307 $authors = $this->translator->trans('export.unknown');
308 if (!empty($publishedBy)) {
309 $authors = implode(',', $publishedBy);
310 }
311
312 $pdf->addPage();
313 $html = '<h1>' . $entry->getTitle() . '</h1>' .
314 '<dl>' .
315 '<dt>' . $this->translator->trans('entry.view.published_by') . '</dt><dd>' . $authors . '</dd>' .
316 '<dt>' . $this->translator->trans('entry.metadata.reading_time') . '</dt><dd>' . $this->translator->trans('entry.metadata.reading_time_minutes_short', ['%readingTime%' => $entry->getReadingTime()]) . '</dd>' .
317 '<dt>' . $this->translator->trans('entry.metadata.added_on') . '</dt><dd>' . $entry->getCreatedAt()->format('Y-m-d') . '</dd>' .
318 '<dt>' . $this->translator->trans('entry.metadata.address') . '</dt><dd><a href="' . $entry->getUrl() . '">' . $entry->getUrl() . '</a></dd>' .
319 '</dl>';
320 $pdf->writeHTMLCell(0, 0, '', '', $html, 0, 1, 0, true, '', true);
321
297 $pdf->AddPage(); 322 $pdf->AddPage();
298 $html = '<h1>' . $entry->getTitle() . '</h1>'; 323 $html = '<h1>' . $entry->getTitle() . '</h1>';
299 $html .= $entry->getContent(); 324 $html .= $entry->getContent();
@@ -301,6 +326,14 @@ class EntriesExport
301 $pdf->writeHTMLCell(0, 0, '', '', $html, 0, 1, 0, true, '', true); 326 $pdf->writeHTMLCell(0, 0, '', '', $html, 0, 1, 0, true, '', true);
302 } 327 }
303 328
329 /*
330 * Last page
331 */
332 $pdf->AddPage();
333 $html = $this->getExportInformation('tcpdf');
334
335 $pdf->writeHTMLCell(0, 0, '', '', $html, 0, 1, 0, true, '', true);
336
304 // set image scale factor 337 // set image scale factor
305 $pdf->setImageScale(PDF_IMAGE_SCALE_RATIO); 338 $pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
306 339
@@ -310,7 +343,7 @@ class EntriesExport
310 [ 343 [
311 'Content-Description' => 'File Transfer', 344 'Content-Description' => 'File Transfer',
312 'Content-type' => 'application/pdf', 345 'Content-type' => 'application/pdf',
313 'Content-Disposition' => 'attachment; filename="' . $this->title . '.pdf"', 346 'Content-Disposition' => 'attachment; filename="' . $this->getSanitizedFilename() . '.pdf"',
314 'Content-Transfer-Encoding' => 'binary', 347 'Content-Transfer-Encoding' => 'binary',
315 ] 348 ]
316 ); 349 );
@@ -356,7 +389,7 @@ class EntriesExport
356 200, 389 200,
357 [ 390 [
358 'Content-type' => 'application/csv', 391 'Content-type' => 'application/csv',
359 'Content-Disposition' => 'attachment; filename="' . $this->title . '.csv"', 392 'Content-Disposition' => 'attachment; filename="' . $this->getSanitizedFilename() . '.csv"',
360 'Content-Transfer-Encoding' => 'UTF-8', 393 'Content-Transfer-Encoding' => 'UTF-8',
361 ] 394 ]
362 ); 395 );
@@ -374,7 +407,7 @@ class EntriesExport
374 200, 407 200,
375 [ 408 [
376 'Content-type' => 'application/json', 409 'Content-type' => 'application/json',
377 'Content-Disposition' => 'attachment; filename="' . $this->title . '.json"', 410 'Content-Disposition' => 'attachment; filename="' . $this->getSanitizedFilename() . '.json"',
378 'Content-Transfer-Encoding' => 'UTF-8', 411 'Content-Transfer-Encoding' => 'UTF-8',
379 ] 412 ]
380 ); 413 );
@@ -392,7 +425,7 @@ class EntriesExport
392 200, 425 200,
393 [ 426 [
394 'Content-type' => 'application/xml', 427 'Content-type' => 'application/xml',
395 'Content-Disposition' => 'attachment; filename="' . $this->title . '.xml"', 428 'Content-Disposition' => 'attachment; filename="' . $this->getSanitizedFilename() . '.xml"',
396 'Content-Transfer-Encoding' => 'UTF-8', 429 'Content-Transfer-Encoding' => 'UTF-8',
397 ] 430 ]
398 ); 431 );
@@ -418,7 +451,7 @@ class EntriesExport
418 200, 451 200,
419 [ 452 [
420 'Content-type' => 'text/plain', 453 'Content-type' => 'text/plain',
421 'Content-Disposition' => 'attachment; filename="' . $this->title . '.txt"', 454 'Content-Disposition' => 'attachment; filename="' . $this->getSanitizedFilename() . '.txt"',
422 'Content-Transfer-Encoding' => 'UTF-8', 455 'Content-Transfer-Encoding' => 'UTF-8',
423 ] 456 ]
424 ); 457 );
@@ -461,4 +494,15 @@ class EntriesExport
461 494
462 return str_replace('%IMAGE%', '', $info); 495 return str_replace('%IMAGE%', '', $info);
463 } 496 }
497
498 /**
499 * Return a sanitized version of the title by applying translit iconv
500 * and removing non alphanumeric characters, - and space.
501 *
502 * @return string Sanitized filename
503 */
504 private function getSanitizedFilename()
505 {
506 return preg_replace('/[^A-Za-z0-9\- \']/', '', iconv('utf-8', 'us-ascii//TRANSLIT', $this->title));
507 }
464} 508}
diff --git a/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php
index 63f65067..fbdf2ac7 100644
--- a/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php
+++ b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php
@@ -6,6 +6,7 @@ use Psr\Log\LoggerInterface;
6use RulerZ\RulerZ; 6use RulerZ\RulerZ;
7use Wallabag\CoreBundle\Entity\Entry; 7use Wallabag\CoreBundle\Entity\Entry;
8use Wallabag\CoreBundle\Entity\Tag; 8use Wallabag\CoreBundle\Entity\Tag;
9use Wallabag\CoreBundle\Entity\TaggingRule;
9use Wallabag\CoreBundle\Repository\EntryRepository; 10use Wallabag\CoreBundle\Repository\EntryRepository;
10use Wallabag\CoreBundle\Repository\TagRepository; 11use Wallabag\CoreBundle\Repository\TagRepository;
11use Wallabag\UserBundle\Entity\User; 12use Wallabag\UserBundle\Entity\User;
diff --git a/src/Wallabag/CoreBundle/Repository/EntryRepository.php b/src/Wallabag/CoreBundle/Repository/EntryRepository.php
index 93c630c0..8b6cf443 100644
--- a/src/Wallabag/CoreBundle/Repository/EntryRepository.php
+++ b/src/Wallabag/CoreBundle/Repository/EntryRepository.php
@@ -142,7 +142,7 @@ class EntryRepository extends EntityRepository
142 * 142 *
143 * @return Pagerfanta 143 * @return Pagerfanta
144 */ 144 */
145 public function findEntries($userId, $isArchived = null, $isStarred = null, $isPublic = null, $sort = 'created', $order = 'ASC', $since = 0, $tags = '') 145 public function findEntries($userId, $isArchived = null, $isStarred = null, $isPublic = null, $sort = 'created', $order = 'asc', $since = 0, $tags = '')
146 { 146 {
147 $qb = $this->createQueryBuilder('e') 147 $qb = $this->createQueryBuilder('e')
148 ->leftJoin('e.tags', 't') 148 ->leftJoin('e.tags', 't')
@@ -185,6 +185,10 @@ class EntryRepository extends EntityRepository
185 } 185 }
186 } 186 }
187 187
188 if (!\in_array(strtolower($order), ['asc', 'desc'], true)) {
189 throw new \Exception('Order "' . $order . '" parameter is wrong, allowed: asc or desc');
190 }
191
188 if ('created' === $sort) { 192 if ('created' === $sort) {
189 $qb->orderBy('e.id', $order); 193 $qb->orderBy('e.id', $order);
190 } elseif ('updated' === $sort) { 194 } elseif ('updated' === $sort) {
diff --git a/src/Wallabag/CoreBundle/Repository/TagRepository.php b/src/Wallabag/CoreBundle/Repository/TagRepository.php
index 3ae9d414..8464a6a5 100644
--- a/src/Wallabag/CoreBundle/Repository/TagRepository.php
+++ b/src/Wallabag/CoreBundle/Repository/TagRepository.php
@@ -3,6 +3,7 @@
3namespace Wallabag\CoreBundle\Repository; 3namespace Wallabag\CoreBundle\Repository;
4 4
5use Doctrine\ORM\EntityRepository; 5use Doctrine\ORM\EntityRepository;
6use Doctrine\ORM\QueryBuilder;
6use Wallabag\CoreBundle\Entity\Tag; 7use Wallabag\CoreBundle\Entity\Tag;
7 8
8class TagRepository extends EntityRepository 9class TagRepository extends EntityRepository
@@ -45,12 +46,8 @@ class TagRepository extends EntityRepository
45 */ 46 */
46 public function findAllTags($userId) 47 public function findAllTags($userId)
47 { 48 {
48 $ids = $this->createQueryBuilder('t') 49 $ids = $this->getQueryBuilderByUser($userId)
49 ->select('t.id') 50 ->select('t.id')
50 ->leftJoin('t.entries', 'e')
51 ->where('e.user = :userId')->setParameter('userId', $userId)
52 ->groupBy('t.id')
53 ->orderBy('t.slug')
54 ->getQuery() 51 ->getQuery()
55 ->getArrayResult(); 52 ->getArrayResult();
56 53
@@ -71,18 +68,30 @@ class TagRepository extends EntityRepository
71 */ 68 */
72 public function findAllFlatTagsWithNbEntries($userId) 69 public function findAllFlatTagsWithNbEntries($userId)
73 { 70 {
74 return $this->createQueryBuilder('t') 71 return $this->getQueryBuilderByUser($userId)
75 ->select('t.id, t.label, t.slug, count(e.id) as nbEntries') 72 ->select('t.id, t.label, t.slug, count(e.id) as nbEntries')
76 ->distinct(true) 73 ->distinct(true)
77 ->leftJoin('t.entries', 'e')
78 ->where('e.user = :userId')
79 ->groupBy('t.id')
80 ->orderBy('t.slug')
81 ->setParameter('userId', $userId)
82 ->getQuery() 74 ->getQuery()
83 ->getArrayResult(); 75 ->getArrayResult();
84 } 76 }
85 77
78 public function findByLabelsAndUser($labels, $userId)
79 {
80 $qb = $this->getQueryBuilderByUser($userId)
81 ->select('t.id');
82
83 $ids = $qb->andWhere($qb->expr()->in('t.label', $labels))
84 ->getQuery()
85 ->getArrayResult();
86
87 $tags = [];
88 foreach ($ids as $id) {
89 $tags[] = $this->find($id);
90 }
91
92 return $tags;
93 }
94
86 /** 95 /**
87 * Used only in test case to get a tag for our entry. 96 * Used only in test case to get a tag for our entry.
88 * 97 *
@@ -101,13 +110,9 @@ class TagRepository extends EntityRepository
101 110
102 public function findForArchivedArticlesByUser($userId) 111 public function findForArchivedArticlesByUser($userId)
103 { 112 {
104 $ids = $this->createQueryBuilder('t') 113 $ids = $this->getQueryBuilderByUser($userId)
105 ->select('t.id') 114 ->select('t.id')
106 ->leftJoin('t.entries', 'e')
107 ->where('e.user = :userId')->setParameter('userId', $userId)
108 ->andWhere('e.isArchived = true') 115 ->andWhere('e.isArchived = true')
109 ->groupBy('t.id')
110 ->orderBy('t.slug')
111 ->getQuery() 116 ->getQuery()
112 ->getArrayResult(); 117 ->getArrayResult();
113 118
@@ -118,4 +123,20 @@ class TagRepository extends EntityRepository
118 123
119 return $tags; 124 return $tags;
120 } 125 }
126
127 /**
128 * Retrieve a sorted list of tags used by a user.
129 *
130 * @param int $userId
131 *
132 * @return QueryBuilder
133 */
134 private function getQueryBuilderByUser($userId)
135 {
136 return $this->createQueryBuilder('t')
137 ->leftJoin('t.entries', 'e')
138 ->where('e.user = :userId')->setParameter('userId', $userId)
139 ->groupBy('t.id')
140 ->orderBy('t.slug');
141 }
121} 142}
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
index c6a1ff05..4cf69916 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
@@ -253,6 +253,11 @@ entry:
253 confirm: 253 confirm:
254 # delete: "Are you sure you want to remove that article?" 254 # delete: "Are you sure you want to remove that article?"
255 # delete_tag: "Are you sure you want to remove that tag from that article?" 255 # delete_tag: "Are you sure you want to remove that tag from that article?"
256 metadata:
257 # reading_time: "Estimated reading time"
258 # reading_time_minutes_short: "%readingTime% min"
259 # address: "Address"
260 # added_on: "Added on"
256 261
257about: 262about:
258 page_title: 'Om' 263 page_title: 'Om'
@@ -404,6 +409,7 @@ tag:
404 409
405# export: 410# export:
406# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' 411# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
412# unknown: 'Unknown'
407 413
408import: 414import:
409 # page_title: 'Import' 415 # page_title: 'Import'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
index 888d9b39..10981788 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
@@ -253,6 +253,11 @@ entry:
253 confirm: 253 confirm:
254 delete: 'Bist du sicher, dass du diesen Artikel löschen möchtest?' 254 delete: 'Bist du sicher, dass du diesen Artikel löschen möchtest?'
255 delete_tag: 'Bist du sicher, dass du diesen Tag vom Artikel entfernen möchtest?' 255 delete_tag: 'Bist du sicher, dass du diesen Tag vom Artikel entfernen möchtest?'
256 metadata:
257 # reading_time: "Estimated reading time"
258 # reading_time_minutes_short: "%readingTime% min"
259 # address: "Address"
260 # added_on: "Added on"
256 261
257about: 262about:
258 page_title: 'Über' 263 page_title: 'Über'
@@ -404,6 +409,7 @@ tag:
404 409
405export: 410export:
406 footer_template: '<div style="text-align:center;"><p>Generiert von wallabag mit Hilfe von %method%</p><p>Bitte öffne <a href="https://github.com/wallabag/wallabag/issues">ein Ticket</a> wenn du ein Problem mit der Darstellung von diesem E-Book auf deinem Gerät hast.</p></div>' 411 footer_template: '<div style="text-align:center;"><p>Generiert von wallabag mit Hilfe von %method%</p><p>Bitte öffne <a href="https://github.com/wallabag/wallabag/issues">ein Ticket</a> wenn du ein Problem mit der Darstellung von diesem E-Book auf deinem Gerät hast.</p></div>'
412 # unknown: 'Unknown'
407 413
408import: 414import:
409 page_title: 'Importieren' 415 page_title: 'Importieren'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
index e13987ae..95e10faf 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
@@ -253,6 +253,11 @@ entry:
253 confirm: 253 confirm:
254 delete: "Are you sure you want to remove that article?" 254 delete: "Are you sure you want to remove that article?"
255 delete_tag: "Are you sure you want to remove that tag from that article?" 255 delete_tag: "Are you sure you want to remove that tag from that article?"
256 metadata:
257 reading_time: "Estimated reading time"
258 reading_time_minutes_short: "%readingTime% min"
259 address: "Address"
260 added_on: "Added on"
256 261
257about: 262about:
258 page_title: 'About' 263 page_title: 'About'
@@ -404,6 +409,7 @@ tag:
404 409
405export: 410export:
406 footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' 411 footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
412 unknown: 'Unknown'
407 413
408import: 414import:
409 page_title: 'Import' 415 page_title: 'Import'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
index ef4b278a..c95bee5b 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
@@ -253,6 +253,11 @@ entry:
253 confirm: 253 confirm:
254 # delete: "Are you sure you want to remove that article?" 254 # delete: "Are you sure you want to remove that article?"
255 # delete_tag: "Are you sure you want to remove that tag from that article?" 255 # delete_tag: "Are you sure you want to remove that tag from that article?"
256 metadata:
257 # reading_time: "Estimated reading time"
258 # reading_time_minutes_short: "%readingTime% min"
259 # address: "Address"
260 # added_on: "Added on"
256 261
257about: 262about:
258 page_title: 'Acerca de' 263 page_title: 'Acerca de'
@@ -404,6 +409,7 @@ tag:
404 409
405# export: 410# export:
406# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' 411# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
412# unknown: 'Unknown'
407 413
408import: 414import:
409 page_title: 'Importar' 415 page_title: 'Importar'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
index a13b6668..4fde53dd 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
@@ -253,6 +253,11 @@ entry:
253 confirm: 253 confirm:
254 # delete: "Are you sure you want to remove that article?" 254 # delete: "Are you sure you want to remove that article?"
255 # delete_tag: "Are you sure you want to remove that tag from that article?" 255 # delete_tag: "Are you sure you want to remove that tag from that article?"
256 metadata:
257 # reading_time: "Estimated reading time"
258 # reading_time_minutes_short: "%readingTime% min"
259 # address: "Address"
260 # added_on: "Added on"
256 261
257about: 262about:
258 page_title: 'درباره' 263 page_title: 'درباره'
@@ -404,6 +409,7 @@ tag:
404 409
405# export: 410# export:
406# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' 411# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
412# unknown: 'Unknown'
407 413
408import: 414import:
409 page_title: 'درون‌ریزی' 415 page_title: 'درون‌ریزی'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
index 2d4d0acd..edf3ac35 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
@@ -253,6 +253,11 @@ entry:
253 confirm: 253 confirm:
254 delete: "Voulez-vous vraiment supprimer cet article ?" 254 delete: "Voulez-vous vraiment supprimer cet article ?"
255 delete_tag: "Voulez-vous vraiment supprimer ce tag de cet article ?" 255 delete_tag: "Voulez-vous vraiment supprimer ce tag de cet article ?"
256 metadata:
257 reading_time: "Durée de lecture estimée"
258 reading_time_minutes_short: "%readingTime% min"
259 address: "Adresse"
260 added_on: "Ajouté le"
256 261
257about: 262about:
258 page_title: "À propos" 263 page_title: "À propos"
@@ -404,6 +409,7 @@ tag:
404 409
405export: 410export:
406 footer_template: '<div style="text-align:center;"><p>Généré par wallabag with %method%</p><p>Merci d''ouvrir <a href="https://github.com/wallabag/wallabag/issues">un ticket</a> si vous rencontrez des soucis d''affichage avec ce document sur votre support.</p></div>' 411 footer_template: '<div style="text-align:center;"><p>Généré par wallabag with %method%</p><p>Merci d''ouvrir <a href="https://github.com/wallabag/wallabag/issues">un ticket</a> si vous rencontrez des soucis d''affichage avec ce document sur votre support.</p></div>'
412 unknown: 'Inconnu'
407 413
408import: 414import:
409 page_title: "Importer" 415 page_title: "Importer"
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
index e854c323..f178ddbf 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
@@ -253,6 +253,11 @@ entry:
253 confirm: 253 confirm:
254 delete: "Vuoi veramente rimuovere quell'articolo?" 254 delete: "Vuoi veramente rimuovere quell'articolo?"
255 delete_tag: "Vuoi veramente rimuovere quell'etichetta da quell'articolo?" 255 delete_tag: "Vuoi veramente rimuovere quell'etichetta da quell'articolo?"
256 metadata:
257 # reading_time: "Estimated reading time"
258 # reading_time_minutes_short: "%readingTime% min"
259 # address: "Address"
260 # added_on: "Added on"
256 261
257about: 262about:
258 page_title: 'A proposito' 263 page_title: 'A proposito'
@@ -404,6 +409,7 @@ tag:
404 409
405# export: 410# export:
406# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' 411# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
412# unknown: 'Unknown'
407 413
408import: 414import:
409 page_title: 'Importa' 415 page_title: 'Importa'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
index 9e9f8a2f..a1220f52 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
@@ -253,6 +253,11 @@ entry:
253 confirm: 253 confirm:
254 delete: "Sètz segur de voler suprimir aqueste article ?" 254 delete: "Sètz segur de voler suprimir aqueste article ?"
255 delete_tag: "Sètz segur de voler levar aquesta etiqueta de l'article ?" 255 delete_tag: "Sètz segur de voler levar aquesta etiqueta de l'article ?"
256 metadata:
257 # reading_time: "Estimated reading time"
258 # reading_time_minutes_short: "%readingTime% min"
259 # address: "Address"
260 # added_on: "Added on"
256 261
257about: 262about:
258 page_title: 'A prepaus' 263 page_title: 'A prepaus'
@@ -404,6 +409,7 @@ tag:
404 409
405export: 410export:
406 footer_template: '<div style="text-align:center;"><p>Produch per wallabag amb %method%</p><p>Mercés de dobrir <a href="https://github.com/wallabag/wallabag/issues">una sollicitacion</a> s’avètz de problèmas amb l’afichatge d’aqueste E-Book sus vòstre periferic.</p></div>' 411 footer_template: '<div style="text-align:center;"><p>Produch per wallabag amb %method%</p><p>Mercés de dobrir <a href="https://github.com/wallabag/wallabag/issues">una sollicitacion</a> s’avètz de problèmas amb l’afichatge d’aqueste E-Book sus vòstre periferic.</p></div>'
412 # unknown: 'Unknown'
407 413
408import: 414import:
409 page_title: 'Importar' 415 page_title: 'Importar'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
index 83f6d2e6..b6f7faf7 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
@@ -253,6 +253,11 @@ entry:
253 confirm: 253 confirm:
254 delete: "Czy jesteś pewien, że chcesz usunąć ten artykuł?" 254 delete: "Czy jesteś pewien, że chcesz usunąć ten artykuł?"
255 delete_tag: "Czy jesteś pewien, że chcesz usunąć ten tag, z tego artykułu?" 255 delete_tag: "Czy jesteś pewien, że chcesz usunąć ten tag, z tego artykułu?"
256 metadata:
257 # reading_time: "Estimated reading time"
258 # reading_time_minutes_short: "%readingTime% min"
259 # address: "Address"
260 # added_on: "Added on"
256 261
257about: 262about:
258 page_title: 'O nas' 263 page_title: 'O nas'
@@ -403,7 +408,8 @@ tag:
403 placeholder: 'Możesz zaktualizować nazwę taga.' 408 placeholder: 'Możesz zaktualizować nazwę taga.'
404 409
405export: 410export:
406 footer_template: '<div style="text-align:center;"><p>Stworzone przez wallabag z %method%</p><p>Proszę zgłoś <a href="https://github.com/wallabag/wallabag/issues">sprawę</a>, jeżeli masz problem z wyświetleniem tego e-booka na swoim urządzeniu.</p></div>' 411 footer_template: '<div style="text-align:center;"><p>Stworzone przez wallabag z %method%</p><p>Proszę zgłoś <a href="https://github.com/wallabag/wallabag/issues">sprawę</a>, jeżeli masz problem z wyświetleniem tego e-booka na swoim urządzeniu.</p></div>'
412 # unknown: 'Unknown'
407 413
408import: 414import:
409 page_title: 'Import' 415 page_title: 'Import'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
index db5830d2..78df254f 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
@@ -253,6 +253,11 @@ entry:
253 confirm: 253 confirm:
254 # delete: "Are you sure you want to remove that article?" 254 # delete: "Are you sure you want to remove that article?"
255 # delete_tag: "Are you sure you want to remove that tag from that article?" 255 # delete_tag: "Are you sure you want to remove that tag from that article?"
256 metadata:
257 # reading_time: "Estimated reading time"
258 # reading_time_minutes_short: "%readingTime% min"
259 # address: "Address"
260 # added_on: "Added on"
256 261
257about: 262about:
258 page_title: 'Sobre' 263 page_title: 'Sobre'
@@ -404,6 +409,7 @@ tag:
404 409
405# export: 410# export:
406# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' 411# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
412# unknown: 'Unknown'
407 413
408import: 414import:
409 page_title: 'Importar' 415 page_title: 'Importar'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
index e7daf880..8312ca15 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
@@ -253,6 +253,11 @@ entry:
253 confirm: 253 confirm:
254 # delete: "Are you sure you want to remove that article?" 254 # delete: "Are you sure you want to remove that article?"
255 # delete_tag: "Are you sure you want to remove that tag from that article?" 255 # delete_tag: "Are you sure you want to remove that tag from that article?"
256 metadata:
257 # reading_time: "Estimated reading time"
258 # reading_time_minutes_short: "%readingTime% min"
259 # address: "Address"
260 # added_on: "Added on"
256 261
257about: 262about:
258 page_title: 'Despre' 263 page_title: 'Despre'
@@ -404,6 +409,7 @@ tag:
404 409
405# export: 410# export:
406# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' 411# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
412# unknown: 'Unknown'
407 413
408import: 414import:
409 # page_title: 'Import' 415 # page_title: 'Import'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.ru.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.ru.yml
index d713f13f..f14aad12 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.ru.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.ru.yml
@@ -241,6 +241,11 @@ entry:
241 save_label: 'Сохранить' 241 save_label: 'Сохранить'
242 public: 242 public:
243 shared_by_wallabag: "Запись была опубликована <a href='%wallabag_instance%'>wallabag</a>" 243 shared_by_wallabag: "Запись была опубликована <a href='%wallabag_instance%'>wallabag</a>"
244 metadata:
245 # reading_time: "Estimated reading time"
246 # reading_time_minutes_short: "%readingTime% min"
247 # address: "Address"
248 # added_on: "Added on"
244 249
245about: 250about:
246 page_title: 'О' 251 page_title: 'О'
@@ -390,6 +395,10 @@ tag:
390 rename: 395 rename:
391 # placeholder: 'You can update tag name.' 396 # placeholder: 'You can update tag name.'
392 397
398# export:
399# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
400# unknown: 'Unknown'
401
393import: 402import:
394 page_title: 'Импорт' 403 page_title: 'Импорт'
395 page_description: 'Добро пожаловать в импортер wallabag. Выберите сервис, из которого вы хотите перенести данные.' 404 page_description: 'Добро пожаловать в импортер wallabag. Выберите сервис, из которого вы хотите перенести данные.'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.th.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.th.yml
index 78e0f0ee..7dbb1399 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.th.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.th.yml
@@ -251,6 +251,11 @@ entry:
251 confirm: 251 confirm:
252 delete: "คุณแน่ใจหรือไม่ว่าคุณต้องการลบบทความนี้?" 252 delete: "คุณแน่ใจหรือไม่ว่าคุณต้องการลบบทความนี้?"
253 delete_tag: "คุณแน่ใจหรือไม่ว่าคุณต้องการลบแท็กจากบทความนี้?" 253 delete_tag: "คุณแน่ใจหรือไม่ว่าคุณต้องการลบแท็กจากบทความนี้?"
254 metadata:
255 # reading_time: "Estimated reading time"
256 # reading_time_minutes_short: "%readingTime% min"
257 # address: "Address"
258 # added_on: "Added on"
254 259
255about: 260about:
256 page_title: 'เกี่ยวกับ' 261 page_title: 'เกี่ยวกับ'
@@ -402,6 +407,7 @@ tag:
402 407
403export: 408export:
404 footer_template: '<div style="text-align:center;"><p>ผลิตโดย wallabag กับ %method%</p><p>ให้ทำการเปิด <a href="https://github.com/wallabag/wallabag/issues">ฉบับนี้</a> ถ้าคุณมีข้อบกพร่องif you have trouble with the display of this E-Book on your device.</p></div>' 409 footer_template: '<div style="text-align:center;"><p>ผลิตโดย wallabag กับ %method%</p><p>ให้ทำการเปิด <a href="https://github.com/wallabag/wallabag/issues">ฉบับนี้</a> ถ้าคุณมีข้อบกพร่องif you have trouble with the display of this E-Book on your device.</p></div>'
410 # unknown: 'Unknown'
405 411
406import: 412import:
407 page_title: 'นำข้อมูลเช้า' 413 page_title: 'นำข้อมูลเช้า'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
index edb1ce7c..b4bc04d0 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
@@ -251,6 +251,11 @@ entry:
251 confirm: 251 confirm:
252 # delete: "Are you sure you want to remove that article?" 252 # delete: "Are you sure you want to remove that article?"
253 # delete_tag: "Are you sure you want to remove that tag from that article?" 253 # delete_tag: "Are you sure you want to remove that tag from that article?"
254 metadata:
255 # reading_time: "Estimated reading time"
256 # reading_time_minutes_short: "%readingTime% min"
257 # address: "Address"
258 # added_on: "Added on"
254 259
255about: 260about:
256 page_title: 'Hakkımızda' 261 page_title: 'Hakkımızda'
@@ -402,6 +407,7 @@ tag:
402 407
403# export: 408# export:
404# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>' 409# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
410# unknown: 'Unknown'
405 411
406import: 412import:
407 page_title: 'İçe Aktar' 413 page_title: 'İçe Aktar'
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig
index cfc6644b..832112be 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig
@@ -99,8 +99,8 @@
99 {% if craue_setting('export_epub') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'epub', 'tag' : currentTag }) }}">EPUB</a></li>{% endif %} 99 {% if craue_setting('export_epub') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'epub', 'tag' : currentTag }) }}">EPUB</a></li>{% endif %}
100 {% if craue_setting('export_mobi') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'mobi', 'tag' : currentTag }) }}">MOBI</a></li>{% endif %} 100 {% if craue_setting('export_mobi') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'mobi', 'tag' : currentTag }) }}">MOBI</a></li>{% endif %}
101 {% if craue_setting('export_pdf') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'pdf', 'tag' : currentTag }) }}">PDF</a></li>{% endif %} 101 {% if craue_setting('export_pdf') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'pdf', 'tag' : currentTag }) }}">PDF</a></li>{% endif %}
102 {% if craue_setting('export_csv') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'json', 'tag' : currentTag }) }}">JSON</a></li>{% endif %} 102 {% if craue_setting('export_json') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'json', 'tag' : currentTag }) }}">JSON</a></li>{% endif %}
103 {% if craue_setting('export_json') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'csv', 'tag' : currentTag }) }}">CSV</a></li>{% endif %} 103 {% if craue_setting('export_csv') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'csv', 'tag' : currentTag }) }}">CSV</a></li>{% endif %}
104 {% if craue_setting('export_txt') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'txt', 'tag' : currentTag }) }}">TXT</a></li>{% endif %} 104 {% if craue_setting('export_txt') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'txt', 'tag' : currentTag }) }}">TXT</a></li>{% endif %}
105 {% if craue_setting('export_xml') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'xml', 'tag' : currentTag }) }}">XML</a></li>{% endif %} 105 {% if craue_setting('export_xml') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'xml', 'tag' : currentTag }) }}">XML</a></li>{% endif %}
106 </ul> 106 </ul>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig
index 48866f6e..e7d42b3d 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig
@@ -96,9 +96,6 @@
96 </div> 96 </div>
97 </aside> 97 </aside>
98 </div> 98 </div>
99 {% if entry.previewPicture is not null %}
100 <div><img class="preview" src="{{ entry.previewPicture }}" alt="{{ entry.title|e|raw }}" /></div>
101 {% endif %}
102 <article> 99 <article>
103 {{ entry.content | raw }} 100 {{ entry.content | raw }}
104 </article> 101 </article>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_list.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_list.html.twig
index 7c83c3bb..1c00f2fa 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_list.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_list.html.twig
@@ -1,10 +1,9 @@
1<div class="card-stacked"> 1<div class="card-stacked">
2 <div class="preview"> 2 <div class="card-preview">
3 {% if entry.previewPicture is not null %} 3 <a href="{{ path('view', { 'id': entry.id }) }}">
4 <a href="{{ path('view', { 'id': entry.id }) }}"> 4 {% set previewClassModifier = entry.previewPicture ? '' : ' preview--default' %}
5 <img src="{{ entry.previewPicture }}" /> 5 <span class="preview{{ previewClassModifier }}" style="background-image: url({{ entry.previewPicture | default(asset('wallassets/themes/_global/img/logo-square.svg')) }})"></span>
6 </a> 6 </a>
7 {% endif %}
8 </div> 7 </div>
9 {% include "@WallabagCore/themes/material/Entry/Card/_content.html.twig" with {'entry': entry, 'withTags': true, 'subClass': 'metadata'} only %} 8 {% include "@WallabagCore/themes/material/Entry/Card/_content.html.twig" with {'entry': entry, 'withTags': true, 'subClass': 'metadata'} only %}
10 <ul class="tools-list hide-on-small-only"> 9 <ul class="tools-list hide-on-small-only">
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig
index a137f3c3..742dd330 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig
@@ -68,8 +68,8 @@
68 {% if craue_setting('export_epub') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'epub', 'tag' : currentTag }) }}">EPUB</a></li>{% endif %} 68 {% if craue_setting('export_epub') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'epub', 'tag' : currentTag }) }}">EPUB</a></li>{% endif %}
69 {% if craue_setting('export_mobi') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'mobi', 'tag' : currentTag }) }}">MOBI</a></li>{% endif %} 69 {% if craue_setting('export_mobi') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'mobi', 'tag' : currentTag }) }}">MOBI</a></li>{% endif %}
70 {% if craue_setting('export_pdf') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'pdf', 'tag' : currentTag }) }}">PDF</a></li>{% endif %} 70 {% if craue_setting('export_pdf') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'pdf', 'tag' : currentTag }) }}">PDF</a></li>{% endif %}
71 {% if craue_setting('export_csv') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'json', 'tag' : currentTag }) }}">JSON</a></li>{% endif %} 71 {% if craue_setting('export_json') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'json', 'tag' : currentTag }) }}">JSON</a></li>{% endif %}
72 {% if craue_setting('export_json') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'csv', 'tag' : currentTag }) }}">CSV</a></li>{% endif %} 72 {% if craue_setting('export_csv') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'csv', 'tag' : currentTag }) }}">CSV</a></li>{% endif %}
73 {% if craue_setting('export_txt') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'txt', 'tag' : currentTag }) }}">TXT</a></li>{% endif %} 73 {% if craue_setting('export_txt') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'txt', 'tag' : currentTag }) }}">TXT</a></li>{% endif %}
74 {% if craue_setting('export_xml') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'xml', 'tag' : currentTag }) }}">XML</a></li>{% endif %} 74 {% if craue_setting('export_xml') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'xml', 'tag' : currentTag }) }}">XML</a></li>{% endif %}
75 </ul> 75 </ul>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig
index 9ae6e73e..c6c19de6 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig
@@ -275,10 +275,6 @@
275 {{ render(controller( "WallabagCoreBundle:Tag:addTagForm", { 'id': entry.id } )) }} 275 {{ render(controller( "WallabagCoreBundle:Tag:addTagForm", { 'id': entry.id } )) }}
276 </div> 276 </div>
277 277
278 {% if entry.previewPicture is not null %}
279 <div><img class="preview" src="{{ entry.previewPicture }}" alt="{{ entry.title|striptags|default('entry.default_title'|trans)|raw }}" /></div>
280 {% endif %}
281
282 </aside> 278 </aside>
283 <article> 279 <article>
284 {{ entry.content | raw }} 280 {{ entry.content | raw }}
diff --git a/src/Wallabag/CoreBundle/Tools/Utils.php b/src/Wallabag/CoreBundle/Tools/Utils.php
index 46bb1dc5..e56e251e 100644
--- a/src/Wallabag/CoreBundle/Tools/Utils.php
+++ b/src/Wallabag/CoreBundle/Tools/Utils.php
@@ -20,15 +20,14 @@ class Utils
20 } 20 }
21 21
22 /** 22 /**
23 * For a given text, we calculate reading time for an article 23 * For a given text, we calculate reading time for an article based on 200 words per minute.
24 * based on 200 words per minute.
25 * 24 *
26 * @param $text 25 * @param string $text
27 * 26 *
28 * @return float 27 * @return float
29 */ 28 */
30 public static function getReadingTime($text) 29 public static function getReadingTime($text)
31 { 30 {
32 return floor(\count(preg_split('~[^\p{L}\p{N}\']+~u', strip_tags($text))) / 200); 31 return floor(\count(preg_split('~([^\p{L}\p{N}\']+|(\p{Han}|\p{Hiragana}|\p{Katakana}|\p{Hangul}){1,2})~u', strip_tags($text))) / 200);
33 } 32 }
34} 33}
diff --git a/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php b/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php
index b035f5cc..e4bfbdf0 100644
--- a/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php
+++ b/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php
@@ -52,6 +52,13 @@ abstract class AbstractConsumer
52 52
53 $this->import->setUser($user); 53 $this->import->setUser($user);
54 54
55 if (false === $this->import->validateEntry($storedEntry)) {
56 $this->logger->warning('Entry is invalid', ['entry' => $storedEntry]);
57
58 // return true to skip message
59 return true;
60 }
61
55 $entry = $this->import->parseEntry($storedEntry); 62 $entry = $this->import->parseEntry($storedEntry);
56 63
57 if (null === $entry) { 64 if (null === $entry) {
diff --git a/src/Wallabag/ImportBundle/Import/AbstractImport.php b/src/Wallabag/ImportBundle/Import/AbstractImport.php
index 58a234f4..d39d71b6 100644
--- a/src/Wallabag/ImportBundle/Import/AbstractImport.php
+++ b/src/Wallabag/ImportBundle/Import/AbstractImport.php
@@ -119,6 +119,15 @@ abstract class AbstractImport implements ImportInterface
119 abstract public function parseEntry(array $importedEntry); 119 abstract public function parseEntry(array $importedEntry);
120 120
121 /** 121 /**
122 * Validate that an entry is valid (like has some required keys, etc.).
123 *
124 * @param array $importedEntry
125 *
126 * @return bool
127 */
128 abstract public function validateEntry(array $importedEntry);
129
130 /**
122 * Fetch content from the ContentProxy (using graby). 131 * Fetch content from the ContentProxy (using graby).
123 * If it fails return the given entry to be saved in all case (to avoid user to loose the content). 132 * If it fails return the given entry to be saved in all case (to avoid user to loose the content).
124 * 133 *
@@ -141,9 +150,9 @@ abstract class AbstractImport implements ImportInterface
141 /** 150 /**
142 * Parse and insert all given entries. 151 * Parse and insert all given entries.
143 * 152 *
144 * @param $entries 153 * @param array $entries
145 */ 154 */
146 protected function parseEntries($entries) 155 protected function parseEntries(array $entries)
147 { 156 {
148 $i = 1; 157 $i = 1;
149 $entryToBeFlushed = []; 158 $entryToBeFlushed = [];
@@ -153,6 +162,10 @@ abstract class AbstractImport implements ImportInterface
153 $importedEntry = $this->setEntryAsRead($importedEntry); 162 $importedEntry = $this->setEntryAsRead($importedEntry);
154 } 163 }
155 164
165 if (false === $this->validateEntry($importedEntry)) {
166 continue;
167 }
168
156 $entry = $this->parseEntry($importedEntry); 169 $entry = $this->parseEntry($importedEntry);
157 170
158 if (null === $entry) { 171 if (null === $entry) {
diff --git a/src/Wallabag/ImportBundle/Import/BrowserImport.php b/src/Wallabag/ImportBundle/Import/BrowserImport.php
index 614386cb..804bc6cd 100644
--- a/src/Wallabag/ImportBundle/Import/BrowserImport.php
+++ b/src/Wallabag/ImportBundle/Import/BrowserImport.php
@@ -149,9 +149,9 @@ abstract class BrowserImport extends AbstractImport
149 /** 149 /**
150 * Parse and insert all given entries. 150 * Parse and insert all given entries.
151 * 151 *
152 * @param $entries 152 * @param array $entries
153 */ 153 */
154 protected function parseEntries($entries) 154 protected function parseEntries(array $entries)
155 { 155 {
156 $i = 1; 156 $i = 1;
157 $entryToBeFlushed = []; 157 $entryToBeFlushed = [];
diff --git a/src/Wallabag/ImportBundle/Import/ChromeImport.php b/src/Wallabag/ImportBundle/Import/ChromeImport.php
index 09183abe..eccee698 100644
--- a/src/Wallabag/ImportBundle/Import/ChromeImport.php
+++ b/src/Wallabag/ImportBundle/Import/ChromeImport.php
@@ -33,6 +33,18 @@ class ChromeImport extends BrowserImport
33 /** 33 /**
34 * {@inheritdoc} 34 * {@inheritdoc}
35 */ 35 */
36 public function validateEntry(array $importedEntry)
37 {
38 if (empty($importedEntry['url'])) {
39 return false;
40 }
41
42 return true;
43 }
44
45 /**
46 * {@inheritdoc}
47 */
36 protected function prepareEntry(array $entry = []) 48 protected function prepareEntry(array $entry = [])
37 { 49 {
38 $data = [ 50 $data = [
diff --git a/src/Wallabag/ImportBundle/Import/FirefoxImport.php b/src/Wallabag/ImportBundle/Import/FirefoxImport.php
index 73269fe1..8999e3f3 100644
--- a/src/Wallabag/ImportBundle/Import/FirefoxImport.php
+++ b/src/Wallabag/ImportBundle/Import/FirefoxImport.php
@@ -33,6 +33,18 @@ class FirefoxImport extends BrowserImport
33 /** 33 /**
34 * {@inheritdoc} 34 * {@inheritdoc}
35 */ 35 */
36 public function validateEntry(array $importedEntry)
37 {
38 if (empty($importedEntry['uri'])) {
39 return false;
40 }
41
42 return true;
43 }
44
45 /**
46 * {@inheritdoc}
47 */
36 protected function prepareEntry(array $entry = []) 48 protected function prepareEntry(array $entry = [])
37 { 49 {
38 $data = [ 50 $data = [
diff --git a/src/Wallabag/ImportBundle/Import/InstapaperImport.php b/src/Wallabag/ImportBundle/Import/InstapaperImport.php
index a12af758..44c034f8 100644
--- a/src/Wallabag/ImportBundle/Import/InstapaperImport.php
+++ b/src/Wallabag/ImportBundle/Import/InstapaperImport.php
@@ -108,6 +108,18 @@ class InstapaperImport extends AbstractImport
108 /** 108 /**
109 * {@inheritdoc} 109 * {@inheritdoc}
110 */ 110 */
111 public function validateEntry(array $importedEntry)
112 {
113 if (empty($importedEntry['url'])) {
114 return false;
115 }
116
117 return true;
118 }
119
120 /**
121 * {@inheritdoc}
122 */
111 public function parseEntry(array $importedEntry) 123 public function parseEntry(array $importedEntry)
112 { 124 {
113 $existingEntry = $this->em 125 $existingEntry = $this->em
diff --git a/src/Wallabag/ImportBundle/Import/PinboardImport.php b/src/Wallabag/ImportBundle/Import/PinboardImport.php
index 9a5e8cb6..202eb1b3 100644
--- a/src/Wallabag/ImportBundle/Import/PinboardImport.php
+++ b/src/Wallabag/ImportBundle/Import/PinboardImport.php
@@ -83,6 +83,18 @@ class PinboardImport extends AbstractImport
83 /** 83 /**
84 * {@inheritdoc} 84 * {@inheritdoc}
85 */ 85 */
86 public function validateEntry(array $importedEntry)
87 {
88 if (empty($importedEntry['href'])) {
89 return false;
90 }
91
92 return true;
93 }
94
95 /**
96 * {@inheritdoc}
97 */
86 public function parseEntry(array $importedEntry) 98 public function parseEntry(array $importedEntry)
87 { 99 {
88 $existingEntry = $this->em 100 $existingEntry = $this->em
diff --git a/src/Wallabag/ImportBundle/Import/PocketImport.php b/src/Wallabag/ImportBundle/Import/PocketImport.php
index 4b1ad1d7..f2e59183 100644
--- a/src/Wallabag/ImportBundle/Import/PocketImport.php
+++ b/src/Wallabag/ImportBundle/Import/PocketImport.php
@@ -170,6 +170,18 @@ class PocketImport extends AbstractImport
170 170
171 /** 171 /**
172 * {@inheritdoc} 172 * {@inheritdoc}
173 */
174 public function validateEntry(array $importedEntry)
175 {
176 if (empty($importedEntry['resolved_url']) && empty($importedEntry['given_url'])) {
177 return false;
178 }
179
180 return true;
181 }
182
183 /**
184 * {@inheritdoc}
173 * 185 *
174 * @see https://getpocket.com/developer/docs/v3/retrieve 186 * @see https://getpocket.com/developer/docs/v3/retrieve
175 */ 187 */
diff --git a/src/Wallabag/ImportBundle/Import/ReadabilityImport.php b/src/Wallabag/ImportBundle/Import/ReadabilityImport.php
index d6777582..c5abf189 100644
--- a/src/Wallabag/ImportBundle/Import/ReadabilityImport.php
+++ b/src/Wallabag/ImportBundle/Import/ReadabilityImport.php
@@ -83,6 +83,18 @@ class ReadabilityImport extends AbstractImport
83 /** 83 /**
84 * {@inheritdoc} 84 * {@inheritdoc}
85 */ 85 */
86 public function validateEntry(array $importedEntry)
87 {
88 if (empty($importedEntry['article__url'])) {
89 return false;
90 }
91
92 return true;
93 }
94
95 /**
96 * {@inheritdoc}
97 */
86 public function parseEntry(array $importedEntry) 98 public function parseEntry(array $importedEntry)
87 { 99 {
88 $existingEntry = $this->em 100 $existingEntry = $this->em
diff --git a/src/Wallabag/ImportBundle/Import/WallabagImport.php b/src/Wallabag/ImportBundle/Import/WallabagImport.php
index 916137f1..c3a142b9 100644
--- a/src/Wallabag/ImportBundle/Import/WallabagImport.php
+++ b/src/Wallabag/ImportBundle/Import/WallabagImport.php
@@ -89,6 +89,18 @@ abstract class WallabagImport extends AbstractImport
89 /** 89 /**
90 * {@inheritdoc} 90 * {@inheritdoc}
91 */ 91 */
92 public function validateEntry(array $importedEntry)
93 {
94 if (empty($importedEntry['url'])) {
95 return false;
96 }
97
98 return true;
99 }
100
101 /**
102 * {@inheritdoc}
103 */
92 public function parseEntry(array $importedEntry) 104 public function parseEntry(array $importedEntry)
93 { 105 {
94 $existingEntry = $this->em 106 $existingEntry = $this->em
diff --git a/src/Wallabag/UserBundle/Resources/views/layout.html.twig b/src/Wallabag/UserBundle/Resources/views/layout.html.twig
index b53f8746..a47b31d0 100644
--- a/src/Wallabag/UserBundle/Resources/views/layout.html.twig
+++ b/src/Wallabag/UserBundle/Resources/views/layout.html.twig
@@ -11,7 +11,7 @@
11<main class="valign-wrapper"> 11<main class="valign-wrapper">
12 <div class="valign row"> 12 <div class="valign row">
13 <div class="card sw"> 13 <div class="card sw">
14 <div class="center"><img src="{{ asset('wallassets/themes/_global/img/logo-wallabag.svg') }}" alt="wallabag logo" class="typo-logo" /></div> 14 <div class="center"><img src="{{ asset('wallassets/themes/_global/img/logo-wallabag.svg') }}" class="typo-logo" alt="wallabag logo" /></div>
15 {% block fos_user_content %} 15 {% block fos_user_content %}
16 {% endblock fos_user_content %} 16 {% endblock fos_user_content %}
17 </div> 17 </div>