aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/Wallabag
diff options
context:
space:
mode:
Diffstat (limited to 'src/Wallabag')
-rw-r--r--src/Wallabag/ApiBundle/Controller/EntryRestController.php92
-rw-r--r--src/Wallabag/ApiBundle/Controller/UserRestController.php2
-rw-r--r--src/Wallabag/ApiBundle/Controller/WallabagRestController.php4
-rw-r--r--src/Wallabag/ApiBundle/Entity/AccessToken.php1
-rw-r--r--src/Wallabag/ApiBundle/Entity/AuthCode.php1
-rw-r--r--src/Wallabag/ApiBundle/Entity/RefreshToken.php1
-rw-r--r--src/Wallabag/CoreBundle/Command/GenerateUrlHashesCommand.php5
-rw-r--r--src/Wallabag/CoreBundle/Controller/ConfigController.php28
-rw-r--r--src/Wallabag/CoreBundle/DataFixtures/SiteCredentialFixtures.php27
-rw-r--r--src/Wallabag/CoreBundle/Entity/Entry.php73
-rw-r--r--src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php19
-rw-r--r--src/Wallabag/CoreBundle/Helper/ContentProxy.php52
-rw-r--r--src/Wallabag/CoreBundle/Helper/DownloadImages.php75
-rw-r--r--src/Wallabag/CoreBundle/Helper/HttpClientFactory.php51
-rw-r--r--src/Wallabag/CoreBundle/Helper/UrlHasher.php23
-rw-r--r--src/Wallabag/CoreBundle/Repository/EntryRepository.php49
-rw-r--r--src/Wallabag/CoreBundle/Repository/SiteCredentialRepository.php8
-rw-r--r--src/Wallabag/CoreBundle/Resources/config/services.yml11
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.da.yml5
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.de.yml5
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.en.yml5
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.es.yml5
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml5
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml5
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.it.yml5
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml5
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml5
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml5
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml5
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.ru.yml4
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.th.yml5
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml2
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/base.html.twig1
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig16
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig1
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/share.html.twig3
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig9
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/Card/_content.html.twig5
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/_card_list.html.twig2
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig3
-rw-r--r--src/Wallabag/ImportBundle/Import/InstapaperImport.php4
-rw-r--r--src/Wallabag/ImportBundle/Import/PocketImport.php91
-rw-r--r--src/Wallabag/ImportBundle/Import/WallabagV2Import.php4
-rw-r--r--src/Wallabag/ImportBundle/Resources/config/services.yml8
44 files changed, 501 insertions, 234 deletions
diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
index 06520af9..9f933adb 100644
--- a/src/Wallabag/ApiBundle/Controller/EntryRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
@@ -14,6 +14,7 @@ use Wallabag\CoreBundle\Entity\Entry;
14use Wallabag\CoreBundle\Entity\Tag; 14use Wallabag\CoreBundle\Entity\Tag;
15use Wallabag\CoreBundle\Event\EntryDeletedEvent; 15use Wallabag\CoreBundle\Event\EntryDeletedEvent;
16use Wallabag\CoreBundle\Event\EntrySavedEvent; 16use Wallabag\CoreBundle\Event\EntrySavedEvent;
17use Wallabag\CoreBundle\Helper\UrlHasher;
17 18
18class EntryRestController extends WallabagRestController 19class EntryRestController extends WallabagRestController
19{ 20{
@@ -43,50 +44,45 @@ class EntryRestController extends WallabagRestController
43 44
44 $returnId = (null === $request->query->get('return_id')) ? false : (bool) $request->query->get('return_id'); 45 $returnId = (null === $request->query->get('return_id')) ? false : (bool) $request->query->get('return_id');
45 46
46 $urls = $request->query->get('urls', []);
47 $hashedUrls = $request->query->get('hashed_urls', []); 47 $hashedUrls = $request->query->get('hashed_urls', []);
48 $hashedUrl = $request->query->get('hashed_url', '');
49 if (!empty($hashedUrl)) {
50 $hashedUrls[] = $hashedUrl;
51 }
48 52
49 // handle multiple urls first 53 $urls = $request->query->get('urls', []);
50 if (!empty($hashedUrls)) { 54 $url = $request->query->get('url', '');
51 $results = []; 55 if (!empty($url)) {
52 foreach ($hashedUrls as $hashedUrl) { 56 $urls[] = $url;
53 $res = $repo->findByHashedUrlAndUserId($hashedUrl, $this->getUser()->getId()); 57 }
54
55 $results[$hashedUrl] = $this->returnExistInformation($res, $returnId);
56 }
57 58
58 return $this->sendResponse($results); 59 $urlHashMap = [];
60 foreach ($urls as $urlToHash) {
61 $urlHash = UrlHasher::hashUrl($urlToHash);
62 $hashedUrls[] = $urlHash;
63 $urlHashMap[$urlHash] = $urlToHash;
59 } 64 }
60 65
61 // @deprecated, to be remove in 3.0 66 if (empty($hashedUrls)) {
62 if (!empty($urls)) { 67 throw $this->createAccessDeniedException('URL is empty?, logged user id: ' . $this->getUser()->getId());
63 $results = []; 68 }
64 foreach ($urls as $url) {
65 $res = $repo->findByUrlAndUserId($url, $this->getUser()->getId());
66 69
67 $results[$url] = $this->returnExistInformation($res, $returnId); 70 $results = [];
68 } 71 foreach ($hashedUrls as $hashedUrlToSearch) {
72 $res = $repo->findByHashedUrlAndUserId($hashedUrlToSearch, $this->getUser()->getId());
69 73
70 return $this->sendResponse($results); 74 $results[$hashedUrlToSearch] = $this->returnExistInformation($res, $returnId);
71 } 75 }
72 76
73 // let's see if it is a simple url? 77 $results = $this->replaceUrlHashes($results, $urlHashMap);
74 $url = $request->query->get('url', '');
75 $hashedUrl = $request->query->get('hashed_url', '');
76 78
77 if (empty($url) && empty($hashedUrl)) { 79 if (!empty($url) || !empty($hashedUrl)) {
78 throw $this->createAccessDeniedException('URL is empty?, logged user id: ' . $this->getUser()->getId()); 80 $hu = array_keys($results)[0];
79 }
80 81
81 $method = 'findByUrlAndUserId'; 82 return $this->sendResponse(['exists' => $results[$hu]]);
82 if (!empty($hashedUrl)) {
83 $method = 'findByHashedUrlAndUserId';
84 $url = $hashedUrl;
85 } 83 }
86 84
87 $res = $repo->$method($url, $this->getUser()->getId()); 85 return $this->sendResponse($results);
88
89 return $this->sendResponse(['exists' => $this->returnExistInformation($res, $returnId)]);
90 } 86 }
91 87
92 /** 88 /**
@@ -103,6 +99,7 @@ class EntryRestController extends WallabagRestController
103 * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."}, 99 * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."},
104 * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."}, 100 * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."},
105 * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by entries with a public link"}, 101 * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by entries with a public link"},
102 * {"name"="detail", "dataType"="string", "required"=false, "format"="metadata or full, metadata by default", "description"="include content field if 'full'. 'full' by default for backward compatibility."},
106 * } 103 * }
107 * ) 104 * )
108 * 105 *
@@ -121,6 +118,7 @@ class EntryRestController extends WallabagRestController
121 $perPage = (int) $request->query->get('perPage', 30); 118 $perPage = (int) $request->query->get('perPage', 30);
122 $tags = \is_array($request->query->get('tags')) ? '' : (string) $request->query->get('tags', ''); 119 $tags = \is_array($request->query->get('tags')) ? '' : (string) $request->query->get('tags', '');
123 $since = $request->query->get('since', 0); 120 $since = $request->query->get('since', 0);
121 $detail = strtolower($request->query->get('detail', 'full'));
124 122
125 try { 123 try {
126 /** @var \Pagerfanta\Pagerfanta $pager */ 124 /** @var \Pagerfanta\Pagerfanta $pager */
@@ -132,7 +130,8 @@ class EntryRestController extends WallabagRestController
132 $sort, 130 $sort,
133 $order, 131 $order,
134 $since, 132 $since,
135 $tags 133 $tags,
134 $detail
136 ); 135 );
137 } catch (\Exception $e) { 136 } catch (\Exception $e) {
138 throw new BadRequestHttpException($e->getMessage()); 137 throw new BadRequestHttpException($e->getMessage());
@@ -156,6 +155,7 @@ class EntryRestController extends WallabagRestController
156 'perPage' => $perPage, 155 'perPage' => $perPage,
157 'tags' => $tags, 156 'tags' => $tags,
158 'since' => $since, 157 'since' => $since,
158 'detail' => $detail,
159 ], 159 ],
160 true 160 true
161 ) 161 )
@@ -365,9 +365,7 @@ class EntryRestController extends WallabagRestController
365 'language' => !empty($data['language']) ? $data['language'] : $entry->getLanguage(), 365 'language' => !empty($data['language']) ? $data['language'] : $entry->getLanguage(),
366 'date' => !empty($data['publishedAt']) ? $data['publishedAt'] : $entry->getPublishedAt(), 366 'date' => !empty($data['publishedAt']) ? $data['publishedAt'] : $entry->getPublishedAt(),
367 // faking the open graph preview picture 367 // faking the open graph preview picture
368 'open_graph' => [ 368 'image' => !empty($data['picture']) ? $data['picture'] : $entry->getPreviewPicture(),
369 'og_image' => !empty($data['picture']) ? $data['picture'] : $entry->getPreviewPicture(),
370 ],
371 'authors' => \is_string($data['authors']) ? explode(',', $data['authors']) : $entry->getPublishedBy(), 369 'authors' => \is_string($data['authors']) ? explode(',', $data['authors']) : $entry->getPublishedBy(),
372 ] 370 ]
373 ); 371 );
@@ -566,7 +564,7 @@ class EntryRestController extends WallabagRestController
566 } 564 }
567 565
568 // if refreshing entry failed, don't save it 566 // if refreshing entry failed, don't save it
569 if ($this->getParameter('wallabag_core.fetching_error_message') === $entry->getContent()) { 567 if ($this->container->getParameter('wallabag_core.fetching_error_message') === $entry->getContent()) {
570 return new JsonResponse([], 304); 568 return new JsonResponse([], 304);
571 } 569 }
572 570
@@ -803,6 +801,24 @@ class EntryRestController extends WallabagRestController
803 } 801 }
804 802
805 /** 803 /**
804 * Replace the hashedUrl keys in $results with the unhashed URL from the
805 * request, as recorded in $urlHashMap.
806 */
807 private function replaceUrlHashes(array $results, array $urlHashMap)
808 {
809 $newResults = [];
810 foreach ($results as $hash => $res) {
811 if (isset($urlHashMap[$hash])) {
812 $newResults[$urlHashMap[$hash]] = $res;
813 } else {
814 $newResults[$hash] = $res;
815 }
816 }
817
818 return $newResults;
819 }
820
821 /**
806 * Retrieve value from the request. 822 * Retrieve value from the request.
807 * Used for POST & PATCH on a an entry. 823 * Used for POST & PATCH on a an entry.
808 * 824 *
@@ -830,8 +846,8 @@ class EntryRestController extends WallabagRestController
830 /** 846 /**
831 * Return information about the entry if it exist and depending on the id or not. 847 * Return information about the entry if it exist and depending on the id or not.
832 * 848 *
833 * @param Entry|null $entry 849 * @param Entry|bool|null $entry
834 * @param bool $returnId 850 * @param bool $returnId
835 * 851 *
836 * @return bool|int 852 * @return bool|int
837 */ 853 */
diff --git a/src/Wallabag/ApiBundle/Controller/UserRestController.php b/src/Wallabag/ApiBundle/Controller/UserRestController.php
index 3a4dafcd..1b10a076 100644
--- a/src/Wallabag/ApiBundle/Controller/UserRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/UserRestController.php
@@ -45,7 +45,7 @@ class UserRestController extends WallabagRestController
45 */ 45 */
46 public function putUserAction(Request $request) 46 public function putUserAction(Request $request)
47 { 47 {
48 if (!$this->getParameter('fosuser_registration') || !$this->get('craue_config')->get('api_user_registration')) { 48 if (!$this->container->getParameter('fosuser_registration') || !$this->get('craue_config')->get('api_user_registration')) {
49 $json = $this->get('jms_serializer')->serialize(['error' => "Server doesn't allow registrations"], 'json'); 49 $json = $this->get('jms_serializer')->serialize(['error' => "Server doesn't allow registrations"], 'json');
50 50
51 return (new JsonResponse()) 51 return (new JsonResponse())
diff --git a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
index f18b0910..44fd9683 100644
--- a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
@@ -2,13 +2,13 @@
2 2
3namespace Wallabag\ApiBundle\Controller; 3namespace Wallabag\ApiBundle\Controller;
4 4
5use FOS\RestBundle\Controller\FOSRestController; 5use FOS\RestBundle\Controller\AbstractFOSRestController;
6use JMS\Serializer\SerializationContext; 6use JMS\Serializer\SerializationContext;
7use Nelmio\ApiDocBundle\Annotation\ApiDoc; 7use Nelmio\ApiDocBundle\Annotation\ApiDoc;
8use Symfony\Component\HttpFoundation\JsonResponse; 8use Symfony\Component\HttpFoundation\JsonResponse;
9use Symfony\Component\Security\Core\Exception\AccessDeniedException; 9use Symfony\Component\Security\Core\Exception\AccessDeniedException;
10 10
11class WallabagRestController extends FOSRestController 11class WallabagRestController extends AbstractFOSRestController
12{ 12{
13 /** 13 /**
14 * Retrieve version number. 14 * Retrieve version number.
diff --git a/src/Wallabag/ApiBundle/Entity/AccessToken.php b/src/Wallabag/ApiBundle/Entity/AccessToken.php
index 5e4099dd..98e0af3e 100644
--- a/src/Wallabag/ApiBundle/Entity/AccessToken.php
+++ b/src/Wallabag/ApiBundle/Entity/AccessToken.php
@@ -42,6 +42,7 @@ class AccessToken extends BaseAccessToken
42 42
43 /** 43 /**
44 * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User") 44 * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User")
45 * @ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")
45 */ 46 */
46 protected $user; 47 protected $user;
47} 48}
diff --git a/src/Wallabag/ApiBundle/Entity/AuthCode.php b/src/Wallabag/ApiBundle/Entity/AuthCode.php
index 5fa205ac..7c9c8539 100644
--- a/src/Wallabag/ApiBundle/Entity/AuthCode.php
+++ b/src/Wallabag/ApiBundle/Entity/AuthCode.php
@@ -42,6 +42,7 @@ class AuthCode extends BaseAuthCode
42 42
43 /** 43 /**
44 * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User") 44 * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User")
45 * @ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")
45 */ 46 */
46 protected $user; 47 protected $user;
47} 48}
diff --git a/src/Wallabag/ApiBundle/Entity/RefreshToken.php b/src/Wallabag/ApiBundle/Entity/RefreshToken.php
index dd8e9c63..55a507e1 100644
--- a/src/Wallabag/ApiBundle/Entity/RefreshToken.php
+++ b/src/Wallabag/ApiBundle/Entity/RefreshToken.php
@@ -42,6 +42,7 @@ class RefreshToken extends BaseRefreshToken
42 42
43 /** 43 /**
44 * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User") 44 * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User")
45 * @ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")
45 */ 46 */
46 protected $user; 47 protected $user;
47} 48}
diff --git a/src/Wallabag/CoreBundle/Command/GenerateUrlHashesCommand.php b/src/Wallabag/CoreBundle/Command/GenerateUrlHashesCommand.php
index 45bd8c5f..8f2bff11 100644
--- a/src/Wallabag/CoreBundle/Command/GenerateUrlHashesCommand.php
+++ b/src/Wallabag/CoreBundle/Command/GenerateUrlHashesCommand.php
@@ -7,6 +7,7 @@ use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
7use Symfony\Component\Console\Input\InputArgument; 7use Symfony\Component\Console\Input\InputArgument;
8use Symfony\Component\Console\Input\InputInterface; 8use Symfony\Component\Console\Input\InputInterface;
9use Symfony\Component\Console\Output\OutputInterface; 9use Symfony\Component\Console\Output\OutputInterface;
10use Wallabag\CoreBundle\Helper\UrlHasher;
10use Wallabag\UserBundle\Entity\User; 11use Wallabag\UserBundle\Entity\User;
11 12
12class GenerateUrlHashesCommand extends ContainerAwareCommand 13class GenerateUrlHashesCommand extends ContainerAwareCommand
@@ -65,7 +66,7 @@ class GenerateUrlHashesCommand extends ContainerAwareCommand
65 66
66 $i = 1; 67 $i = 1;
67 foreach ($entries as $entry) { 68 foreach ($entries as $entry) {
68 $entry->setHashedUrl(hash('sha1', $entry->getUrl())); 69 $entry->setHashedUrl(UrlHasher::hashUrl($entry->getUrl()));
69 $em->persist($entry); 70 $em->persist($entry);
70 71
71 if (0 === ($i % 20)) { 72 if (0 === ($i % 20)) {
@@ -84,7 +85,7 @@ class GenerateUrlHashesCommand extends ContainerAwareCommand
84 * 85 *
85 * @param string $username 86 * @param string $username
86 * 87 *
87 * @return \Wallabag\UserBundle\Entity\User 88 * @return User
88 */ 89 */
89 private function getUser($username) 90 private function getUser($username)
90 { 91 {
diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php
index 3b281d48..cea41303 100644
--- a/src/Wallabag/CoreBundle/Controller/ConfigController.php
+++ b/src/Wallabag/CoreBundle/Controller/ConfigController.php
@@ -300,6 +300,34 @@ class ConfigController extends Controller
300 } 300 }
301 301
302 /** 302 /**
303 * @param Request $request
304 *
305 * @Route("/revoke-token", name="revoke_token")
306 *
307 * @return RedirectResponse|JsonResponse
308 */
309 public function revokeTokenAction(Request $request)
310 {
311 $config = $this->getConfig();
312 $config->setFeedToken(null);
313
314 $em = $this->getDoctrine()->getManager();
315 $em->persist($config);
316 $em->flush();
317
318 if ($request->isXmlHttpRequest()) {
319 return new JsonResponse();
320 }
321
322 $this->addFlash(
323 'notice',
324 'flashes.config.notice.feed_token_revoked'
325 );
326
327 return $this->redirect($this->generateUrl('config') . '#set2');
328 }
329
330 /**
303 * Deletes a tagging rule and redirect to the config homepage. 331 * Deletes a tagging rule and redirect to the config homepage.
304 * 332 *
305 * @param TaggingRule $rule 333 * @param TaggingRule $rule
diff --git a/src/Wallabag/CoreBundle/DataFixtures/SiteCredentialFixtures.php b/src/Wallabag/CoreBundle/DataFixtures/SiteCredentialFixtures.php
index c73173e8..9a7d116f 100644
--- a/src/Wallabag/CoreBundle/DataFixtures/SiteCredentialFixtures.php
+++ b/src/Wallabag/CoreBundle/DataFixtures/SiteCredentialFixtures.php
@@ -5,20 +5,39 @@ namespace Wallabag\CoreBundle\DataFixtures;
5use Doctrine\Bundle\FixturesBundle\Fixture; 5use Doctrine\Bundle\FixturesBundle\Fixture;
6use Doctrine\Common\DataFixtures\DependentFixtureInterface; 6use Doctrine\Common\DataFixtures\DependentFixtureInterface;
7use Doctrine\Common\Persistence\ObjectManager; 7use Doctrine\Common\Persistence\ObjectManager;
8use Symfony\Component\DependencyInjection\ContainerAwareInterface;
9use Symfony\Component\DependencyInjection\ContainerInterface;
8use Wallabag\CoreBundle\Entity\SiteCredential; 10use Wallabag\CoreBundle\Entity\SiteCredential;
9use Wallabag\UserBundle\DataFixtures\UserFixtures; 11use Wallabag\UserBundle\DataFixtures\UserFixtures;
10 12
11class SiteCredentialFixtures extends Fixture implements DependentFixtureInterface 13class SiteCredentialFixtures extends Fixture implements DependentFixtureInterface, ContainerAwareInterface
12{ 14{
13 /** 15 /**
16 * @var ContainerInterface
17 */
18 private $container;
19
20 public function setContainer(ContainerInterface $container = null)
21 {
22 $this->container = $container;
23 }
24
25 /**
14 * {@inheritdoc} 26 * {@inheritdoc}
15 */ 27 */
16 public function load(ObjectManager $manager) 28 public function load(ObjectManager $manager)
17 { 29 {
18 $credential = new SiteCredential($this->getReference('admin-user')); 30 $credential = new SiteCredential($this->getReference('admin-user'));
19 $credential->setHost('example.com'); 31 $credential->setHost('.super.com');
20 $credential->setUsername('foo'); 32 $credential->setUsername($this->container->get('wallabag_core.helper.crypto_proxy')->crypt('.super'));
21 $credential->setPassword('bar'); 33 $credential->setPassword($this->container->get('wallabag_core.helper.crypto_proxy')->crypt('bar'));
34
35 $manager->persist($credential);
36
37 $credential = new SiteCredential($this->getReference('admin-user'));
38 $credential->setHost('paywall.example.com');
39 $credential->setUsername($this->container->get('wallabag_core.helper.crypto_proxy')->crypt('paywall.example'));
40 $credential->setPassword($this->container->get('wallabag_core.helper.crypto_proxy')->crypt('bar'));
22 41
23 $manager->persist($credential); 42 $manager->persist($credential);
24 43
diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php
index c3fb87d2..4d5e6fc9 100644
--- a/src/Wallabag/CoreBundle/Entity/Entry.php
+++ b/src/Wallabag/CoreBundle/Entity/Entry.php
@@ -13,6 +13,7 @@ use JMS\Serializer\Annotation\XmlRoot;
13use Symfony\Component\Validator\Constraints as Assert; 13use Symfony\Component\Validator\Constraints as Assert;
14use Wallabag\AnnotationBundle\Entity\Annotation; 14use Wallabag\AnnotationBundle\Entity\Annotation;
15use Wallabag\CoreBundle\Helper\EntityTimestampsTrait; 15use Wallabag\CoreBundle\Helper\EntityTimestampsTrait;
16use Wallabag\CoreBundle\Helper\UrlHasher;
16use Wallabag\UserBundle\Entity\User; 17use Wallabag\UserBundle\Entity\User;
17 18
18/** 19/**
@@ -26,7 +27,8 @@ use Wallabag\UserBundle\Entity\User;
26 * indexes={ 27 * indexes={
27 * @ORM\Index(name="created_at", columns={"created_at"}), 28 * @ORM\Index(name="created_at", columns={"created_at"}),
28 * @ORM\Index(name="uid", columns={"uid"}), 29 * @ORM\Index(name="uid", columns={"uid"}),
29 * @ORM\Index(name="hashed_url_user_id", columns={"user_id", "hashed_url"}, options={"lengths"={null, 40}}) 30 * @ORM\Index(name="hashed_url_user_id", columns={"user_id", "hashed_url"}, options={"lengths"={null, 40}}),
31 * @ORM\Index(name="hashed_given_url_user_id", columns={"user_id", "hashed_given_url"}, options={"lengths"={null, 40}})
30 * } 32 * }
31 * ) 33 * )
32 * @ORM\HasLifecycleCallbacks() 34 * @ORM\HasLifecycleCallbacks()
@@ -67,6 +69,8 @@ class Entry
67 private $title; 69 private $title;
68 70
69 /** 71 /**
72 * Define the url fetched by wallabag (the final url after potential redirections).
73 *
70 * @var string 74 * @var string
71 * 75 *
72 * @Assert\NotBlank() 76 * @Assert\NotBlank()
@@ -84,6 +88,35 @@ class Entry
84 private $hashedUrl; 88 private $hashedUrl;
85 89
86 /** 90 /**
91 * From where user retrieved/found the url (an other article, a twitter, or the given_url if non are provided).
92 *
93 * @var string
94 *
95 * @ORM\Column(name="origin_url", type="text", nullable=true)
96 *
97 * @Groups({"entries_for_user", "export_all"})
98 */
99 private $originUrl;
100
101 /**
102 * Define the url entered by the user (without redirections).
103 *
104 * @var string
105 *
106 * @ORM\Column(name="given_url", type="text", nullable=true)
107 *
108 * @Groups({"entries_for_user", "export_all"})
109 */
110 private $givenUrl;
111
112 /**
113 * @var string
114 *
115 * @ORM\Column(name="hashed_given_url", type="string", length=40, nullable=true)
116 */
117 private $hashedGivenUrl;
118
119 /**
87 * @var bool 120 * @var bool
88 * 121 *
89 * @Exclude 122 * @Exclude
@@ -262,15 +295,6 @@ class Entry
262 */ 295 */
263 private $tags; 296 private $tags;
264 297
265 /**
266 * @var string
267 *
268 * @ORM\Column(name="origin_url", type="text", nullable=true)
269 *
270 * @Groups({"entries_for_user", "export_all"})
271 */
272 private $originUrl;
273
274 /* 298 /*
275 * @param User $user 299 * @param User $user
276 */ 300 */
@@ -324,7 +348,7 @@ class Entry
324 public function setUrl($url) 348 public function setUrl($url)
325 { 349 {
326 $this->url = $url; 350 $this->url = $url;
327 $this->hashedUrl = hash('sha1', $url); 351 $this->hashedUrl = UrlHasher::hashUrl($url);
328 352
329 return $this; 353 return $this;
330 } 354 }
@@ -770,7 +794,7 @@ class Entry
770 } 794 }
771 795
772 /** 796 /**
773 * @return string 797 * @return string|null
774 */ 798 */
775 public function getUid() 799 public function getUid()
776 { 800 {
@@ -922,6 +946,31 @@ class Entry
922 } 946 }
923 947
924 /** 948 /**
949 * Set given url.
950 *
951 * @param string $givenUrl
952 *
953 * @return Entry
954 */
955 public function setGivenUrl($givenUrl)
956 {
957 $this->givenUrl = $givenUrl;
958 $this->hashedGivenUrl = UrlHasher::hashUrl($givenUrl);
959
960 return $this;
961 }
962
963 /**
964 * Get given url.
965 *
966 * @return string
967 */
968 public function getGivenUrl()
969 {
970 return $this->givenUrl;
971 }
972
973 /**
925 * @return string 974 * @return string
926 */ 975 */
927 public function getHashedUrl() 976 public function getHashedUrl()
diff --git a/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php b/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php
index 90e00c62..c7502bac 100644
--- a/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php
+++ b/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php
@@ -62,11 +62,24 @@ class GrabySiteConfigBuilder implements SiteConfigBuilder
62 $host = substr($host, 4); 62 $host = substr($host, 4);
63 } 63 }
64 64
65 $credentials = null; 65 if (!$this->currentUser) {
66 if ($this->currentUser) { 66 $this->logger->debug('Auth: no current user defined.');
67 $credentials = $this->credentialRepository->findOneByHostAndUser($host, $this->currentUser->getId()); 67
68 return false;
69 }
70
71 $hosts = [$host];
72 // will try to see for a host without the first subdomain (fr.example.org & .example.org)
73 $split = explode('.', $host);
74
75 if (\count($split) > 1) {
76 // remove first subdomain
77 array_shift($split);
78 $hosts[] = '.' . implode('.', $split);
68 } 79 }
69 80
81 $credentials = $this->credentialRepository->findOneByHostsAndUser($hosts, $this->currentUser->getId());
82
70 if (null === $credentials) { 83 if (null === $credentials) {
71 $this->logger->debug('Auth: no credentials available for host.', ['host' => $host]); 84 $this->logger->debug('Auth: no credentials available for host.', ['host' => $host]);
72 85
diff --git a/src/Wallabag/CoreBundle/Helper/ContentProxy.php b/src/Wallabag/CoreBundle/Helper/ContentProxy.php
index 31953f12..5901df8b 100644
--- a/src/Wallabag/CoreBundle/Helper/ContentProxy.php
+++ b/src/Wallabag/CoreBundle/Helper/ContentProxy.php
@@ -12,8 +12,8 @@ use Wallabag\CoreBundle\Entity\Entry;
12use Wallabag\CoreBundle\Tools\Utils; 12use Wallabag\CoreBundle\Tools\Utils;
13 13
14/** 14/**
15 * This kind of proxy class take care of getting the content from an url 15 * This kind of proxy class takes care of getting the content from an url
16 * and update the entry with what it found. 16 * and updates the entry with what it found.
17 */ 17 */
18class ContentProxy 18class ContentProxy
19{ 19{
@@ -47,13 +47,18 @@ class ContentProxy
47 */ 47 */
48 public function updateEntry(Entry $entry, $url, array $content = [], $disableContentUpdate = false) 48 public function updateEntry(Entry $entry, $url, array $content = [], $disableContentUpdate = false)
49 { 49 {
50 $this->graby->toggleImgNoReferrer(true);
50 if (!empty($content['html'])) { 51 if (!empty($content['html'])) {
51 $content['html'] = $this->graby->cleanupHtml($content['html'], $url); 52 $content['html'] = $this->graby->cleanupHtml($content['html'], $url);
52 } 53 }
53 54
54 if ((empty($content) || false === $this->validateContent($content)) && false === $disableContentUpdate) { 55 if ((empty($content) || false === $this->validateContent($content)) && false === $disableContentUpdate) {
55 $fetchedContent = $this->graby->fetchContent($url); 56 $fetchedContent = $this->graby->fetchContent($url);
56 $fetchedContent['title'] = $this->sanitizeContentTitle($fetchedContent['title'], $fetchedContent['content_type']); 57
58 $fetchedContent['title'] = $this->sanitizeContentTitle(
59 $fetchedContent['title'],
60 isset($fetchedContent['headers']['content-type']) ? $fetchedContent['headers']['content-type'] : ''
61 );
57 62
58 // when content is imported, we have information in $content 63 // when content is imported, we have information in $content
59 // in case fetching content goes bad, we'll keep the imported information instead of overriding them 64 // in case fetching content goes bad, we'll keep the imported information instead of overriding them
@@ -73,6 +78,8 @@ class ContentProxy
73 $entry->setUrl($url); 78 $entry->setUrl($url);
74 } 79 }
75 80
81 $entry->setGivenUrl($url);
82
76 $this->stockEntry($entry, $content); 83 $this->stockEntry($entry, $content);
77 } 84 }
78 85
@@ -187,8 +194,8 @@ class ContentProxy
187 /** 194 /**
188 * Try to sanitize the title of the fetched content from wrong character encodings and invalid UTF-8 character. 195 * Try to sanitize the title of the fetched content from wrong character encodings and invalid UTF-8 character.
189 * 196 *
190 * @param $title 197 * @param string $title
191 * @param $contentType 198 * @param string $contentType
192 * 199 *
193 * @return string 200 * @return string
194 */ 201 */
@@ -252,16 +259,14 @@ class ContentProxy
252 259
253 if (!empty($content['title'])) { 260 if (!empty($content['title'])) {
254 $entry->setTitle($content['title']); 261 $entry->setTitle($content['title']);
255 } elseif (!empty($content['open_graph']['og_title'])) {
256 $entry->setTitle($content['open_graph']['og_title']);
257 } 262 }
258 263
259 if (empty($content['html'])) { 264 if (empty($content['html'])) {
260 $content['html'] = $this->fetchingErrorMessage; 265 $content['html'] = $this->fetchingErrorMessage;
261 266
262 if (!empty($content['open_graph']['og_description'])) { 267 if (!empty($content['description'])) {
263 $content['html'] .= '<p><i>But we found a short description: </i></p>'; 268 $content['html'] .= '<p><i>But we found a short description: </i></p>';
264 $content['html'] .= $content['open_graph']['og_description']; 269 $content['html'] .= $content['description'];
265 } 270 }
266 } 271 }
267 272
@@ -276,8 +281,8 @@ class ContentProxy
276 $entry->setPublishedBy($content['authors']); 281 $entry->setPublishedBy($content['authors']);
277 } 282 }
278 283
279 if (!empty($content['all_headers']) && $this->storeArticleHeaders) { 284 if (!empty($content['headers'])) {
280 $entry->setHeaders($content['all_headers']); 285 $entry->setHeaders($content['headers']);
281 } 286 }
282 287
283 if (!empty($content['date'])) { 288 if (!empty($content['date'])) {
@@ -288,17 +293,30 @@ class ContentProxy
288 $this->updateLanguage($entry, $content['language']); 293 $this->updateLanguage($entry, $content['language']);
289 } 294 }
290 295
291 if (!empty($content['open_graph']['og_image'])) { 296 $previewPictureUrl = '';
292 $this->updatePreviewPicture($entry, $content['open_graph']['og_image']); 297 if (!empty($content['image'])) {
298 $previewPictureUrl = $content['image'];
293 } 299 }
294 300
295 // if content is an image, define it as a preview too 301 // if content is an image, define it as a preview too
296 if (!empty($content['content_type']) && \in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) { 302 if (!empty($content['headers']['content-type']) && \in_array($this->mimeGuesser->guess($content['headers']['content-type']), ['jpeg', 'jpg', 'gif', 'png'], true)) {
297 $this->updatePreviewPicture($entry, $content['url']); 303 $previewPictureUrl = $content['url'];
304 } elseif (empty($previewPictureUrl)) {
305 $this->logger->debug('Extracting images from content to provide a default preview picture');
306 $imagesUrls = DownloadImages::extractImagesUrlsFromHtml($content['html']);
307 $this->logger->debug(\count($imagesUrls) . ' pictures found');
308
309 if (!empty($imagesUrls)) {
310 $previewPictureUrl = $imagesUrls[0];
311 }
312 }
313
314 if (!empty($content['headers']['content-type'])) {
315 $entry->setMimetype($content['headers']['content-type']);
298 } 316 }
299 317
300 if (!empty($content['content_type'])) { 318 if (!empty($previewPictureUrl)) {
301 $entry->setMimetype($content['content_type']); 319 $this->updatePreviewPicture($entry, $previewPictureUrl);
302 } 320 }
303 321
304 try { 322 try {
diff --git a/src/Wallabag/CoreBundle/Helper/DownloadImages.php b/src/Wallabag/CoreBundle/Helper/DownloadImages.php
index 9a7e9828..1d361d6d 100644
--- a/src/Wallabag/CoreBundle/Helper/DownloadImages.php
+++ b/src/Wallabag/CoreBundle/Helper/DownloadImages.php
@@ -2,8 +2,15 @@
2 2
3namespace Wallabag\CoreBundle\Helper; 3namespace Wallabag\CoreBundle\Helper;
4 4
5use GuzzleHttp\Client; 5use GuzzleHttp\Psr7\Uri;
6use GuzzleHttp\Message\Response; 6use GuzzleHttp\Psr7\UriResolver;
7use Http\Client\Common\HttpMethodsClient;
8use Http\Client\Common\Plugin\ErrorPlugin;
9use Http\Client\Common\PluginClient;
10use Http\Client\HttpClient;
11use Http\Discovery\MessageFactoryDiscovery;
12use Http\Message\MessageFactory;
13use Psr\Http\Message\ResponseInterface;
7use Psr\Log\LoggerInterface; 14use Psr\Log\LoggerInterface;
8use Symfony\Component\DomCrawler\Crawler; 15use Symfony\Component\DomCrawler\Crawler;
9use Symfony\Component\Finder\Finder; 16use Symfony\Component\Finder\Finder;
@@ -19,9 +26,9 @@ class DownloadImages
19 private $mimeGuesser; 26 private $mimeGuesser;
20 private $wallabagUrl; 27 private $wallabagUrl;
21 28
22 public function __construct(Client $client, $baseFolder, $wallabagUrl, LoggerInterface $logger) 29 public function __construct(HttpClient $client, $baseFolder, $wallabagUrl, LoggerInterface $logger, MessageFactory $messageFactory = null)
23 { 30 {
24 $this->client = $client; 31 $this->client = new HttpMethodsClient(new PluginClient($client, [new ErrorPlugin()]), $messageFactory ?: MessageFactoryDiscovery::find());
25 $this->baseFolder = $baseFolder; 32 $this->baseFolder = $baseFolder;
26 $this->wallabagUrl = rtrim($wallabagUrl, '/'); 33 $this->wallabagUrl = rtrim($wallabagUrl, '/');
27 $this->logger = $logger; 34 $this->logger = $logger;
@@ -31,6 +38,23 @@ class DownloadImages
31 } 38 }
32 39
33 /** 40 /**
41 * Process the html and extract images URLs from it.
42 *
43 * @param string $html
44 *
45 * @return string[]
46 */
47 public static function extractImagesUrlsFromHtml($html)
48 {
49 $crawler = new Crawler($html);
50 $imagesCrawler = $crawler->filterXpath('//img');
51 $imagesUrls = $imagesCrawler->extract(['src']);
52 $imagesSrcsetUrls = self::getSrcsetUrls($imagesCrawler);
53
54 return array_unique(array_merge($imagesUrls, $imagesSrcsetUrls));
55 }
56
57 /**
34 * Process the html and extract image from it, save them to local and return the updated html. 58 * Process the html and extract image from it, save them to local and return the updated html.
35 * 59 *
36 * @param int $entryId ID of the entry 60 * @param int $entryId ID of the entry
@@ -41,13 +65,7 @@ class DownloadImages
41 */ 65 */
42 public function processHtml($entryId, $html, $url) 66 public function processHtml($entryId, $html, $url)
43 { 67 {
44 $crawler = new Crawler($html); 68 $imagesUrls = self::extractImagesUrlsFromHtml($html);
45 $imagesCrawler = $crawler
46 ->filterXpath('//img');
47 $imagesUrls = $imagesCrawler
48 ->extract(['src']);
49 $imagesSrcsetUrls = $this->getSrcsetUrls($imagesCrawler);
50 $imagesUrls = array_unique(array_merge($imagesUrls, $imagesSrcsetUrls));
51 69
52 $relativePath = $this->getRelativePath($entryId); 70 $relativePath = $this->getRelativePath($entryId);
53 71
@@ -122,7 +140,7 @@ class DownloadImages
122 $localPath = $folderPath . '/' . $hashImage . '.' . $ext; 140 $localPath = $folderPath . '/' . $hashImage . '.' . $ext;
123 141
124 try { 142 try {
125 $im = imagecreatefromstring($res->getBody()); 143 $im = imagecreatefromstring((string) $res->getBody());
126 } catch (\Exception $e) { 144 } catch (\Exception $e) {
127 $im = false; 145 $im = false;
128 } 146 }
@@ -199,25 +217,28 @@ class DownloadImages
199 * 217 *
200 * @return array An array of urls 218 * @return array An array of urls
201 */ 219 */
202 private function getSrcsetUrls(Crawler $imagesCrawler) 220 private static function getSrcsetUrls(Crawler $imagesCrawler)
203 { 221 {
204 $urls = []; 222 $urls = [];
205 $iterator = $imagesCrawler 223 $iterator = $imagesCrawler->getIterator();
206 ->getIterator(); 224
207 while ($iterator->valid()) { 225 while ($iterator->valid()) {
208 $srcsetAttribute = $iterator->current()->getAttribute('srcset'); 226 $srcsetAttribute = $iterator->current()->getAttribute('srcset');
227
209 if ('' !== $srcsetAttribute) { 228 if ('' !== $srcsetAttribute) {
210 // Couldn't start with " OR ' OR a white space 229 // Couldn't start with " OR ' OR a white space
211 // Could be one or more white space 230 // Could be one or more white space
212 // Must be one or more digits followed by w OR x 231 // Must be one or more digits followed by w OR x
213 $pattern = "/(?:[^\"'\s]+\s*(?:\d+[wx])+)/"; 232 $pattern = "/(?:[^\"'\s]+\s*(?:\d+[wx])+)/";
214 preg_match_all($pattern, $srcsetAttribute, $matches); 233 preg_match_all($pattern, $srcsetAttribute, $matches);
234
215 $srcset = \call_user_func_array('array_merge', $matches); 235 $srcset = \call_user_func_array('array_merge', $matches);
216 $srcsetUrls = array_map(function ($src) { 236 $srcsetUrls = array_map(function ($src) {
217 return trim(explode(' ', $src, 2)[0]); 237 return trim(explode(' ', $src, 2)[0]);
218 }, $srcset); 238 }, $srcset);
219 $urls = array_merge($srcsetUrls, $urls); 239 $urls = array_merge($srcsetUrls, $urls);
220 } 240 }
241
221 $iterator->next(); 242 $iterator->next();
222 } 243 }
223 244
@@ -274,33 +295,29 @@ class DownloadImages
274 return $url; 295 return $url;
275 } 296 }
276 297
277 $base = new \SimplePie_IRI($base); 298 $base = new Uri($base);
278 299
279 // remove '//' in URL path (causes URLs not to resolve properly) 300 // in case the url has no scheme & host
280 if (isset($base->ipath)) { 301 if ('' === $base->getAuthority() || '' === $base->getScheme()) {
281 $base->ipath = preg_replace('!//+!', '/', $base->ipath); 302 $this->logger->error('DownloadImages: Can not make an absolute link', ['base' => $base, 'url' => $url]);
282 }
283 303
284 if ($absolute = \SimplePie_IRI::absolutize($base, $url)) { 304 return false;
285 return $absolute->get_uri();
286 } 305 }
287 306
288 $this->logger->error('DownloadImages: Can not make an absolute link', ['base' => $base, 'url' => $url]); 307 return (string) UriResolver::resolve($base, new Uri($url));
289
290 return false;
291 } 308 }
292 309
293 /** 310 /**
294 * Retrieve and validate the extension from the response of the url of the image. 311 * Retrieve and validate the extension from the response of the url of the image.
295 * 312 *
296 * @param Response $res Guzzle Response 313 * @param ResponseInterface $res Http Response
297 * @param string $imagePath Path from the src image from the content (used for log only) 314 * @param string $imagePath Path from the src image from the content (used for log only)
298 * 315 *
299 * @return string|false Extension name or false if validation failed 316 * @return string|false Extension name or false if validation failed
300 */ 317 */
301 private function getExtensionFromResponse(Response $res, $imagePath) 318 private function getExtensionFromResponse(ResponseInterface $res, $imagePath)
302 { 319 {
303 $ext = $this->mimeGuesser->guess($res->getHeader('content-type')); 320 $ext = $this->mimeGuesser->guess(current($res->getHeader('content-type')));
304 $this->logger->debug('DownloadImages: Checking extension', ['ext' => $ext, 'header' => $res->getHeader('content-type')]); 321 $this->logger->debug('DownloadImages: Checking extension', ['ext' => $ext, 'header' => $res->getHeader('content-type')]);
305 322
306 // ok header doesn't have the extension, try a different way 323 // ok header doesn't have the extension, try a different way
diff --git a/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php b/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php
index 4602a684..b8e95381 100644
--- a/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php
+++ b/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php
@@ -2,16 +2,18 @@
2 2
3namespace Wallabag\CoreBundle\Helper; 3namespace Wallabag\CoreBundle\Helper;
4 4
5use Graby\Ring\Client\SafeCurlHandler; 5use GuzzleHttp\Client as GuzzleClient;
6use GuzzleHttp\Client;
7use GuzzleHttp\Cookie\CookieJar; 6use GuzzleHttp\Cookie\CookieJar;
8use GuzzleHttp\Event\SubscriberInterface; 7use GuzzleHttp\Event\SubscriberInterface;
8use Http\Adapter\Guzzle5\Client as GuzzleAdapter;
9use Http\Client\HttpClient;
10use Http\HttplugBundle\ClientFactory\ClientFactory;
9use Psr\Log\LoggerInterface; 11use Psr\Log\LoggerInterface;
10 12
11/** 13/**
12 * Builds and configures the Guzzle HTTP client. 14 * Builds and configures the HTTP client.
13 */ 15 */
14class HttpClientFactory 16class HttpClientFactory implements ClientFactory
15{ 17{
16 /** @var [\GuzzleHttp\Event\SubscriberInterface] */ 18 /** @var [\GuzzleHttp\Event\SubscriberInterface] */
17 private $subscribers = []; 19 private $subscribers = [];
@@ -37,35 +39,42 @@ class HttpClientFactory
37 } 39 }
38 40
39 /** 41 /**
40 * @return \GuzzleHttp\Client|null 42 * Adds a subscriber to the HTTP client.
43 *
44 * @param SubscriberInterface $subscriber
45 */
46 public function addSubscriber(SubscriberInterface $subscriber)
47 {
48 $this->subscribers[] = $subscriber;
49 }
50
51 /**
52 * Input an array of configuration to be able to create a HttpClient.
53 *
54 * @param array $config
55 *
56 * @return HttpClient
41 */ 57 */
42 public function buildHttpClient() 58 public function createClient(array $config = [])
43 { 59 {
44 $this->logger->log('debug', 'Restricted access config enabled?', ['enabled' => (int) $this->restrictedAccess]); 60 $this->logger->log('debug', 'Restricted access config enabled?', ['enabled' => (int) $this->restrictedAccess]);
45 61
46 if (0 === (int) $this->restrictedAccess) { 62 if (0 === (int) $this->restrictedAccess) {
47 return; 63 return new GuzzleAdapter(new GuzzleClient($config));
48 } 64 }
49 65
50 // we clear the cookie to avoid websites who use cookies for analytics 66 // we clear the cookie to avoid websites who use cookies for analytics
51 $this->cookieJar->clear(); 67 $this->cookieJar->clear();
52 // need to set the (shared) cookie jar 68 if (!isset($config['defaults']['cookies'])) {
53 $client = new Client(['handler' => new SafeCurlHandler(), 'defaults' => ['cookies' => $this->cookieJar]]); 69 // need to set the (shared) cookie jar
70 $config['defaults']['cookies'] = $this->cookieJar;
71 }
54 72
73 $guzzle = new GuzzleClient($config);
55 foreach ($this->subscribers as $subscriber) { 74 foreach ($this->subscribers as $subscriber) {
56 $client->getEmitter()->attach($subscriber); 75 $guzzle->getEmitter()->attach($subscriber);
57 } 76 }
58 77
59 return $client; 78 return new GuzzleAdapter($guzzle);
60 }
61
62 /**
63 * Adds a subscriber to the HTTP client.
64 *
65 * @param SubscriberInterface $subscriber
66 */
67 public function addSubscriber(SubscriberInterface $subscriber)
68 {
69 $this->subscribers[] = $subscriber;
70 } 79 }
71} 80}
diff --git a/src/Wallabag/CoreBundle/Helper/UrlHasher.php b/src/Wallabag/CoreBundle/Helper/UrlHasher.php
new file mode 100644
index 00000000..d123eaba
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Helper/UrlHasher.php
@@ -0,0 +1,23 @@
1<?php
2
3namespace Wallabag\CoreBundle\Helper;
4
5/**
6 * Hash URLs for privacy and performance.
7 */
8class UrlHasher
9{
10 /**
11 * Hash the given url using the given algorithm.
12 * Hashed url are faster to be retrieved in the database than the real url.
13 *
14 * @param string $url
15 * @param string $algorithm
16 *
17 * @return string
18 */
19 public static function hashUrl(string $url, $algorithm = 'sha1')
20 {
21 return hash($algorithm, urldecode($url));
22 }
23}
diff --git a/src/Wallabag/CoreBundle/Repository/EntryRepository.php b/src/Wallabag/CoreBundle/Repository/EntryRepository.php
index f5089729..16c44885 100644
--- a/src/Wallabag/CoreBundle/Repository/EntryRepository.php
+++ b/src/Wallabag/CoreBundle/Repository/EntryRepository.php
@@ -9,6 +9,7 @@ use Pagerfanta\Adapter\DoctrineORMAdapter;
9use Pagerfanta\Pagerfanta; 9use Pagerfanta\Pagerfanta;
10use Wallabag\CoreBundle\Entity\Entry; 10use Wallabag\CoreBundle\Entity\Entry;
11use Wallabag\CoreBundle\Entity\Tag; 11use Wallabag\CoreBundle\Entity\Tag;
12use Wallabag\CoreBundle\Helper\UrlHasher;
12 13
13class EntryRepository extends EntityRepository 14class EntryRepository extends EntityRepository
14{ 15{
@@ -139,15 +140,30 @@ class EntryRepository extends EntityRepository
139 * @param string $order 140 * @param string $order
140 * @param int $since 141 * @param int $since
141 * @param string $tags 142 * @param string $tags
143 * @param string $detail 'metadata' or 'full'. Include content field if 'full'
144 *
145 * @todo Breaking change: replace default detail=full by detail=metadata in a future version
142 * 146 *
143 * @return Pagerfanta 147 * @return Pagerfanta
144 */ 148 */
145 public function findEntries($userId, $isArchived = null, $isStarred = null, $isPublic = null, $sort = 'created', $order = 'asc', $since = 0, $tags = '') 149 public function findEntries($userId, $isArchived = null, $isStarred = null, $isPublic = null, $sort = 'created', $order = 'asc', $since = 0, $tags = '', $detail = 'full')
146 { 150 {
151 if (!\in_array(strtolower($detail), ['full', 'metadata'], true)) {
152 throw new \Exception('Detail "' . $detail . '" parameter is wrong, allowed: full or metadata');
153 }
154
147 $qb = $this->createQueryBuilder('e') 155 $qb = $this->createQueryBuilder('e')
148 ->leftJoin('e.tags', 't') 156 ->leftJoin('e.tags', 't')
149 ->where('e.user = :userId')->setParameter('userId', $userId); 157 ->where('e.user = :userId')->setParameter('userId', $userId);
150 158
159 if ('metadata' === $detail) {
160 $fieldNames = $this->getClassMetadata()->getFieldNames();
161 $fields = array_filter($fieldNames, function ($k) {
162 return 'content' !== $k;
163 });
164 $qb->select(sprintf('partial e.{%s}', implode(',', $fields)));
165 }
166
151 if (null !== $isArchived) { 167 if (null !== $isArchived) {
152 $qb->andWhere('e.isArchived = :isArchived')->setParameter('isArchived', (bool) $isArchived); 168 $qb->andWhere('e.isArchived = :isArchived')->setParameter('isArchived', (bool) $isArchived);
153 } 169 }
@@ -329,21 +345,14 @@ class EntryRepository extends EntityRepository
329 * @param string $url 345 * @param string $url
330 * @param int $userId 346 * @param int $userId
331 * 347 *
332 * @return Entry|bool 348 * @return Entry|false
333 */ 349 */
334 public function findByUrlAndUserId($url, $userId) 350 public function findByUrlAndUserId($url, $userId)
335 { 351 {
336 $res = $this->createQueryBuilder('e') 352 return $this->findByHashedUrlAndUserId(
337 ->where('e.url = :url')->setParameter('url', urldecode($url)) 353 UrlHasher::hashUrl($url),
338 ->andWhere('e.user = :user_id')->setParameter('user_id', $userId) 354 $userId
339 ->getQuery() 355 );
340 ->getResult();
341
342 if (\count($res)) {
343 return current($res);
344 }
345
346 return false;
347 } 356 }
348 357
349 /** 358 /**
@@ -353,10 +362,11 @@ class EntryRepository extends EntityRepository
353 * @param string $hashedUrl Url hashed using sha1 362 * @param string $hashedUrl Url hashed using sha1
354 * @param int $userId 363 * @param int $userId
355 * 364 *
356 * @return Entry|bool 365 * @return Entry|false
357 */ 366 */
358 public function findByHashedUrlAndUserId($hashedUrl, $userId) 367 public function findByHashedUrlAndUserId($hashedUrl, $userId)
359 { 368 {
369 // try first using hashed_url (to use the database index)
360 $res = $this->createQueryBuilder('e') 370 $res = $this->createQueryBuilder('e')
361 ->where('e.hashedUrl = :hashed_url')->setParameter('hashed_url', $hashedUrl) 371 ->where('e.hashedUrl = :hashed_url')->setParameter('hashed_url', $hashedUrl)
362 ->andWhere('e.user = :user_id')->setParameter('user_id', $userId) 372 ->andWhere('e.user = :user_id')->setParameter('user_id', $userId)
@@ -367,6 +377,17 @@ class EntryRepository extends EntityRepository
367 return current($res); 377 return current($res);
368 } 378 }
369 379
380 // then try using hashed_given_url (to use the database index)
381 $res = $this->createQueryBuilder('e')
382 ->where('e.hashedGivenUrl = :hashed_given_url')->setParameter('hashed_given_url', $hashedUrl)
383 ->andWhere('e.user = :user_id')->setParameter('user_id', $userId)
384 ->getQuery()
385 ->getResult();
386
387 if (\count($res)) {
388 return current($res);
389 }
390
370 return false; 391 return false;
371 } 392 }
372 393
diff --git a/src/Wallabag/CoreBundle/Repository/SiteCredentialRepository.php b/src/Wallabag/CoreBundle/Repository/SiteCredentialRepository.php
index b2e212a4..aeadd770 100644
--- a/src/Wallabag/CoreBundle/Repository/SiteCredentialRepository.php
+++ b/src/Wallabag/CoreBundle/Repository/SiteCredentialRepository.php
@@ -19,16 +19,16 @@ class SiteCredentialRepository extends \Doctrine\ORM\EntityRepository
19 /** 19 /**
20 * Retrieve one username/password for the given host and userId. 20 * Retrieve one username/password for the given host and userId.
21 * 21 *
22 * @param string $host 22 * @param array $hosts An array of host to look for
23 * @param int $userId 23 * @param int $userId
24 * 24 *
25 * @return array|null 25 * @return array|null
26 */ 26 */
27 public function findOneByHostAndUser($host, $userId) 27 public function findOneByHostsAndUser($hosts, $userId)
28 { 28 {
29 $res = $this->createQueryBuilder('s') 29 $res = $this->createQueryBuilder('s')
30 ->select('s.username', 's.password') 30 ->select('s.username', 's.password')
31 ->where('s.host = :hostname')->setParameter('hostname', $host) 31 ->where('s.host IN (:hosts)')->setParameter('hosts', $hosts)
32 ->andWhere('s.user = :userId')->setParameter('userId', $userId) 32 ->andWhere('s.user = :userId')->setParameter('userId', $userId)
33 ->setMaxResults(1) 33 ->setMaxResults(1)
34 ->getQuery() 34 ->getQuery()
diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml
index 280d779d..31986951 100644
--- a/src/Wallabag/CoreBundle/Resources/config/services.yml
+++ b/src/Wallabag/CoreBundle/Resources/config/services.yml
@@ -42,7 +42,7 @@ services:
42 - 42 -
43 error_message: '%wallabag_core.fetching_error_message%' 43 error_message: '%wallabag_core.fetching_error_message%'
44 error_message_title: '%wallabag_core.fetching_error_message_title%' 44 error_message_title: '%wallabag_core.fetching_error_message_title%'
45 - "@wallabag_core.guzzle.http_client" 45 - "@wallabag_core.http_client"
46 - "@wallabag_core.graby.config_builder" 46 - "@wallabag_core.graby.config_builder"
47 calls: 47 calls:
48 - [ setLogger, [ "@logger" ] ] 48 - [ setLogger, [ "@logger" ] ]
@@ -55,9 +55,8 @@ services:
55 - {} 55 - {}
56 - "@logger" 56 - "@logger"
57 57
58 wallabag_core.guzzle.http_client: 58 wallabag_core.http_client:
59 class: GuzzleHttp\ClientInterface 59 alias: 'httplug.client.wallabag_core'
60 factory: ["@wallabag_core.guzzle.http_client_factory", buildHttpClient]
61 60
62 wallabag_core.guzzle_authenticator.config_builder: 61 wallabag_core.guzzle_authenticator.config_builder:
63 class: Wallabag\CoreBundle\GuzzleSiteAuthenticator\GrabySiteConfigBuilder 62 class: Wallabag\CoreBundle\GuzzleSiteAuthenticator\GrabySiteConfigBuilder
@@ -73,7 +72,7 @@ services:
73 bd_guzzle_site_authenticator.site_config_builder: 72 bd_guzzle_site_authenticator.site_config_builder:
74 alias: wallabag_core.guzzle_authenticator.config_builder 73 alias: wallabag_core.guzzle_authenticator.config_builder
75 74
76 wallabag_core.guzzle.http_client_factory: 75 wallabag_core.http_client_factory:
77 class: Wallabag\CoreBundle\Helper\HttpClientFactory 76 class: Wallabag\CoreBundle\Helper\HttpClientFactory
78 arguments: 77 arguments:
79 - "@wallabag_core.guzzle.cookie_jar" 78 - "@wallabag_core.guzzle.cookie_jar"
@@ -212,7 +211,7 @@ services:
212 - "@logger" 211 - "@logger"
213 212
214 wallabag_core.entry.download_images.client: 213 wallabag_core.entry.download_images.client:
215 class: GuzzleHttp\Client 214 alias: 'httplug.client.wallabag_core.entry.download_images'
216 215
217 wallabag_core.helper.crypto_proxy: 216 wallabag_core.helper.crypto_proxy:
218 class: Wallabag\CoreBundle\Helper\CryptoProxy 217 class: Wallabag\CoreBundle\Helper\CryptoProxy
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
index 61ef3b8f..c6a91cd1 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
@@ -33,6 +33,7 @@ menu:
33 back_to_unread: 'Tilbage til de ulæste artikler' 33 back_to_unread: 'Tilbage til de ulæste artikler'
34 # users_management: 'Users management' 34 # users_management: 'Users management'
35 # site_credentials: 'Site credentials' 35 # site_credentials: 'Site credentials'
36 # quickstart: "Quickstart"
36 top: 37 top:
37 add_new_entry: 'Tilføj ny artikel' 38 add_new_entry: 'Tilføj ny artikel'
38 search: 'Søg' 39 search: 'Søg'
@@ -91,6 +92,7 @@ config:
91 no_token: 'Intet token' 92 no_token: 'Intet token'
92 token_create: 'Opret token' 93 token_create: 'Opret token'
93 token_reset: 'Nulstil token' 94 token_reset: 'Nulstil token'
95 # token_revoke: 'Revoke the token'
94 feed_links: 'RSS-Links' 96 feed_links: 'RSS-Links'
95 feed_link: 97 feed_link:
96 unread: 'Ulæst' 98 unread: 'Ulæst'
@@ -572,7 +574,7 @@ site_credential:
572 # create_new_one: Create a new credential 574 # create_new_one: Create a new credential
573 # form: 575 # form:
574 # username_label: 'Username' 576 # username_label: 'Username'
575 # host_label: 'Host' 577 # host_label: 'Host (subdomain.example.org, .example.org, etc.)'
576 # password_label: 'Password' 578 # password_label: 'Password'
577 # save: Save 579 # save: Save
578 # delete: Delete 580 # delete: Delete
@@ -593,6 +595,7 @@ flashes:
593 # tagging_rules_updated: 'Tagging rules updated' 595 # tagging_rules_updated: 'Tagging rules updated'
594 # tagging_rules_deleted: 'Tagging rule deleted' 596 # tagging_rules_deleted: 'Tagging rule deleted'
595 # feed_token_updated: 'RSS token updated' 597 # feed_token_updated: 'RSS token updated'
598 # feed_token_revoked: 'RSS token revoked'
596 # annotations_reset: Annotations reset 599 # annotations_reset: Annotations reset
597 # tags_reset: Tags reset 600 # tags_reset: Tags reset
598 # entries_reset: Entries reset 601 # entries_reset: Entries reset
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
index 991e00f1..4b785306 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
@@ -33,6 +33,7 @@ menu:
33 back_to_unread: 'Zurück zu ungelesenen Artikeln' 33 back_to_unread: 'Zurück zu ungelesenen Artikeln'
34 users_management: 'Benutzerverwaltung' 34 users_management: 'Benutzerverwaltung'
35 site_credentials: 'Zugangsdaten' 35 site_credentials: 'Zugangsdaten'
36 quickstart: "Schnelleinstieg"
36 top: 37 top:
37 add_new_entry: 'Neuen Artikel hinzufügen' 38 add_new_entry: 'Neuen Artikel hinzufügen'
38 search: 'Suche' 39 search: 'Suche'
@@ -91,6 +92,7 @@ config:
91 no_token: 'Kein Token' 92 no_token: 'Kein Token'
92 token_create: 'Token erstellen' 93 token_create: 'Token erstellen'
93 token_reset: 'Token zurücksetzen' 94 token_reset: 'Token zurücksetzen'
95 # token_revoke: 'Revoke the token'
94 feed_links: 'RSS-Links' 96 feed_links: 'RSS-Links'
95 feed_link: 97 feed_link:
96 unread: 'Ungelesene' 98 unread: 'Ungelesene'
@@ -563,7 +565,7 @@ site_credential:
563 create_new_one: 'Einen neuen Seitenzugang anlegen' 565 create_new_one: 'Einen neuen Seitenzugang anlegen'
564 form: 566 form:
565 username_label: 'Benutzername' 567 username_label: 'Benutzername'
566 host_label: 'Host' 568 host_label: 'Host (subdomain.example.org, .example.org, etc.)'
567 password_label: 'Passwort' 569 password_label: 'Passwort'
568 save: 'Speichern' 570 save: 'Speichern'
569 delete: 'Löschen' 571 delete: 'Löschen'
@@ -584,6 +586,7 @@ flashes:
584 tagging_rules_updated: 'Tagging-Regeln aktualisiert' 586 tagging_rules_updated: 'Tagging-Regeln aktualisiert'
585 tagging_rules_deleted: 'Tagging-Regel gelöscht' 587 tagging_rules_deleted: 'Tagging-Regel gelöscht'
586 feed_token_updated: 'RSS-Token aktualisiert' 588 feed_token_updated: 'RSS-Token aktualisiert'
589 # feed_token_revoked: 'RSS token revoked'
587 annotations_reset: Anmerkungen zurücksetzen 590 annotations_reset: Anmerkungen zurücksetzen
588 tags_reset: Tags zurücksetzen 591 tags_reset: Tags zurücksetzen
589 entries_reset: Einträge zurücksetzen 592 entries_reset: Einträge zurücksetzen
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
index 5b875652..1f8c47aa 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
@@ -33,6 +33,7 @@ menu:
33 back_to_unread: 'Back to unread articles' 33 back_to_unread: 'Back to unread articles'
34 users_management: 'Users management' 34 users_management: 'Users management'
35 site_credentials: 'Site credentials' 35 site_credentials: 'Site credentials'
36 quickstart: "Quickstart"
36 top: 37 top:
37 add_new_entry: 'Add a new entry' 38 add_new_entry: 'Add a new entry'
38 search: 'Search' 39 search: 'Search'
@@ -91,6 +92,7 @@ config:
91 no_token: 'No token' 92 no_token: 'No token'
92 token_create: 'Create your token' 93 token_create: 'Create your token'
93 token_reset: 'Regenerate your token' 94 token_reset: 'Regenerate your token'
95 token_revoke: 'Revoke the token'
94 feed_links: 'Feed links' 96 feed_links: 'Feed links'
95 feed_link: 97 feed_link:
96 unread: 'Unread' 98 unread: 'Unread'
@@ -572,7 +574,7 @@ site_credential:
572 create_new_one: Create a new credential 574 create_new_one: Create a new credential
573 form: 575 form:
574 username_label: 'Username' 576 username_label: 'Username'
575 host_label: 'Host' 577 host_label: 'Host (subdomain.example.org, .example.org, etc.)'
576 password_label: 'Password' 578 password_label: 'Password'
577 save: Save 579 save: Save
578 delete: Delete 580 delete: Delete
@@ -593,6 +595,7 @@ flashes:
593 tagging_rules_updated: 'Tagging rules updated' 595 tagging_rules_updated: 'Tagging rules updated'
594 tagging_rules_deleted: 'Tagging rule deleted' 596 tagging_rules_deleted: 'Tagging rule deleted'
595 feed_token_updated: 'Feed token updated' 597 feed_token_updated: 'Feed token updated'
598 feed_token_revoked: 'RSS token revoked'
596 annotations_reset: Annotations reset 599 annotations_reset: Annotations reset
597 tags_reset: Tags reset 600 tags_reset: Tags reset
598 entries_reset: Entries reset 601 entries_reset: Entries reset
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
index 562b4191..06fa1e48 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
@@ -33,6 +33,7 @@ menu:
33 back_to_unread: 'Volver a los artículos sin leer' 33 back_to_unread: 'Volver a los artículos sin leer'
34 users_management: 'Configuración de usuarios' 34 users_management: 'Configuración de usuarios'
35 # site_credentials: 'Site credentials' 35 # site_credentials: 'Site credentials'
36 quickstart: "Inicio rápido"
36 top: 37 top:
37 add_new_entry: 'Añadir un nuevo artículo' 38 add_new_entry: 'Añadir un nuevo artículo'
38 search: 'Buscar' 39 search: 'Buscar'
@@ -91,6 +92,7 @@ config:
91 no_token: 'Sin token' 92 no_token: 'Sin token'
92 token_create: 'Crear token' 93 token_create: 'Crear token'
93 token_reset: 'Reiniciar token' 94 token_reset: 'Reiniciar token'
95 # token_revoke: 'Revoke the token'
94 feed_links: 'URLs de feeds RSS' 96 feed_links: 'URLs de feeds RSS'
95 feed_link: 97 feed_link:
96 unread: 'sin leer' 98 unread: 'sin leer'
@@ -572,7 +574,7 @@ site_credential:
572 # create_new_one: Create a new credential 574 # create_new_one: Create a new credential
573 # form: 575 # form:
574 # username_label: 'Username' 576 # username_label: 'Username'
575 # host_label: 'Host' 577 # host_label: 'Host (subdomain.example.org, .example.org, etc.)'
576 # password_label: 'Password' 578 # password_label: 'Password'
577 # save: Save 579 # save: Save
578 # delete: Delete 580 # delete: Delete
@@ -593,6 +595,7 @@ flashes:
593 tagging_rules_updated: 'Regla de etiquetado actualizada' 595 tagging_rules_updated: 'Regla de etiquetado actualizada'
594 tagging_rules_deleted: 'Regla de etiquetado eliminada' 596 tagging_rules_deleted: 'Regla de etiquetado eliminada'
595 feed_token_updated: 'Token RSS actualizado' 597 feed_token_updated: 'Token RSS actualizado'
598 # feed_token_revoked: 'RSS token revoked'
596 annotations_reset: Anotaciones reiniciadas 599 annotations_reset: Anotaciones reiniciadas
597 tags_reset: Etiquetas reiniciadas 600 tags_reset: Etiquetas reiniciadas
598 entries_reset: Artículos reiniciados 601 entries_reset: Artículos reiniciados
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
index f360e0d6..5c1eaccc 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
@@ -33,6 +33,7 @@ menu:
33 back_to_unread: 'بازگشت به خوانده‌نشده‌ها' 33 back_to_unread: 'بازگشت به خوانده‌نشده‌ها'
34 # users_management: 'Users management' 34 # users_management: 'Users management'
35 # site_credentials: 'Site credentials' 35 # site_credentials: 'Site credentials'
36 quickstart: "Quickstart"
36 top: 37 top:
37 add_new_entry: 'افزودن مقالهٔ تازه' 38 add_new_entry: 'افزودن مقالهٔ تازه'
38 search: 'جستجو' 39 search: 'جستجو'
@@ -91,6 +92,7 @@ config:
91 no_token: 'بدون کد' 92 no_token: 'بدون کد'
92 token_create: 'کد خود را بسازید' 93 token_create: 'کد خود را بسازید'
93 token_reset: 'بازنشانی کد' 94 token_reset: 'بازنشانی کد'
95 # token_revoke: 'Revoke the token'
94 feed_links: 'پیوند آر-اس-اس' 96 feed_links: 'پیوند آر-اس-اس'
95 feed_link: 97 feed_link:
96 unread: 'خوانده‌نشده' 98 unread: 'خوانده‌نشده'
@@ -572,7 +574,7 @@ site_credential:
572 # create_new_one: Create a new credential 574 # create_new_one: Create a new credential
573 # form: 575 # form:
574 # username_label: 'Username' 576 # username_label: 'Username'
575 # host_label: 'Host' 577 # host_label: 'Host (subdomain.example.org, .example.org, etc.)'
576 # password_label: 'Password' 578 # password_label: 'Password'
577 # save: Save 579 # save: Save
578 # delete: Delete 580 # delete: Delete
@@ -593,6 +595,7 @@ flashes:
593 tagging_rules_updated: 'برچسب‌گذاری خودکار به‌روز شد' 595 tagging_rules_updated: 'برچسب‌گذاری خودکار به‌روز شد'
594 tagging_rules_deleted: 'قانون برچسب‌گذاری پاک شد' 596 tagging_rules_deleted: 'قانون برچسب‌گذاری پاک شد'
595 feed_token_updated: 'کد آر-اس-اس به‌روز شد' 597 feed_token_updated: 'کد آر-اس-اس به‌روز شد'
598 # feed_token_revoked: 'RSS token revoked'
596 # annotations_reset: Annotations reset 599 # annotations_reset: Annotations reset
597 # tags_reset: Tags reset 600 # tags_reset: Tags reset
598 # entries_reset: Entries reset 601 # entries_reset: Entries reset
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
index 79f15154..09c1cc8f 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
@@ -33,6 +33,7 @@ menu:
33 back_to_unread: "Retour aux articles non lus" 33 back_to_unread: "Retour aux articles non lus"
34 users_management: "Gestion des utilisateurs" 34 users_management: "Gestion des utilisateurs"
35 site_credentials: 'Accès aux sites' 35 site_credentials: 'Accès aux sites'
36 quickstart: "Pour bien débuter"
36 top: 37 top:
37 add_new_entry: "Sauvegarder un nouvel article" 38 add_new_entry: "Sauvegarder un nouvel article"
38 search: "Rechercher" 39 search: "Rechercher"
@@ -91,6 +92,7 @@ config:
91 no_token: "Aucun jeton généré" 92 no_token: "Aucun jeton généré"
92 token_create: "Créez votre jeton" 93 token_create: "Créez votre jeton"
93 token_reset: "Réinitialisez votre jeton" 94 token_reset: "Réinitialisez votre jeton"
95 token_revoke: 'Supprimer le jeton'
94 feed_links: "Adresses de vos flux" 96 feed_links: "Adresses de vos flux"
95 feed_link: 97 feed_link:
96 unread: "Non lus" 98 unread: "Non lus"
@@ -573,7 +575,7 @@ site_credential:
573 create_new_one: Créer un nouvel accès à un site 575 create_new_one: Créer un nouvel accès à un site
574 form: 576 form:
575 username_label: 'Identifiant' 577 username_label: 'Identifiant'
576 host_label: 'Domaine' 578 host_label: 'Domaine (subdomain.example.org, .example.org, etc.)'
577 password_label: 'Mot de passe' 579 password_label: 'Mot de passe'
578 save: "Sauvegarder" 580 save: "Sauvegarder"
579 delete: "Supprimer" 581 delete: "Supprimer"
@@ -594,6 +596,7 @@ flashes:
594 tagging_rules_updated: "Règles mises à jour" 596 tagging_rules_updated: "Règles mises à jour"
595 tagging_rules_deleted: "Règle supprimée" 597 tagging_rules_deleted: "Règle supprimée"
596 feed_token_updated: "Jeton des flux mis à jour" 598 feed_token_updated: "Jeton des flux mis à jour"
599 feed_token_revoked: 'Jeton des flux supprimé'
597 annotations_reset: "Annotations supprimées" 600 annotations_reset: "Annotations supprimées"
598 tags_reset: "Tags supprimés" 601 tags_reset: "Tags supprimés"
599 entries_reset: "Articles supprimés" 602 entries_reset: "Articles supprimés"
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
index daef359f..92f2415b 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
@@ -33,6 +33,7 @@ menu:
33 back_to_unread: 'Torna ai contenuti non letti' 33 back_to_unread: 'Torna ai contenuti non letti'
34 users_management: 'Gestione utenti' 34 users_management: 'Gestione utenti'
35 site_credentials: 'Credenziali sito' 35 site_credentials: 'Credenziali sito'
36 quickstart: "Introduzione"
36 top: 37 top:
37 add_new_entry: 'Aggiungi un nuovo contenuto' 38 add_new_entry: 'Aggiungi un nuovo contenuto'
38 search: 'Cerca' 39 search: 'Cerca'
@@ -91,6 +92,7 @@ config:
91 no_token: 'Nessun token' 92 no_token: 'Nessun token'
92 token_create: 'Crea il tuo token' 93 token_create: 'Crea il tuo token'
93 token_reset: 'Rigenera il tuo token' 94 token_reset: 'Rigenera il tuo token'
95 # token_revoke: 'Revoke the token'
94 feed_links: 'Collegamenti RSS' 96 feed_links: 'Collegamenti RSS'
95 feed_link: 97 feed_link:
96 unread: 'Non letti' 98 unread: 'Non letti'
@@ -571,7 +573,7 @@ site_credential:
571 # create_new_one: Create a new credential 573 # create_new_one: Create a new credential
572 # form: 574 # form:
573 # username_label: 'Username' 575 # username_label: 'Username'
574 # host_label: 'Host' 576 # host_label: 'Host (subdomain.example.org, .example.org, etc.)'
575 # password_label: 'Password' 577 # password_label: 'Password'
576 # save: Save 578 # save: Save
577 # delete: Delete 579 # delete: Delete
@@ -592,6 +594,7 @@ flashes:
592 tagging_rules_updated: 'Regole di etichettatura aggiornate' 594 tagging_rules_updated: 'Regole di etichettatura aggiornate'
593 tagging_rules_deleted: 'Regola di etichettatura eliminate' 595 tagging_rules_deleted: 'Regola di etichettatura eliminate'
594 feed_token_updated: 'RSS token aggiornato' 596 feed_token_updated: 'RSS token aggiornato'
597 # feed_token_revoked: 'RSS token revoked'
595 annotations_reset: Reset annotazioni 598 annotations_reset: Reset annotazioni
596 tags_reset: Reset etichette 599 tags_reset: Reset etichette
597 entries_reset: Reset articoli 600 entries_reset: Reset articoli
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
index 980ddeb4..4439cbcd 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
@@ -33,6 +33,7 @@ menu:
33 back_to_unread: 'Tornar als articles pas legits' 33 back_to_unread: 'Tornar als articles pas legits'
34 users_management: 'Gestion dels utilizaires' 34 users_management: 'Gestion dels utilizaires'
35 site_credentials: 'Identificants del site' 35 site_credentials: 'Identificants del site'
36 quickstart: "Per ben començar"
36 top: 37 top:
37 add_new_entry: 'Enregistrar un novèl article' 38 add_new_entry: 'Enregistrar un novèl article'
38 search: 'Cercar' 39 search: 'Cercar'
@@ -91,6 +92,7 @@ config:
91 no_token: 'Pas cap de geton generat' 92 no_token: 'Pas cap de geton generat'
92 token_create: 'Creatz vòstre geton' 93 token_create: 'Creatz vòstre geton'
93 token_reset: 'Reïnicializatz vòstre geton' 94 token_reset: 'Reïnicializatz vòstre geton'
95 # token_revoke: 'Revoke the token'
94 feed_links: 'URLs de vòstres fluxes RSS' 96 feed_links: 'URLs de vòstres fluxes RSS'
95 feed_link: 97 feed_link:
96 unread: 'Pas legits' 98 unread: 'Pas legits'
@@ -571,7 +573,7 @@ site_credential:
571 create_new_one: Crear un novèl identificant 573 create_new_one: Crear un novèl identificant
572 form: 574 form:
573 username_label: "Nom d'utilizaire" 575 username_label: "Nom d'utilizaire"
574 host_label: 'Òste' 576 host_label: 'Òste (subdomain.example.org, .example.org, etc.)'
575 password_label: 'Senhal' 577 password_label: 'Senhal'
576 save: 'Enregistrar' 578 save: 'Enregistrar'
577 delete: 'Suprimir' 579 delete: 'Suprimir'
@@ -592,6 +594,7 @@ flashes:
592 tagging_rules_updated: 'Règlas misa a jorn' 594 tagging_rules_updated: 'Règlas misa a jorn'
593 tagging_rules_deleted: 'Règla suprimida' 595 tagging_rules_deleted: 'Règla suprimida'
594 feed_token_updated: 'Geton RSS mes a jorn' 596 feed_token_updated: 'Geton RSS mes a jorn'
597 # feed_token_revoked: 'RSS token revoked'
595 annotations_reset: Anotacions levadas 598 annotations_reset: Anotacions levadas
596 tags_reset: Etiquetas levadas 599 tags_reset: Etiquetas levadas
597 entries_reset: Articles levats 600 entries_reset: Articles levats
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
index 3813ac37..8311770f 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
@@ -33,6 +33,7 @@ menu:
33 back_to_unread: 'Powrót do nieprzeczytanych artykułów' 33 back_to_unread: 'Powrót do nieprzeczytanych artykułów'
34 users_management: 'Zarządzanie użytkownikami' 34 users_management: 'Zarządzanie użytkownikami'
35 site_credentials: 'Poświadczenia strony' 35 site_credentials: 'Poświadczenia strony'
36 quickstart: "Szybki start"
36 top: 37 top:
37 add_new_entry: 'Dodaj nowy wpis' 38 add_new_entry: 'Dodaj nowy wpis'
38 search: 'Szukaj' 39 search: 'Szukaj'
@@ -91,6 +92,7 @@ config:
91 no_token: 'Brak tokena' 92 no_token: 'Brak tokena'
92 token_create: 'Stwórz tokena' 93 token_create: 'Stwórz tokena'
93 token_reset: 'Zresetuj swojego tokena' 94 token_reset: 'Zresetuj swojego tokena'
95 # token_revoke: 'Revoke the token'
94 feed_links: 'RSS links' 96 feed_links: 'RSS links'
95 feed_link: 97 feed_link:
96 unread: 'Nieprzeczytane' 98 unread: 'Nieprzeczytane'
@@ -571,7 +573,7 @@ site_credential:
571 create_new_one: Stwórz nowe poświadczenie 573 create_new_one: Stwórz nowe poświadczenie
572 form: 574 form:
573 username_label: 'Nazwa użytkownika' 575 username_label: 'Nazwa użytkownika'
574 host_label: 'Host' 576 host_label: 'Host (subdomain.example.org, .example.org, etc.)'
575 password_label: 'Hasło' 577 password_label: 'Hasło'
576 save: Zapisz 578 save: Zapisz
577 delete: Usuń 579 delete: Usuń
@@ -592,6 +594,7 @@ flashes:
592 tagging_rules_updated: 'Reguły tagowania zaktualizowane' 594 tagging_rules_updated: 'Reguły tagowania zaktualizowane'
593 tagging_rules_deleted: 'Reguła tagowania usunięta' 595 tagging_rules_deleted: 'Reguła tagowania usunięta'
594 feed_token_updated: 'Token kanału RSS zaktualizowany' 596 feed_token_updated: 'Token kanału RSS zaktualizowany'
597 # feed_token_revoked: 'RSS token revoked'
595 annotations_reset: Zresetuj adnotacje 598 annotations_reset: Zresetuj adnotacje
596 tags_reset: Zresetuj tagi 599 tags_reset: Zresetuj tagi
597 entries_reset: Zresetuj wpisy 600 entries_reset: Zresetuj wpisy
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
index 96943c05..c83bf6c1 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
@@ -33,6 +33,7 @@ menu:
33 back_to_unread: 'Voltar para os artigos não lidos' 33 back_to_unread: 'Voltar para os artigos não lidos'
34 users_management: 'Gestão de Usuários' 34 users_management: 'Gestão de Usuários'
35 # site_credentials: 'Site credentials' 35 # site_credentials: 'Site credentials'
36 quickstart: "Começo Rápido"
36 top: 37 top:
37 add_new_entry: 'Adicionar uma nova entrada' 38 add_new_entry: 'Adicionar uma nova entrada'
38 search: 'Pesquisa' 39 search: 'Pesquisa'
@@ -91,6 +92,7 @@ config:
91 no_token: 'Nenhum Token' 92 no_token: 'Nenhum Token'
92 token_create: 'Criar seu token' 93 token_create: 'Criar seu token'
93 token_reset: 'Gerar novamente seu token' 94 token_reset: 'Gerar novamente seu token'
95 # token_revoke: 'Revoke the token'
94 feed_links: 'Links RSS' 96 feed_links: 'Links RSS'
95 feed_link: 97 feed_link:
96 unread: 'Não lido' 98 unread: 'Não lido'
@@ -571,7 +573,7 @@ site_credential:
571 # create_new_one: Create a new credential 573 # create_new_one: Create a new credential
572 form: 574 form:
573 # username_label: 'Username' 575 # username_label: 'Username'
574 # host_label: 'Host' 576 # host_label: 'Host (subdomain.example.org, .example.org, etc.)'
575 # password_label: 'Password' 577 # password_label: 'Password'
576 save: 'Salvar' 578 save: 'Salvar'
577 delete: 'Apagar' 579 delete: 'Apagar'
@@ -592,6 +594,7 @@ flashes:
592 tagging_rules_updated: 'Regras de tags atualizadas' 594 tagging_rules_updated: 'Regras de tags atualizadas'
593 tagging_rules_deleted: 'Regra de tag apagada' 595 tagging_rules_deleted: 'Regra de tag apagada'
594 feed_token_updated: 'Token RSS atualizado' 596 feed_token_updated: 'Token RSS atualizado'
597 # feed_token_revoked: 'RSS token revoked'
595 # annotations_reset: Annotations reset 598 # annotations_reset: Annotations reset
596 # tags_reset: Tags reset 599 # tags_reset: Tags reset
597 # entries_reset: Entries reset 600 # entries_reset: Entries reset
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
index 0ce11e74..c8bf8083 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
@@ -33,6 +33,7 @@ menu:
33 back_to_unread: 'Înapoi la articolele necitite' 33 back_to_unread: 'Înapoi la articolele necitite'
34 # users_management: 'Users management' 34 # users_management: 'Users management'
35 # site_credentials: 'Site credentials' 35 # site_credentials: 'Site credentials'
36 # quickstart: "Quickstart"
36 top: 37 top:
37 add_new_entry: 'Introdu un nou articol' 38 add_new_entry: 'Introdu un nou articol'
38 search: 'Căutare' 39 search: 'Căutare'
@@ -91,6 +92,7 @@ config:
91 no_token: 'Fără token' 92 no_token: 'Fără token'
92 token_create: 'Crează-ți token' 93 token_create: 'Crează-ți token'
93 token_reset: 'Resetează-ți token-ul' 94 token_reset: 'Resetează-ți token-ul'
95 # token_revoke: 'Revoke the token'
94 feed_links: 'Link-uri RSS' 96 feed_links: 'Link-uri RSS'
95 feed_link: 97 feed_link:
96 unread: 'Unread' 98 unread: 'Unread'
@@ -571,7 +573,7 @@ site_credential:
571 # create_new_one: Create a new credential 573 # create_new_one: Create a new credential
572 # form: 574 # form:
573 # username_label: 'Username' 575 # username_label: 'Username'
574 # host_label: 'Host' 576 # host_label: 'Host (subdomain.example.org, .example.org, etc.)'
575 # password_label: 'Password' 577 # password_label: 'Password'
576 # save: Save 578 # save: Save
577 # delete: Delete 579 # delete: Delete
@@ -592,6 +594,7 @@ flashes:
592 # tagging_rules_updated: 'Tagging rules updated' 594 # tagging_rules_updated: 'Tagging rules updated'
593 # tagging_rules_deleted: 'Tagging rule deleted' 595 # tagging_rules_deleted: 'Tagging rule deleted'
594 # feed_token_updated: 'RSS token updated' 596 # feed_token_updated: 'RSS token updated'
597 # feed_token_revoked: 'RSS token revoked'
595 # annotations_reset: Annotations reset 598 # annotations_reset: Annotations reset
596 # tags_reset: Tags reset 599 # tags_reset: Tags reset
597 # entries_reset: Entries reset 600 # entries_reset: Entries reset
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.ru.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.ru.yml
index 2ee2d83a..5d0e70c9 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.ru.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.ru.yml
@@ -32,6 +32,8 @@ menu:
32 save_link: 'Сохранить ссылку' 32 save_link: 'Сохранить ссылку'
33 back_to_unread: 'Назад к непрочитанным записям' 33 back_to_unread: 'Назад к непрочитанным записям'
34 users_management: 'Управление пользователями' 34 users_management: 'Управление пользователями'
35 site_credentials: 'Site credentials'
36 quickstart: "Быстрый старт"
35 top: 37 top:
36 add_new_entry: 'Добавить новую запись' 38 add_new_entry: 'Добавить новую запись'
37 search: 'Поиск' 39 search: 'Поиск'
@@ -89,6 +91,7 @@ config:
89 no_token: 'Ключ не задан' 91 no_token: 'Ключ не задан'
90 token_create: 'Создать ключ' 92 token_create: 'Создать ключ'
91 token_reset: 'Пересоздать ключ' 93 token_reset: 'Пересоздать ключ'
94 # token_revoke: 'Revoke the token'
92 feed_links: 'ссылка на RSS' 95 feed_links: 'ссылка на RSS'
93 feed_link: 96 feed_link:
94 unread: 'непрочитанные' 97 unread: 'непрочитанные'
@@ -558,6 +561,7 @@ flashes:
558 tagging_rules_updated: 'Правила тегировния обновлены' 561 tagging_rules_updated: 'Правила тегировния обновлены'
559 tagging_rules_deleted: 'Правила тегировния удалены' 562 tagging_rules_deleted: 'Правила тегировния удалены'
560 feed_token_updated: 'RSS ключ обновлен' 563 feed_token_updated: 'RSS ключ обновлен'
564 # feed_token_revoked: 'RSS token revoked'
561 annotations_reset: "Аннотации сброшены" 565 annotations_reset: "Аннотации сброшены"
562 tags_reset: "Теги сброшены" 566 tags_reset: "Теги сброшены"
563 entries_reset: "Записи сброшены" 567 entries_reset: "Записи сброшены"
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.th.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.th.yml
index e04eee68..66426d53 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.th.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.th.yml
@@ -33,6 +33,7 @@ menu:
33 back_to_unread: 'กลับไปยังรายการที่ไม่ได้อ่าน' 33 back_to_unread: 'กลับไปยังรายการที่ไม่ได้อ่าน'
34 users_management: 'การจัดการผู้ใช้' 34 users_management: 'การจัดการผู้ใช้'
35 site_credentials: 'การรับรองไซต์' 35 site_credentials: 'การรับรองไซต์'
36 quickstart: "เริ่มแบบด่วน"
36 top: 37 top:
37 add_new_entry: 'เพิ่มรายการใหม่' 38 add_new_entry: 'เพิ่มรายการใหม่'
38 search: 'ค้นหา' 39 search: 'ค้นหา'
@@ -91,6 +92,7 @@ config:
91 no_token: 'ไม่มีเครื่องหมาย' 92 no_token: 'ไม่มีเครื่องหมาย'
92 token_create: 'สร้างเครื่องหมาย' 93 token_create: 'สร้างเครื่องหมาย'
93 token_reset: 'ทำเครื่องหมาย' 94 token_reset: 'ทำเครื่องหมาย'
95 # token_revoke: 'Revoke the token'
94 feed_links: 'ลิงค์ RSS' 96 feed_links: 'ลิงค์ RSS'
95 feed_link: 97 feed_link:
96 unread: 'ยังไมได้่อ่าน' 98 unread: 'ยังไมได้่อ่าน'
@@ -569,7 +571,7 @@ site_credential:
569 create_new_one: สร้างข้อมูลส่วนตัวใหม่ 571 create_new_one: สร้างข้อมูลส่วนตัวใหม่
570 form: 572 form:
571 username_label: 'ชื่อผู้ใช้' 573 username_label: 'ชื่อผู้ใช้'
572 host_label: 'โฮส' 574 host_label: 'โฮส (subdomain.example.org, .example.org, etc.)'
573 password_label: 'รหัสผ่าน' 575 password_label: 'รหัสผ่าน'
574 save: บันทึก 576 save: บันทึก
575 delete: ลบ 577 delete: ลบ
@@ -590,6 +592,7 @@ flashes:
590 tagging_rules_updated: 'อัปเดตการแท็กข้อบังคับ' 592 tagging_rules_updated: 'อัปเดตการแท็กข้อบังคับ'
591 tagging_rules_deleted: 'การลบข้อบังคับของแท็ก' 593 tagging_rules_deleted: 'การลบข้อบังคับของแท็ก'
592 feed_token_updated: 'อัปเดตเครื่องหมาย RSS ' 594 feed_token_updated: 'อัปเดตเครื่องหมาย RSS '
595 # feed_token_revoked: 'RSS token revoked'
593 annotations_reset: รีเซ็ตหมายเหตุ 596 annotations_reset: รีเซ็ตหมายเหตุ
594 tags_reset: รีเซ็ตแท็ก 597 tags_reset: รีเซ็ตแท็ก
595 entries_reset: รีเซ็ตรายการ 598 entries_reset: รีเซ็ตรายการ
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
index 2f86f25d..50bd38e3 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
@@ -33,6 +33,7 @@ menu:
33 back_to_unread: 'Okunmayan makalelere geri dön' 33 back_to_unread: 'Okunmayan makalelere geri dön'
34 # users_management: 'Users management' 34 # users_management: 'Users management'
35 # site_credentials: 'Site credentials' 35 # site_credentials: 'Site credentials'
36 quickstart: "Hızlı başlangıç"
36 top: 37 top:
37 add_new_entry: 'Yeni bir makale ekle' 38 add_new_entry: 'Yeni bir makale ekle'
38 search: 'Ara' 39 search: 'Ara'
@@ -91,6 +92,7 @@ config:
91 no_token: 'Belirteç (token) yok' 92 no_token: 'Belirteç (token) yok'
92 token_create: 'Yeni belirteç (token) oluştur' 93 token_create: 'Yeni belirteç (token) oluştur'
93 token_reset: 'Belirteci (token) sıfırla' 94 token_reset: 'Belirteci (token) sıfırla'
95 # token_revoke: 'Revoke the token'
94 feed_links: 'RSS akış bağlantıları' 96 feed_links: 'RSS akış bağlantıları'
95 feed_link: 97 feed_link:
96 unread: 'Okunmayan' 98 unread: 'Okunmayan'
diff --git a/src/Wallabag/CoreBundle/Resources/views/base.html.twig b/src/Wallabag/CoreBundle/Resources/views/base.html.twig
index aa388bcb..c0eecd57 100644
--- a/src/Wallabag/CoreBundle/Resources/views/base.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/base.html.twig
@@ -8,6 +8,7 @@
8 {% block head %} 8 {% block head %}
9 <meta name="viewport" content="initial-scale=1.0"> 9 <meta name="viewport" content="initial-scale=1.0">
10 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 10 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
11 <meta name="referrer" content="strict-origin-when-cross-origin">
11 <!--[if IE]> 12 <!--[if IE]>
12 <meta http-equiv="X-UA-Compatible" content="IE=10"> 13 <meta http-equiv="X-UA-Compatible" content="IE=10">
13 <![endif]--> 14 <![endif]-->
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig
index 4ef6ab3c..a1caf242 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig
@@ -111,14 +111,14 @@
111 {% else %} 111 {% else %}
112 <em>{{ 'config.form_feed.no_token'|trans }}</em> 112 <em>{{ 'config.form_feed.no_token'|trans }}</em>
113 {% endif %} 113 {% endif %}
114 114
115 <a href="{{ path('generate_token') }}"> 115 {% if feed.token %}
116 {% if feed.token %} 116 – <a href="{{ path('generate_token') }}">{{ 'config.form_feed.token_reset'|trans }}</a>
117 {{ 'config.form_feed.token_reset'|trans }} 117 <a href="{{ path('revoke_token') }}">{{ 'config.form_feed.token_revoke'|trans }}</a>
118 {% else %} 118 {% else %}
119 {{ 'config.form_feed.token_create'|trans }} 119 <a href="{{ path('generate_token') }}">{{ 'config.form_feed.token_create'|trans }}</a>
120 {% endif %} 120 {% endif %}
121 </a> 121
122 </div> 122 </div>
123 </fieldset> 123 </fieldset>
124 124
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig
index 35a54daf..6b1e2bd7 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/layout.html.twig
@@ -50,6 +50,7 @@
50 <li class="menu howto"><a href="{{ path('howto') }}">{{ 'menu.left.howto'|trans }}</a></li> 50 <li class="menu howto"><a href="{{ path('howto') }}">{{ 'menu.left.howto'|trans }}</a></li>
51 <li class="menu developer"><a href="{{ path('developer') }}">{{ 'menu.left.developer'|trans }}</a></li> 51 <li class="menu developer"><a href="{{ path('developer') }}">{{ 'menu.left.developer'|trans }}</a></li>
52 <li class="menu about"><a href="{{ path('about') }}">{{ 'footer.wallabag.about'|trans }}</a></li> 52 <li class="menu about"><a href="{{ path('about') }}">{{ 'footer.wallabag.about'|trans }}</a></li>
53 <li class="menu quickstart"><a href="{{ path('quickstart') }}">{{ 'menu.left.quickstart'|trans }}</a></li>
53 <li class="menu logout"><a class="icon icon-power" href="{{ path('fos_user_security_logout') }}">{{ 'menu.left.logout'|trans }}</a></li> 54 <li class="menu logout"><a class="icon icon-power" href="{{ path('fos_user_security_logout') }}">{{ 'menu.left.logout'|trans }}</a></li>
54 </ul> 55 </ul>
55{% endblock %} 56{% endblock %}
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/share.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/share.html.twig
index e1c7aad9..4294a60d 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/share.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/share.html.twig
@@ -29,9 +29,6 @@
29 <h1>{{ entry.title|e|raw }}</h1> 29 <h1>{{ entry.title|e|raw }}</h1>
30 <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> 30 <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>
31 <p class="shared-by">{{ "entry.public.shared_by_wallabag"|trans({'%wallabag_instance%': url('homepage'), '%username%': entry.user.username})|raw }}.</p> 31 <p class="shared-by">{{ "entry.public.shared_by_wallabag"|trans({'%wallabag_instance%': url('homepage'), '%username%': entry.user.username})|raw }}.</p>
32 {% if entry.previewPicture is not null %}
33 <img class="preview" src="{{ entry.previewPicture }}" alt="{{ entry.title|striptags|e('html_attr') }}" />
34 {% endif %}
35 </header> 32 </header>
36 <article class="block"> 33 <article class="block">
37 {{ entry.content | raw }} 34 {{ entry.content | raw }}
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig
index 818fc4e7..a48c9e28 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig
@@ -140,12 +140,13 @@
140 {% else %} 140 {% else %}
141 <em>{{ 'config.form_feed.no_token'|trans }}</em> 141 <em>{{ 'config.form_feed.no_token'|trans }}</em>
142 {% endif %} 142 {% endif %}
143 – <a href="{{ path('generate_token') }}"> 143
144 {% if feed.token %} 144 {% if feed.token %}
145 {{ 'config.form_feed.token_reset'|trans }} 145 – <a href="{{ path('generate_token') }}">{{ 'config.form_feed.token_reset'|trans }}</a>
146 – <a href="{{ path('revoke_token') }}">{{ 'config.form_feed.token_revoke'|trans }}</a>
146 {% else %} 147 {% else %}
147 {{ 'config.form_feed.token_create'|trans }} 148 – <a href="{{ path('generate_token') }}">{{ 'config.form_feed.token_create'|trans }}</a>
148 {% endif %}</a> 149 {% endif %}
149 </div> 150 </div>
150 </div> 151 </div>
151 </div> 152 </div>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/Card/_content.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/Card/_content.html.twig
index 1f3cd1a7..1102a0bd 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/Card/_content.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/Card/_content.html.twig
@@ -8,8 +8,11 @@
8 8
9 <div class="{{ subClass|default('original grey-text') }}"> 9 <div class="{{ subClass|default('original grey-text') }}">
10 <a href="{{ entry.url|e }}" target="_blank" title="{{ entry.domainName|removeWww }}" class="tool grey-text">{{ entry.domainName|removeWww }}</a> 10 <a href="{{ entry.url|e }}" target="_blank" title="{{ entry.domainName|removeWww }}" class="tool grey-text">{{ entry.domainName|removeWww }}</a>
11 {% if withTags is defined %} 11 {% if withMetadata is defined %}
12 {% include "@WallabagCore/themes/material/Entry/_tags.html.twig" with {'tags': entry.tags | slice(0, 3), 'entryId': entry.id, 'listClass': ' hide-on-med-and-down'} only %} 12 {% include "@WallabagCore/themes/material/Entry/_tags.html.twig" with {'tags': entry.tags | slice(0, 3), 'entryId': entry.id, 'listClass': ' hide-on-med-and-down'} only %}
13 <div class="reading-time grey-text">
14 <div class="card-reading-time">{% include "@WallabagCore/themes/material/Entry/_reading_time.html.twig" with {'entry': entry} only %}</div>
15 </div>
13 {% endif %} 16 {% endif %}
14 </div> 17 </div>
15</div> 18</div>
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 1c00f2fa..6a095035 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
@@ -5,7 +5,7 @@
5 <span class="preview{{ previewClassModifier }}" style="background-image: url({{ entry.previewPicture | default(asset('wallassets/themes/_global/img/logo-square.svg')) }})"></span> 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 </div> 7 </div>
8 {% 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, 'withMetadata': true, 'subClass': 'metadata'} only %}
9 <ul class="tools-list hide-on-small-only"> 9 <ul class="tools-list hide-on-small-only">
10 <li> 10 <li>
11 <a title="{{ 'entry.list.toogle_as_read'|trans }}" class="tool grey-text" href="{{ path('archive_entry', { 'id': entry.id }) }}"><i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i></a> 11 <a title="{{ 'entry.list.toogle_as_read'|trans }}" class="tool grey-text" href="{{ path('archive_entry', { 'id': entry.id }) }}"><i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i></a>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig
index b9c45567..c51d07fc 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig
@@ -91,6 +91,9 @@
91 <li class="bold {% if currentRoute == 'howto' %}active{% endif %}"> 91 <li class="bold {% if currentRoute == 'howto' %}active{% endif %}">
92 <a class="waves-effect" href="{{ path('howto') }}">{{ 'menu.left.howto'|trans }}</a> 92 <a class="waves-effect" href="{{ path('howto') }}">{{ 'menu.left.howto'|trans }}</a>
93 </li> 93 </li>
94 <li class="bold {% if currentRoute == 'quickstart' %}active{% endif %}">
95 <a class="waves-effect" href="{{ path('quickstart') }}">{{ 'menu.left.quickstart'|trans }}</a>
96 </li>
94 <li class="bold"> 97 <li class="bold">
95 <a class="waves-effect icon icon-power" href="{{ path('fos_user_security_logout') }}">{{ 'menu.left.logout'|trans }}</a> 98 <a class="waves-effect icon icon-power" href="{{ path('fos_user_security_logout') }}">{{ 'menu.left.logout'|trans }}</a>
96 </li> 99 </li>
diff --git a/src/Wallabag/ImportBundle/Import/InstapaperImport.php b/src/Wallabag/ImportBundle/Import/InstapaperImport.php
index 439c978c..f7bee9ef 100644
--- a/src/Wallabag/ImportBundle/Import/InstapaperImport.php
+++ b/src/Wallabag/ImportBundle/Import/InstapaperImport.php
@@ -93,6 +93,10 @@ class InstapaperImport extends AbstractImport
93 return false; 93 return false;
94 } 94 }
95 95
96 // most recent articles are first, which means we should create them at the end so they will show up first
97 // as Instapaper doesn't export the creation date of the article
98 $entries = array_reverse($entries);
99
96 if ($this->producer) { 100 if ($this->producer) {
97 $this->parseEntriesForProducer($entries); 101 $this->parseEntriesForProducer($entries);
98 102
diff --git a/src/Wallabag/ImportBundle/Import/PocketImport.php b/src/Wallabag/ImportBundle/Import/PocketImport.php
index a39d8156..746120af 100644
--- a/src/Wallabag/ImportBundle/Import/PocketImport.php
+++ b/src/Wallabag/ImportBundle/Import/PocketImport.php
@@ -2,13 +2,22 @@
2 2
3namespace Wallabag\ImportBundle\Import; 3namespace Wallabag\ImportBundle\Import;
4 4
5use GuzzleHttp\Client; 5use Http\Client\Common\HttpMethodsClient;
6use GuzzleHttp\Exception\RequestException; 6use Http\Client\Common\Plugin\ErrorPlugin;
7use Http\Client\Common\PluginClient;
8use Http\Client\Exception\RequestException;
9use Http\Client\HttpClient;
10use Http\Discovery\MessageFactoryDiscovery;
11use Http\Message\MessageFactory;
12use Psr\Http\Message\ResponseInterface;
7use Wallabag\CoreBundle\Entity\Entry; 13use Wallabag\CoreBundle\Entity\Entry;
8 14
9class PocketImport extends AbstractImport 15class PocketImport extends AbstractImport
10{ 16{
11 const NB_ELEMENTS = 5000; 17 const NB_ELEMENTS = 5000;
18 /**
19 * @var HttpMethodsClient
20 */
12 private $client; 21 private $client;
13 private $accessToken; 22 private $accessToken;
14 23
@@ -55,24 +64,18 @@ class PocketImport extends AbstractImport
55 */ 64 */
56 public function getRequestToken($redirectUri) 65 public function getRequestToken($redirectUri)
57 { 66 {
58 $request = $this->client->createRequest('POST', 'https://getpocket.com/v3/oauth/request',
59 [
60 'body' => json_encode([
61 'consumer_key' => $this->user->getConfig()->getPocketConsumerKey(),
62 'redirect_uri' => $redirectUri,
63 ]),
64 ]
65 );
66
67 try { 67 try {
68 $response = $this->client->send($request); 68 $response = $this->client->post('https://getpocket.com/v3/oauth/request', [], json_encode([
69 'consumer_key' => $this->user->getConfig()->getPocketConsumerKey(),
70 'redirect_uri' => $redirectUri,
71 ]));
69 } catch (RequestException $e) { 72 } catch (RequestException $e) {
70 $this->logger->error(sprintf('PocketImport: Failed to request token: %s', $e->getMessage()), ['exception' => $e]); 73 $this->logger->error(sprintf('PocketImport: Failed to request token: %s', $e->getMessage()), ['exception' => $e]);
71 74
72 return false; 75 return false;
73 } 76 }
74 77
75 return $response->json()['code']; 78 return $this->jsonDecode($response)['code'];
76 } 79 }
77 80
78 /** 81 /**
@@ -85,24 +88,18 @@ class PocketImport extends AbstractImport
85 */ 88 */
86 public function authorize($code) 89 public function authorize($code)
87 { 90 {
88 $request = $this->client->createRequest('POST', 'https://getpocket.com/v3/oauth/authorize',
89 [
90 'body' => json_encode([
91 'consumer_key' => $this->user->getConfig()->getPocketConsumerKey(),
92 'code' => $code,
93 ]),
94 ]
95 );
96
97 try { 91 try {
98 $response = $this->client->send($request); 92 $response = $this->client->post('https://getpocket.com/v3/oauth/authorize', [], json_encode([
93 'consumer_key' => $this->user->getConfig()->getPocketConsumerKey(),
94 'code' => $code,
95 ]));
99 } catch (RequestException $e) { 96 } catch (RequestException $e) {
100 $this->logger->error(sprintf('PocketImport: Failed to authorize client: %s', $e->getMessage()), ['exception' => $e]); 97 $this->logger->error(sprintf('PocketImport: Failed to authorize client: %s', $e->getMessage()), ['exception' => $e]);
101 98
102 return false; 99 return false;
103 } 100 }
104 101
105 $this->accessToken = $response->json()['access_token']; 102 $this->accessToken = $this->jsonDecode($response)['access_token'];
106 103
107 return true; 104 return true;
108 } 105 }
@@ -114,29 +111,23 @@ class PocketImport extends AbstractImport
114 { 111 {
115 static $run = 0; 112 static $run = 0;
116 113
117 $request = $this->client->createRequest('POST', 'https://getpocket.com/v3/get',
118 [
119 'body' => json_encode([
120 'consumer_key' => $this->user->getConfig()->getPocketConsumerKey(),
121 'access_token' => $this->accessToken,
122 'detailType' => 'complete',
123 'state' => 'all',
124 'sort' => 'newest',
125 'count' => self::NB_ELEMENTS,
126 'offset' => $offset,
127 ]),
128 ]
129 );
130
131 try { 114 try {
132 $response = $this->client->send($request); 115 $response = $this->client->post('https://getpocket.com/v3/get', [], json_encode([
116 'consumer_key' => $this->user->getConfig()->getPocketConsumerKey(),
117 'access_token' => $this->accessToken,
118 'detailType' => 'complete',
119 'state' => 'all',
120 'sort' => 'newest',
121 'count' => self::NB_ELEMENTS,
122 'offset' => $offset,
123 ]));
133 } catch (RequestException $e) { 124 } catch (RequestException $e) {
134 $this->logger->error(sprintf('PocketImport: Failed to import: %s', $e->getMessage()), ['exception' => $e]); 125 $this->logger->error(sprintf('PocketImport: Failed to import: %s', $e->getMessage()), ['exception' => $e]);
135 126
136 return false; 127 return false;
137 } 128 }
138 129
139 $entries = $response->json(); 130 $entries = $this->jsonDecode($response);
140 131
141 if ($this->producer) { 132 if ($this->producer) {
142 $this->parseEntriesForProducer($entries['list']); 133 $this->parseEntriesForProducer($entries['list']);
@@ -159,13 +150,14 @@ class PocketImport extends AbstractImport
159 } 150 }
160 151
161 /** 152 /**
162 * Set the Guzzle client. 153 * Set the Http client.
163 * 154 *
164 * @param Client $client 155 * @param HttpClient $client
156 * @param MessageFactory|null $messageFactory
165 */ 157 */
166 public function setClient(Client $client) 158 public function setClient(HttpClient $client, MessageFactory $messageFactory = null)
167 { 159 {
168 $this->client = $client; 160 $this->client = new HttpMethodsClient(new PluginClient($client, [new ErrorPlugin()]), $messageFactory ?: MessageFactoryDiscovery::find());
169 } 161 }
170 162
171 /** 163 /**
@@ -252,4 +244,15 @@ class PocketImport extends AbstractImport
252 244
253 return $importedEntry; 245 return $importedEntry;
254 } 246 }
247
248 protected function jsonDecode(ResponseInterface $response)
249 {
250 $data = json_decode((string) $response->getBody(), true);
251
252 if (JSON_ERROR_NONE !== json_last_error()) {
253 throw new \InvalidArgumentException('Unable to parse JSON data: ' . json_last_error_msg());
254 }
255
256 return $data;
257 }
255} 258}
diff --git a/src/Wallabag/ImportBundle/Import/WallabagV2Import.php b/src/Wallabag/ImportBundle/Import/WallabagV2Import.php
index 3e085ecf..2ba26003 100644
--- a/src/Wallabag/ImportBundle/Import/WallabagV2Import.php
+++ b/src/Wallabag/ImportBundle/Import/WallabagV2Import.php
@@ -35,7 +35,9 @@ class WallabagV2Import extends WallabagImport
35 { 35 {
36 return [ 36 return [
37 'html' => $entry['content'], 37 'html' => $entry['content'],
38 'content_type' => $entry['mimetype'], 38 'headers' => [
39 'content-type' => $entry['mimetype'],
40 ],
39 'is_archived' => (bool) ($entry['is_archived'] || $this->markAsRead), 41 'is_archived' => (bool) ($entry['is_archived'] || $this->markAsRead),
40 'is_starred' => (bool) $entry['is_starred'], 42 'is_starred' => (bool) $entry['is_starred'],
41 ] + $entry; 43 ] + $entry;
diff --git a/src/Wallabag/ImportBundle/Resources/config/services.yml b/src/Wallabag/ImportBundle/Resources/config/services.yml
index 2dd7dff8..973c0d03 100644
--- a/src/Wallabag/ImportBundle/Resources/config/services.yml
+++ b/src/Wallabag/ImportBundle/Resources/config/services.yml
@@ -7,13 +7,7 @@ services:
7 class: Wallabag\ImportBundle\Import\ImportChain 7 class: Wallabag\ImportBundle\Import\ImportChain
8 8
9 wallabag_import.pocket.client: 9 wallabag_import.pocket.client:
10 class: GuzzleHttp\Client 10 alias: 'httplug.client.wallabag_import.pocket.client'
11 arguments:
12 -
13 defaults:
14 headers:
15 content-type: "application/json"
16 X-Accept: "application/json"
17 11
18 wallabag_import.pocket.import: 12 wallabag_import.pocket.import:
19 class: Wallabag\ImportBundle\Import\PocketImport 13 class: Wallabag\ImportBundle\Import\PocketImport