aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php74
-rw-r--r--src/Wallabag/AnnotationBundle/Entity/Annotation.php2
-rw-r--r--src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php20
-rw-r--r--src/Wallabag/ApiBundle/Controller/AnnotationRestController.php111
-rw-r--r--src/Wallabag/ApiBundle/Controller/DeveloperController.php8
-rw-r--r--src/Wallabag/ApiBundle/Controller/EntryRestController.php31
-rw-r--r--src/Wallabag/ApiBundle/Controller/TagRestController.php16
-rw-r--r--src/Wallabag/ApiBundle/Controller/WallabagRestController.php18
-rw-r--r--src/Wallabag/ApiBundle/Entity/Client.php17
-rw-r--r--src/Wallabag/ApiBundle/Resources/config/routing_rest.yml18
-rw-r--r--src/Wallabag/CoreBundle/Command/InstallCommand.php51
-rw-r--r--src/Wallabag/CoreBundle/Controller/ConfigController.php110
-rw-r--r--src/Wallabag/CoreBundle/Controller/EntryController.php14
-rw-r--r--src/Wallabag/CoreBundle/Controller/TagController.php10
-rw-r--r--src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php3
-rw-r--r--src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php17
-rw-r--r--src/Wallabag/CoreBundle/Entity/Config.php30
-rw-r--r--src/Wallabag/CoreBundle/Entity/Entry.php39
-rw-r--r--src/Wallabag/CoreBundle/Event/EntryDeletedEvent.php26
-rw-r--r--src/Wallabag/CoreBundle/Event/EntrySavedEvent.php26
-rw-r--r--src/Wallabag/CoreBundle/Event/Listener/LocaleListener.php (renamed from src/Wallabag/CoreBundle/EventListener/LocaleListener.php)2
-rw-r--r--src/Wallabag/CoreBundle/Event/Listener/UserLocaleListener.php (renamed from src/Wallabag/CoreBundle/EventListener/UserLocaleListener.php)2
-rw-r--r--src/Wallabag/CoreBundle/Event/Subscriber/DownloadImagesSubscriber.php121
-rw-r--r--src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php70
-rw-r--r--src/Wallabag/CoreBundle/Event/Subscriber/TablePrefixSubscriber.php (renamed from src/Wallabag/CoreBundle/Subscriber/TablePrefixSubscriber.php)4
-rw-r--r--src/Wallabag/CoreBundle/Form/Type/ConfigType.php8
-rw-r--r--src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php16
-rw-r--r--src/Wallabag/CoreBundle/Form/Type/NewTagType.php11
-rw-r--r--src/Wallabag/CoreBundle/Helper/ContentProxy.php6
-rw-r--r--src/Wallabag/CoreBundle/Helper/DownloadImages.php233
-rw-r--r--src/Wallabag/CoreBundle/Helper/Redirect.php16
-rw-r--r--src/Wallabag/CoreBundle/Repository/EntryRepository.php14
-rw-r--r--src/Wallabag/CoreBundle/Repository/TagRepository.php18
-rw-r--r--src/Wallabag/CoreBundle/Resources/config/services.yml31
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.da.yml35
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.de.yml37
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.en.yml29
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.es.yml35
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml36
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml35
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.it.yml35
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml113
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml37
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml34
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml35
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml36
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/base.html.twig2
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig52
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig7
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig7
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig48
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig8
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig30
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig3
-rw-r--r--src/Wallabag/ImportBundle/Command/ImportCommand.php30
-rw-r--r--src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php2
-rw-r--r--src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php9
-rw-r--r--src/Wallabag/ImportBundle/Controller/ImportController.php2
-rw-r--r--src/Wallabag/ImportBundle/Controller/PinboardController.php77
-rw-r--r--src/Wallabag/ImportBundle/Import/AbstractImport.php24
-rw-r--r--src/Wallabag/ImportBundle/Import/BrowserImport.php17
-rw-r--r--src/Wallabag/ImportBundle/Import/PinboardImport.php143
-rw-r--r--src/Wallabag/ImportBundle/Import/PocketImport.php9
-rw-r--r--src/Wallabag/ImportBundle/Resources/config/rabbit.yml15
-rw-r--r--src/Wallabag/ImportBundle/Resources/config/redis.yml28
-rw-r--r--src/Wallabag/ImportBundle/Resources/config/services.yml18
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Chrome/index.html.twig2
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Firefox/index.html.twig2
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Import/_information.html.twig (renamed from src/Wallabag/ImportBundle/Resources/views/Import/_workerEnabled.html.twig)7
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Import/index.html.twig2
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Instapaper/index.html.twig2
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Pinboard/index.html.twig45
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Pocket/index.html.twig2
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig2
-rw-r--r--src/Wallabag/ImportBundle/Resources/views/WallabagV1/index.html.twig2
-rw-r--r--src/Wallabag/UserBundle/Entity/User.php26
-rw-r--r--src/Wallabag/UserBundle/Repository/UserRepository.php14
-rw-r--r--src/Wallabag/UserBundle/Resources/config/services.yml2
78 files changed, 2117 insertions, 212 deletions
diff --git a/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php b/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php
index ad083e31..c13a034f 100644
--- a/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php
+++ b/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php
@@ -3,9 +3,8 @@
3namespace Wallabag\AnnotationBundle\Controller; 3namespace Wallabag\AnnotationBundle\Controller;
4 4
5use FOS\RestBundle\Controller\FOSRestController; 5use FOS\RestBundle\Controller\FOSRestController;
6use Nelmio\ApiDocBundle\Annotation\ApiDoc; 6use Symfony\Component\HttpFoundation\JsonResponse;
7use Symfony\Component\HttpFoundation\Request; 7use Symfony\Component\HttpFoundation\Request;
8use Symfony\Component\HttpFoundation\Response;
9use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; 8use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
10use Wallabag\AnnotationBundle\Entity\Annotation; 9use Wallabag\AnnotationBundle\Entity\Annotation;
11use Wallabag\CoreBundle\Entity\Entry; 10use Wallabag\CoreBundle\Entity\Entry;
@@ -15,42 +14,35 @@ class WallabagAnnotationController extends FOSRestController
15 /** 14 /**
16 * Retrieve annotations for an entry. 15 * Retrieve annotations for an entry.
17 * 16 *
18 * @ApiDoc( 17 * @param Entry $entry
19 * requirements={ 18 *
20 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} 19 * @see Wallabag\ApiBundle\Controller\WallabagRestController
21 * }
22 * )
23 * 20 *
24 * @return Response 21 * @return JsonResponse
25 */ 22 */
26 public function getAnnotationsAction(Entry $entry) 23 public function getAnnotationsAction(Entry $entry)
27 { 24 {
28 $annotationRows = $this 25 $annotationRows = $this
29 ->getDoctrine() 26 ->getDoctrine()
30 ->getRepository('WallabagAnnotationBundle:Annotation') 27 ->getRepository('WallabagAnnotationBundle:Annotation')
31 ->findAnnotationsByPageId($entry->getId(), $this->getUser()->getId()); 28 ->findAnnotationsByPageId($entry->getId(), $this->getUser()->getId());
32 $total = count($annotationRows); 29 $total = count($annotationRows);
33 $annotations = ['total' => $total, 'rows' => $annotationRows]; 30 $annotations = ['total' => $total, 'rows' => $annotationRows];
34 31
35 $json = $this->get('serializer')->serialize($annotations, 'json'); 32 $json = $this->get('serializer')->serialize($annotations, 'json');
36 33
37 return $this->renderJsonResponse($json); 34 return (new JsonResponse())->setJson($json);
38 } 35 }
39 36
40 /** 37 /**
41 * Creates a new annotation. 38 * Creates a new annotation.
42 * 39 *
43 * @param Entry $entry 40 * @param Request $request
41 * @param Entry $entry
44 * 42 *
45 * @ApiDoc( 43 * @return JsonResponse
46 * requirements={
47 * {"name"="ranges", "dataType"="array", "requirement"="\w+", "description"="The range array for the annotation"},
48 * {"name"="quote", "dataType"="string", "required"=false, "description"="Optional, quote for the annotation"},
49 * {"name"="text", "dataType"="string", "required"=true, "description"=""},
50 * }
51 * )
52 * 44 *
53 * @return Response 45 * @see Wallabag\ApiBundle\Controller\WallabagRestController
54 */ 46 */
55 public function postAnnotationAction(Request $request, Entry $entry) 47 public function postAnnotationAction(Request $request, Entry $entry)
56 { 48 {
@@ -75,21 +67,20 @@ class WallabagAnnotationController extends FOSRestController
75 67
76 $json = $this->get('serializer')->serialize($annotation, 'json'); 68 $json = $this->get('serializer')->serialize($annotation, 'json');
77 69
78 return $this->renderJsonResponse($json); 70 return (new JsonResponse())->setJson($json);
79 } 71 }
80 72
81 /** 73 /**
82 * Updates an annotation. 74 * Updates an annotation.
83 * 75 *
84 * @ApiDoc( 76 * @see Wallabag\ApiBundle\Controller\WallabagRestController
85 * requirements={
86 * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"}
87 * }
88 * )
89 * 77 *
90 * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation") 78 * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation")
91 * 79 *
92 * @return Response 80 * @param Annotation $annotation
81 * @param Request $request
82 *
83 * @return JsonResponse
93 */ 84 */
94 public function putAnnotationAction(Annotation $annotation, Request $request) 85 public function putAnnotationAction(Annotation $annotation, Request $request)
95 { 86 {
@@ -104,21 +95,19 @@ class WallabagAnnotationController extends FOSRestController
104 95
105 $json = $this->get('serializer')->serialize($annotation, 'json'); 96 $json = $this->get('serializer')->serialize($annotation, 'json');
106 97
107 return $this->renderJsonResponse($json); 98 return (new JsonResponse())->setJson($json);
108 } 99 }
109 100
110 /** 101 /**
111 * Removes an annotation. 102 * Removes an annotation.
112 * 103 *
113 * @ApiDoc( 104 * @see Wallabag\ApiBundle\Controller\WallabagRestController
114 * requirements={
115 * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"}
116 * }
117 * )
118 * 105 *
119 * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation") 106 * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation")
120 * 107 *
121 * @return Response 108 * @param Annotation $annotation
109 *
110 * @return JsonResponse
122 */ 111 */
123 public function deleteAnnotationAction(Annotation $annotation) 112 public function deleteAnnotationAction(Annotation $annotation)
124 { 113 {
@@ -128,19 +117,6 @@ class WallabagAnnotationController extends FOSRestController
128 117
129 $json = $this->get('serializer')->serialize($annotation, 'json'); 118 $json = $this->get('serializer')->serialize($annotation, 'json');
130 119
131 return $this->renderJsonResponse($json); 120 return (new JsonResponse())->setJson($json);
132 }
133
134 /**
135 * Send a JSON Response.
136 * We don't use the Symfony JsonRespone, because it takes an array as parameter instead of a JSON string.
137 *
138 * @param string $json
139 *
140 * @return Response
141 */
142 private function renderJsonResponse($json, $code = 200)
143 {
144 return new Response($json, $code, ['application/json']);
145 } 121 }
146} 122}
diff --git a/src/Wallabag/AnnotationBundle/Entity/Annotation.php b/src/Wallabag/AnnotationBundle/Entity/Annotation.php
index c48d8731..0838f5aa 100644
--- a/src/Wallabag/AnnotationBundle/Entity/Annotation.php
+++ b/src/Wallabag/AnnotationBundle/Entity/Annotation.php
@@ -82,7 +82,7 @@ class Annotation
82 * @Exclude 82 * @Exclude
83 * 83 *
84 * @ORM\ManyToOne(targetEntity="Wallabag\CoreBundle\Entity\Entry", inversedBy="annotations") 84 * @ORM\ManyToOne(targetEntity="Wallabag\CoreBundle\Entity\Entry", inversedBy="annotations")
85 * @ORM\JoinColumn(name="entry_id", referencedColumnName="id") 85 * @ORM\JoinColumn(name="entry_id", referencedColumnName="id", onDelete="cascade")
86 */ 86 */
87 private $entry; 87 private $entry;
88 88
diff --git a/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php b/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php
index 5f7da70e..8d3f07ee 100644
--- a/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php
+++ b/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php
@@ -50,7 +50,8 @@ class AnnotationRepository extends EntityRepository
50 { 50 {
51 return $this->createQueryBuilder('a') 51 return $this->createQueryBuilder('a')
52 ->andWhere('a.id = :annotationId')->setParameter('annotationId', $annotationId) 52 ->andWhere('a.id = :annotationId')->setParameter('annotationId', $annotationId)
53 ->getQuery()->getSingleResult() 53 ->getQuery()
54 ->getSingleResult()
54 ; 55 ;
55 } 56 }
56 57
@@ -67,7 +68,8 @@ class AnnotationRepository extends EntityRepository
67 return $this->createQueryBuilder('a') 68 return $this->createQueryBuilder('a')
68 ->where('a.entry = :entryId')->setParameter('entryId', $entryId) 69 ->where('a.entry = :entryId')->setParameter('entryId', $entryId)
69 ->andwhere('a.user = :userId')->setParameter('userId', $userId) 70 ->andwhere('a.user = :userId')->setParameter('userId', $userId)
70 ->getQuery()->getResult() 71 ->getQuery()
72 ->getResult()
71 ; 73 ;
72 } 74 }
73 75
@@ -106,4 +108,18 @@ class AnnotationRepository extends EntityRepository
106 ->getQuery() 108 ->getQuery()
107 ->getSingleResult(); 109 ->getSingleResult();
108 } 110 }
111
112 /**
113 * Remove all annotations for a user id.
114 * Used when a user want to reset all informations.
115 *
116 * @param int $userId
117 */
118 public function removeAllByUserId($userId)
119 {
120 $this->getEntityManager()
121 ->createQuery('DELETE FROM Wallabag\AnnotationBundle\Entity\Annotation a WHERE a.user = :userId')
122 ->setParameter('userId', $userId)
123 ->execute();
124 }
109} 125}
diff --git a/src/Wallabag/ApiBundle/Controller/AnnotationRestController.php b/src/Wallabag/ApiBundle/Controller/AnnotationRestController.php
new file mode 100644
index 00000000..2dd26c07
--- /dev/null
+++ b/src/Wallabag/ApiBundle/Controller/AnnotationRestController.php
@@ -0,0 +1,111 @@
1<?php
2
3namespace Wallabag\ApiBundle\Controller;
4
5use Nelmio\ApiDocBundle\Annotation\ApiDoc;
6use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
7use Symfony\Component\HttpFoundation\Request;
8use Symfony\Component\HttpFoundation\JsonResponse;
9use Wallabag\CoreBundle\Entity\Entry;
10use Wallabag\AnnotationBundle\Entity\Annotation;
11
12class AnnotationRestController extends WallabagRestController
13{
14 /**
15 * Retrieve annotations for an entry.
16 *
17 * @ApiDoc(
18 * requirements={
19 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
20 * }
21 * )
22 *
23 * @param Entry $entry
24 *
25 * @return JsonResponse
26 */
27 public function getAnnotationsAction(Entry $entry)
28 {
29 $this->validateAuthentication();
30
31 return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:getAnnotations', [
32 'entry' => $entry,
33 ]);
34 }
35
36 /**
37 * Creates a new annotation.
38 *
39 * @ApiDoc(
40 * requirements={
41 * {"name"="ranges", "dataType"="array", "requirement"="\w+", "description"="The range array for the annotation"},
42 * {"name"="quote", "dataType"="string", "required"=false, "description"="Optional, quote for the annotation"},
43 * {"name"="text", "dataType"="string", "required"=true, "description"=""},
44 * }
45 * )
46 *
47 * @param Request $request
48 * @param Entry $entry
49 *
50 * @return JsonResponse
51 */
52 public function postAnnotationAction(Request $request, Entry $entry)
53 {
54 $this->validateAuthentication();
55
56 return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:postAnnotation', [
57 'request' => $request,
58 'entry' => $entry,
59 ]);
60 }
61
62 /**
63 * Updates an annotation.
64 *
65 * @ApiDoc(
66 * requirements={
67 * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"}
68 * }
69 * )
70 *
71 * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation")
72 *
73 * @param Annotation $annotation
74 * @param Request $request
75 *
76 * @return JsonResponse
77 */
78 public function putAnnotationAction(Annotation $annotation, Request $request)
79 {
80 $this->validateAuthentication();
81
82 return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:putAnnotation', [
83 'annotation' => $annotation,
84 'request' => $request,
85 ]);
86 }
87
88 /**
89 * Removes an annotation.
90 *
91 * @ApiDoc(
92 * requirements={
93 * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"}
94 * }
95 * )
96 *
97 * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation")
98 *
99 * @param Annotation $annotation
100 *
101 * @return JsonResponse
102 */
103 public function deleteAnnotationAction(Annotation $annotation)
104 {
105 $this->validateAuthentication();
106
107 return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:deleteAnnotation', [
108 'annotation' => $annotation,
109 ]);
110 }
111}
diff --git a/src/Wallabag/ApiBundle/Controller/DeveloperController.php b/src/Wallabag/ApiBundle/Controller/DeveloperController.php
index 5a36a260..550c0608 100644
--- a/src/Wallabag/ApiBundle/Controller/DeveloperController.php
+++ b/src/Wallabag/ApiBundle/Controller/DeveloperController.php
@@ -19,7 +19,7 @@ class DeveloperController extends Controller
19 */ 19 */
20 public function indexAction() 20 public function indexAction()
21 { 21 {
22 $clients = $this->getDoctrine()->getRepository('WallabagApiBundle:Client')->findAll(); 22 $clients = $this->getDoctrine()->getRepository('WallabagApiBundle:Client')->findByUser($this->getUser()->getId());
23 23
24 return $this->render('@WallabagCore/themes/common/Developer/index.html.twig', [ 24 return $this->render('@WallabagCore/themes/common/Developer/index.html.twig', [
25 'clients' => $clients, 25 'clients' => $clients,
@@ -38,7 +38,7 @@ class DeveloperController extends Controller
38 public function createClientAction(Request $request) 38 public function createClientAction(Request $request)
39 { 39 {
40 $em = $this->getDoctrine()->getManager(); 40 $em = $this->getDoctrine()->getManager();
41 $client = new Client(); 41 $client = new Client($this->getUser());
42 $clientForm = $this->createForm(ClientType::class, $client); 42 $clientForm = $this->createForm(ClientType::class, $client);
43 $clientForm->handleRequest($request); 43 $clientForm->handleRequest($request);
44 44
@@ -75,6 +75,10 @@ class DeveloperController extends Controller
75 */ 75 */
76 public function deleteClientAction(Client $client) 76 public function deleteClientAction(Client $client)
77 { 77 {
78 if (null === $this->getUser() || $client->getUser()->getId() != $this->getUser()->getId()) {
79 throw $this->createAccessDeniedException('You can not access this client.');
80 }
81
78 $em = $this->getDoctrine()->getManager(); 82 $em = $this->getDoctrine()->getManager();
79 $em->remove($client); 83 $em->remove($client);
80 $em->flush(); 84 $em->flush();
diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
index 24fa7b3b..c5bf1df8 100644
--- a/src/Wallabag/ApiBundle/Controller/EntryRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
@@ -10,6 +10,8 @@ use Symfony\Component\HttpFoundation\JsonResponse;
10use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 10use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
11use Wallabag\CoreBundle\Entity\Entry; 11use Wallabag\CoreBundle\Entity\Entry;
12use Wallabag\CoreBundle\Entity\Tag; 12use Wallabag\CoreBundle\Entity\Tag;
13use Wallabag\CoreBundle\Event\EntrySavedEvent;
14use Wallabag\CoreBundle\Event\EntryDeletedEvent;
13 15
14class EntryRestController extends WallabagRestController 16class EntryRestController extends WallabagRestController
15{ 17{
@@ -149,6 +151,28 @@ class EntryRestController extends WallabagRestController
149 } 151 }
150 152
151 /** 153 /**
154 * Retrieve a single entry as a predefined format.
155 *
156 * @ApiDoc(
157 * requirements={
158 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
159 * }
160 * )
161 *
162 * @return Response
163 */
164 public function getEntryExportAction(Entry $entry, Request $request)
165 {
166 $this->validateAuthentication();
167 $this->validateUserAccess($entry->getUser()->getId());
168
169 return $this->get('wallabag_core.helper.entries_export')
170 ->setEntries($entry)
171 ->updateTitle('entry')
172 ->exportAs($request->attributes->get('_format'));
173 }
174
175 /**
152 * Create an entry. 176 * Create an entry.
153 * 177 *
154 * @ApiDoc( 178 * @ApiDoc(
@@ -200,9 +224,11 @@ class EntryRestController extends WallabagRestController
200 224
201 $em = $this->getDoctrine()->getManager(); 225 $em = $this->getDoctrine()->getManager();
202 $em->persist($entry); 226 $em->persist($entry);
203
204 $em->flush(); 227 $em->flush();
205 228
229 // entry saved, dispatch event about it!
230 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
231
206 $json = $this->get('serializer')->serialize($entry, 'json'); 232 $json = $this->get('serializer')->serialize($entry, 'json');
207 233
208 return (new JsonResponse())->setJson($json); 234 return (new JsonResponse())->setJson($json);
@@ -279,6 +305,9 @@ class EntryRestController extends WallabagRestController
279 $em->remove($entry); 305 $em->remove($entry);
280 $em->flush(); 306 $em->flush();
281 307
308 // entry deleted, dispatch event about it!
309 $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry));
310
282 $json = $this->get('serializer')->serialize($entry, 'json'); 311 $json = $this->get('serializer')->serialize($entry, 'json');
283 312
284 return (new JsonResponse())->setJson($json); 313 return (new JsonResponse())->setJson($json);
diff --git a/src/Wallabag/ApiBundle/Controller/TagRestController.php b/src/Wallabag/ApiBundle/Controller/TagRestController.php
index 4e7ddc66..bc6d4e64 100644
--- a/src/Wallabag/ApiBundle/Controller/TagRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/TagRestController.php
@@ -132,22 +132,6 @@ class TagRestController extends WallabagRestController
132 } 132 }
133 133
134 /** 134 /**
135 * Retrieve version number.
136 *
137 * @ApiDoc()
138 *
139 * @return JsonResponse
140 */
141 public function getVersionAction()
142 {
143 $version = $this->container->getParameter('wallabag_core.version');
144
145 $json = $this->get('serializer')->serialize($version, 'json');
146
147 return (new JsonResponse())->setJson($json);
148 }
149
150 /**
151 * Remove orphan tag in case no entries are associated to it. 135 * Remove orphan tag in case no entries are associated to it.
152 * 136 *
153 * @param Tag|array $tags 137 * @param Tag|array $tags
diff --git a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
index e927a890..b1e08ca4 100644
--- a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
@@ -3,11 +3,27 @@
3namespace Wallabag\ApiBundle\Controller; 3namespace Wallabag\ApiBundle\Controller;
4 4
5use FOS\RestBundle\Controller\FOSRestController; 5use FOS\RestBundle\Controller\FOSRestController;
6use Nelmio\ApiDocBundle\Annotation\ApiDoc;
7use Symfony\Component\HttpFoundation\JsonResponse;
6use Symfony\Component\Security\Core\Exception\AccessDeniedException; 8use Symfony\Component\Security\Core\Exception\AccessDeniedException;
7use Wallabag\CoreBundle\Entity\Entry;
8 9
9class WallabagRestController extends FOSRestController 10class WallabagRestController extends FOSRestController
10{ 11{
12 /**
13 * Retrieve version number.
14 *
15 * @ApiDoc()
16 *
17 * @return JsonResponse
18 */
19 public function getVersionAction()
20 {
21 $version = $this->container->getParameter('wallabag_core.version');
22 $json = $this->get('serializer')->serialize($version, 'json');
23
24 return (new JsonResponse())->setJson($json);
25 }
26
11 protected function validateAuthentication() 27 protected function validateAuthentication()
12 { 28 {
13 if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) { 29 if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
diff --git a/src/Wallabag/ApiBundle/Entity/Client.php b/src/Wallabag/ApiBundle/Entity/Client.php
index f7898ac8..427a4c7f 100644
--- a/src/Wallabag/ApiBundle/Entity/Client.php
+++ b/src/Wallabag/ApiBundle/Entity/Client.php
@@ -4,6 +4,7 @@ namespace Wallabag\ApiBundle\Entity;
4 4
5use Doctrine\ORM\Mapping as ORM; 5use Doctrine\ORM\Mapping as ORM;
6use FOS\OAuthServerBundle\Entity\Client as BaseClient; 6use FOS\OAuthServerBundle\Entity\Client as BaseClient;
7use Wallabag\UserBundle\Entity\User;
7 8
8/** 9/**
9 * @ORM\Table("oauth2_clients") 10 * @ORM\Table("oauth2_clients")
@@ -35,9 +36,15 @@ class Client extends BaseClient
35 */ 36 */
36 protected $accessTokens; 37 protected $accessTokens;
37 38
38 public function __construct() 39 /**
40 * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="clients")
41 */
42 private $user;
43
44 public function __construct(User $user)
39 { 45 {
40 parent::__construct(); 46 parent::__construct();
47 $this->user = $user;
41 } 48 }
42 49
43 /** 50 /**
@@ -63,4 +70,12 @@ class Client extends BaseClient
63 70
64 return $this; 71 return $this;
65 } 72 }
73
74 /**
75 * @return User
76 */
77 public function getUser()
78 {
79 return $this->user;
80 }
66} 81}
diff --git a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml
index c1af9e02..57d37f4b 100644
--- a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml
+++ b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml
@@ -1,9 +1,19 @@
1entries: 1entry:
2 type: rest 2 type: rest
3 resource: "WallabagApiBundle:EntryRest" 3 resource: "WallabagApiBundle:EntryRest"
4 name_prefix: api_ 4 name_prefix: api_
5 5
6tags: 6tag:
7 type: rest 7 type: rest
8 resource: "WallabagApiBundle:TagRest" 8 resource: "WallabagApiBundle:TagRest"
9 name_prefix: api_
10
11annotation:
12 type: rest
13 resource: "WallabagApiBundle:AnnotationRest"
14 name_prefix: api_
15
16misc:
17 type: rest
18 resource: "WallabagApiBundle:WallabagRest"
9 name_prefix: api_ 19 name_prefix: api_
diff --git a/src/Wallabag/CoreBundle/Command/InstallCommand.php b/src/Wallabag/CoreBundle/Command/InstallCommand.php
index 2daf2dd8..e95c3a7b 100644
--- a/src/Wallabag/CoreBundle/Command/InstallCommand.php
+++ b/src/Wallabag/CoreBundle/Command/InstallCommand.php
@@ -40,7 +40,7 @@ class InstallCommand extends ContainerAwareCommand
40 { 40 {
41 $this 41 $this
42 ->setName('wallabag:install') 42 ->setName('wallabag:install')
43 ->setDescription('Wallabag installer.') 43 ->setDescription('wallabag installer.')
44 ->addOption( 44 ->addOption(
45 'reset', 45 'reset',
46 null, 46 null,
@@ -55,7 +55,7 @@ class InstallCommand extends ContainerAwareCommand
55 $this->defaultInput = $input; 55 $this->defaultInput = $input;
56 $this->defaultOutput = $output; 56 $this->defaultOutput = $output;
57 57
58 $output->writeln('<info>Installing Wallabag...</info>'); 58 $output->writeln('<info>Installing wallabag...</info>');
59 $output->writeln(''); 59 $output->writeln('');
60 60
61 $this 61 $this
@@ -65,7 +65,7 @@ class InstallCommand extends ContainerAwareCommand
65 ->setupConfig() 65 ->setupConfig()
66 ; 66 ;
67 67
68 $output->writeln('<info>Wallabag has been successfully installed.</info>'); 68 $output->writeln('<info>wallabag has been successfully installed.</info>');
69 $output->writeln('<comment>Just execute `php bin/console server:run --env=prod` for using wallabag: http://localhost:8000</comment>'); 69 $output->writeln('<comment>Just execute `php bin/console server:run --env=prod` for using wallabag: http://localhost:8000</comment>');
70 } 70 }
71 71
@@ -78,7 +78,7 @@ class InstallCommand extends ContainerAwareCommand
78 78
79 // testing if database driver exists 79 // testing if database driver exists
80 $fulfilled = true; 80 $fulfilled = true;
81 $label = '<comment>PDO Driver</comment>'; 81 $label = '<comment>PDO Driver (%s)</comment>';
82 $status = '<info>OK!</info>'; 82 $status = '<info>OK!</info>';
83 $help = ''; 83 $help = '';
84 84
@@ -88,7 +88,7 @@ class InstallCommand extends ContainerAwareCommand
88 $help = 'Database driver "'.$this->getContainer()->getParameter('database_driver').'" is not installed.'; 88 $help = 'Database driver "'.$this->getContainer()->getParameter('database_driver').'" is not installed.';
89 } 89 }
90 90
91 $rows[] = [$label, $status, $help]; 91 $rows[] = [sprintf($label, $this->getContainer()->getParameter('database_driver')), $status, $help];
92 92
93 // testing if connection to the database can be etablished 93 // testing if connection to the database can be etablished
94 $label = '<comment>Database connection</comment>'; 94 $label = '<comment>Database connection</comment>';
@@ -96,7 +96,8 @@ class InstallCommand extends ContainerAwareCommand
96 $help = ''; 96 $help = '';
97 97
98 try { 98 try {
99 $doctrineManager->getConnection()->connect(); 99 $conn = $this->getContainer()->get('doctrine')->getManager()->getConnection();
100 $conn->connect();
100 } catch (\Exception $e) { 101 } catch (\Exception $e) {
101 if (false === strpos($e->getMessage(), 'Unknown database') 102 if (false === strpos($e->getMessage(), 'Unknown database')
102 && false === strpos($e->getMessage(), 'database "'.$this->getContainer()->getParameter('database_name').'" does not exist')) { 103 && false === strpos($e->getMessage(), 'database "'.$this->getContainer()->getParameter('database_name').'" does not exist')) {
@@ -108,12 +109,25 @@ class InstallCommand extends ContainerAwareCommand
108 109
109 $rows[] = [$label, $status, $help]; 110 $rows[] = [$label, $status, $help];
110 111
111 // testing if PostgreSQL > 9.1 112 // check MySQL & PostgreSQL version
112 $label = '<comment>SGBD version</comment>'; 113 $label = '<comment>Database version</comment>';
113 $status = '<info>OK!</info>'; 114 $status = '<info>OK!</info>';
114 $help = ''; 115 $help = '';
115 116
116 if ('postgresql' === $doctrineManager->getConnection()->getSchemaManager()->getDatabasePlatform()->getName()) { 117 // now check if MySQL isn't too old to handle utf8mb4
118 if ($conn->isConnected() && 'mysql' === $conn->getDatabasePlatform()->getName()) {
119 $version = $conn->query('select version()')->fetchColumn();
120 $minimalVersion = '5.5.4';
121
122 if (false === version_compare($version, $minimalVersion, '>')) {
123 $fulfilled = false;
124 $status = '<error>ERROR!</error>';
125 $help = 'Your MySQL version ('.$version.') is too old, consider upgrading ('.$minimalVersion.'+).';
126 }
127 }
128
129 // testing if PostgreSQL > 9.1
130 if ($conn->isConnected() && 'postgresql' === $conn->getDatabasePlatform()->getName()) {
117 // return version should be like "PostgreSQL 9.5.4 on x86_64-apple-darwin15.6.0, compiled by Apple LLVM version 8.0.0 (clang-800.0.38), 64-bit" 131 // return version should be like "PostgreSQL 9.5.4 on x86_64-apple-darwin15.6.0, compiled by Apple LLVM version 8.0.0 (clang-800.0.38), 64-bit"
118 $version = $doctrineManager->getConnection()->query('SELECT version();')->fetchColumn(); 132 $version = $doctrineManager->getConnection()->query('SELECT version();')->fetchColumn();
119 133
@@ -152,7 +166,7 @@ class InstallCommand extends ContainerAwareCommand
152 throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.'); 166 throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.');
153 } 167 }
154 168
155 $this->defaultOutput->writeln('<info>Success! Your system can run Wallabag properly.</info>'); 169 $this->defaultOutput->writeln('<info>Success! Your system can run wallabag properly.</info>');
156 170
157 $this->defaultOutput->writeln(''); 171 $this->defaultOutput->writeln('');
158 172
@@ -299,6 +313,16 @@ class InstallCommand extends ContainerAwareCommand
299 'section' => 'entry', 313 'section' => 'entry',
300 ], 314 ],
301 [ 315 [
316 'name' => 'share_unmark',
317 'value' => '1',
318 'section' => 'entry',
319 ],
320 [
321 'name' => 'unmark_url',
322 'value' => 'https://unmark.it',
323 'section' => 'entry',
324 ],
325 [
302 'name' => 'share_shaarli', 326 'name' => 'share_shaarli',
303 'value' => '1', 327 'value' => '1',
304 'section' => 'entry', 328 'section' => 'entry',
@@ -375,7 +399,7 @@ class InstallCommand extends ContainerAwareCommand
375 ], 399 ],
376 [ 400 [
377 'name' => 'wallabag_url', 401 'name' => 'wallabag_url',
378 'value' => 'http://v2.wallabag.org', 402 'value' => '',
379 'section' => 'misc', 403 'section' => 'misc',
380 ], 404 ],
381 [ 405 [
@@ -403,6 +427,11 @@ class InstallCommand extends ContainerAwareCommand
403 'value' => 'wallabag', 427 'value' => 'wallabag',
404 'section' => 'misc', 428 'section' => 'misc',
405 ], 429 ],
430 [
431 'name' => 'download_images_enabled',
432 'value' => '0',
433 'section' => 'misc',
434 ],
406 ]; 435 ];
407 436
408 foreach ($settings as $setting) { 437 foreach ($settings as $setting) {
diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php
index 46fb9503..52a03070 100644
--- a/src/Wallabag/CoreBundle/Controller/ConfigController.php
+++ b/src/Wallabag/CoreBundle/Controller/ConfigController.php
@@ -7,6 +7,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
7use Symfony\Component\HttpFoundation\JsonResponse; 7use Symfony\Component\HttpFoundation\JsonResponse;
8use Symfony\Component\HttpFoundation\RedirectResponse; 8use Symfony\Component\HttpFoundation\RedirectResponse;
9use Symfony\Component\HttpFoundation\Request; 9use Symfony\Component\HttpFoundation\Request;
10use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
10use Wallabag\CoreBundle\Entity\Config; 11use Wallabag\CoreBundle\Entity\Config;
11use Wallabag\CoreBundle\Entity\TaggingRule; 12use Wallabag\CoreBundle\Entity\TaggingRule;
12use Wallabag\CoreBundle\Form\Type\ConfigType; 13use Wallabag\CoreBundle\Form\Type\ConfigType;
@@ -150,6 +151,10 @@ class ConfigController extends Controller
150 'token' => $config->getRssToken(), 151 'token' => $config->getRssToken(),
151 ], 152 ],
152 'twofactor_auth' => $this->getParameter('twofactor_auth'), 153 'twofactor_auth' => $this->getParameter('twofactor_auth'),
154 'wallabag_url' => $this->get('craue_config')->get('wallabag_url'),
155 'enabled_users' => $this->getDoctrine()
156 ->getRepository('WallabagUserBundle:User')
157 ->getSumEnabledUsers(),
153 ]); 158 ]);
154 } 159 }
155 160
@@ -223,6 +228,78 @@ class ConfigController extends Controller
223 } 228 }
224 229
225 /** 230 /**
231 * Remove all annotations OR tags OR entries for the current user.
232 *
233 * @Route("/reset/{type}", requirements={"id" = "annotations|tags|entries"}, name="config_reset")
234 *
235 * @return RedirectResponse
236 */
237 public function resetAction($type)
238 {
239 switch ($type) {
240 case 'annotations':
241 $this->getDoctrine()
242 ->getRepository('WallabagAnnotationBundle:Annotation')
243 ->removeAllByUserId($this->getUser()->getId());
244 break;
245
246 case 'tags':
247 $this->removeAllTagsByUserId($this->getUser()->getId());
248 break;
249
250 case 'entries':
251 // SQLite doesn't care about cascading remove, so we need to manually remove associated stuf
252 // otherwise they won't be removed ...
253 if ($this->get('doctrine')->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) {
254 $this->getDoctrine()->getRepository('WallabagAnnotationBundle:Annotation')->removeAllByUserId($this->getUser()->getId());
255 }
256
257 // manually remove tags to avoid orphan tag
258 $this->removeAllTagsByUserId($this->getUser()->getId());
259
260 $this->getDoctrine()
261 ->getRepository('WallabagCoreBundle:Entry')
262 ->removeAllByUserId($this->getUser()->getId());
263 }
264
265 $this->get('session')->getFlashBag()->add(
266 'notice',
267 'flashes.config.notice.'.$type.'_reset'
268 );
269
270 return $this->redirect($this->generateUrl('config').'#set3');
271 }
272
273 /**
274 * Remove all tags for a given user and cleanup orphan tags.
275 *
276 * @param int $userId
277 */
278 private function removeAllTagsByUserId($userId)
279 {
280 $tags = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findAllTags($userId);
281
282 if (empty($tags)) {
283 return;
284 }
285
286 $this->getDoctrine()
287 ->getRepository('WallabagCoreBundle:Entry')
288 ->removeTags($userId, $tags);
289
290 // cleanup orphan tags
291 $em = $this->getDoctrine()->getManager();
292
293 foreach ($tags as $tag) {
294 if (count($tag->getEntries()) === 0) {
295 $em->remove($tag);
296 }
297 }
298
299 $em->flush();
300 }
301
302 /**
226 * Validate that a rule can be edited/deleted by the current user. 303 * Validate that a rule can be edited/deleted by the current user.
227 * 304 *
228 * @param TaggingRule $rule 305 * @param TaggingRule $rule
@@ -253,4 +330,37 @@ class ConfigController extends Controller
253 330
254 return $config; 331 return $config;
255 } 332 }
333
334 /**
335 * Delete account for current user.
336 *
337 * @Route("/account/delete", name="delete_account")
338 *
339 * @param Request $request
340 *
341 * @throws AccessDeniedHttpException
342 *
343 * @return \Symfony\Component\HttpFoundation\RedirectResponse
344 */
345 public function deleteAccountAction(Request $request)
346 {
347 $enabledUsers = $this->getDoctrine()
348 ->getRepository('WallabagUserBundle:User')
349 ->getSumEnabledUsers();
350
351 if ($enabledUsers <= 1) {
352 throw new AccessDeniedHttpException();
353 }
354
355 $user = $this->getUser();
356
357 // logout current user
358 $this->get('security.token_storage')->setToken(null);
359 $request->getSession()->invalidate();
360
361 $em = $this->get('fos_user.user_manager');
362 $em->deleteUser($user);
363
364 return $this->redirect($this->generateUrl('fos_user_security_login'));
365 }
256} 366}
diff --git a/src/Wallabag/CoreBundle/Controller/EntryController.php b/src/Wallabag/CoreBundle/Controller/EntryController.php
index 97bb3d12..3f4eb17d 100644
--- a/src/Wallabag/CoreBundle/Controller/EntryController.php
+++ b/src/Wallabag/CoreBundle/Controller/EntryController.php
@@ -13,6 +13,8 @@ use Wallabag\CoreBundle\Form\Type\EntryFilterType;
13use Wallabag\CoreBundle\Form\Type\EditEntryType; 13use Wallabag\CoreBundle\Form\Type\EditEntryType;
14use Wallabag\CoreBundle\Form\Type\NewEntryType; 14use Wallabag\CoreBundle\Form\Type\NewEntryType;
15use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; 15use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
16use Wallabag\CoreBundle\Event\EntrySavedEvent;
17use Wallabag\CoreBundle\Event\EntryDeletedEvent;
16 18
17class EntryController extends Controller 19class EntryController extends Controller
18{ 20{
@@ -81,6 +83,9 @@ class EntryController extends Controller
81 $em->persist($entry); 83 $em->persist($entry);
82 $em->flush(); 84 $em->flush();
83 85
86 // entry saved, dispatch event about it!
87 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
88
84 return $this->redirect($this->generateUrl('homepage')); 89 return $this->redirect($this->generateUrl('homepage'));
85 } 90 }
86 91
@@ -107,6 +112,9 @@ class EntryController extends Controller
107 $em = $this->getDoctrine()->getManager(); 112 $em = $this->getDoctrine()->getManager();
108 $em->persist($entry); 113 $em->persist($entry);
109 $em->flush(); 114 $em->flush();
115
116 // entry saved, dispatch event about it!
117 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
110 } 118 }
111 119
112 return $this->redirect($this->generateUrl('homepage')); 120 return $this->redirect($this->generateUrl('homepage'));
@@ -343,6 +351,9 @@ class EntryController extends Controller
343 $em->persist($entry); 351 $em->persist($entry);
344 $em->flush(); 352 $em->flush();
345 353
354 // entry saved, dispatch event about it!
355 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
356
346 return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()])); 357 return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()]));
347 } 358 }
348 359
@@ -431,6 +442,9 @@ class EntryController extends Controller
431 UrlGeneratorInterface::ABSOLUTE_PATH 442 UrlGeneratorInterface::ABSOLUTE_PATH
432 ); 443 );
433 444
445 // entry deleted, dispatch event about it!
446 $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry));
447
434 $em = $this->getDoctrine()->getManager(); 448 $em = $this->getDoctrine()->getManager();
435 $em->remove($entry); 449 $em->remove($entry);
436 $em->flush(); 450 $em->flush();
diff --git a/src/Wallabag/CoreBundle/Controller/TagController.php b/src/Wallabag/CoreBundle/Controller/TagController.php
index 707f3bbe..a3e70fd0 100644
--- a/src/Wallabag/CoreBundle/Controller/TagController.php
+++ b/src/Wallabag/CoreBundle/Controller/TagController.php
@@ -90,15 +90,15 @@ class TagController extends Controller
90 90
91 $flatTags = []; 91 $flatTags = [];
92 92
93 foreach ($tags as $key => $tag) { 93 foreach ($tags as $tag) {
94 $nbEntries = $this->getDoctrine() 94 $nbEntries = $this->getDoctrine()
95 ->getRepository('WallabagCoreBundle:Entry') 95 ->getRepository('WallabagCoreBundle:Entry')
96 ->countAllEntriesByUserIdAndTagId($this->getUser()->getId(), $tag['id']); 96 ->countAllEntriesByUserIdAndTagId($this->getUser()->getId(), $tag->getId());
97 97
98 $flatTags[] = [ 98 $flatTags[] = [
99 'id' => $tag['id'], 99 'id' => $tag->getId(),
100 'label' => $tag['label'], 100 'label' => $tag->getLabel(),
101 'slug' => $tag['slug'], 101 'slug' => $tag->getSlug(),
102 'nbEntries' => $nbEntries, 102 'nbEntries' => $nbEntries,
103 ]; 103 ];
104 } 104 }
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php
index 921c739f..45358022 100644
--- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php
+++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php
@@ -21,6 +21,7 @@ class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface
21 $adminConfig->setReadingSpeed(1); 21 $adminConfig->setReadingSpeed(1);
22 $adminConfig->setLanguage('en'); 22 $adminConfig->setLanguage('en');
23 $adminConfig->setPocketConsumerKey('xxxxx'); 23 $adminConfig->setPocketConsumerKey('xxxxx');
24 $adminConfig->setActionMarkAsRead(0);
24 25
25 $manager->persist($adminConfig); 26 $manager->persist($adminConfig);
26 27
@@ -32,6 +33,7 @@ class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface
32 $bobConfig->setReadingSpeed(1); 33 $bobConfig->setReadingSpeed(1);
33 $bobConfig->setLanguage('fr'); 34 $bobConfig->setLanguage('fr');
34 $bobConfig->setPocketConsumerKey(null); 35 $bobConfig->setPocketConsumerKey(null);
36 $bobConfig->setActionMarkAsRead(1);
35 37
36 $manager->persist($bobConfig); 38 $manager->persist($bobConfig);
37 39
@@ -43,6 +45,7 @@ class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface
43 $emptyConfig->setReadingSpeed(1); 45 $emptyConfig->setReadingSpeed(1);
44 $emptyConfig->setLanguage('en'); 46 $emptyConfig->setLanguage('en');
45 $emptyConfig->setPocketConsumerKey(null); 47 $emptyConfig->setPocketConsumerKey(null);
48 $emptyConfig->setActionMarkAsRead(0);
46 49
47 $manager->persist($emptyConfig); 50 $manager->persist($emptyConfig);
48 51
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php
index a5e1be65..1f74891a 100644
--- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php
+++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php
@@ -36,6 +36,16 @@ class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface
36 'section' => 'entry', 36 'section' => 'entry',
37 ], 37 ],
38 [ 38 [
39 'name' => 'share_unmark',
40 'value' => '1',
41 'section' => 'entry',
42 ],
43 [
44 'name' => 'unmark_url',
45 'value' => 'https://unmark.it',
46 'section' => 'entry',
47 ],
48 [
39 'name' => 'share_shaarli', 49 'name' => 'share_shaarli',
40 'value' => '1', 50 'value' => '1',
41 'section' => 'entry', 51 'section' => 'entry',
@@ -140,6 +150,11 @@ class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface
140 'value' => 'wallabag', 150 'value' => 'wallabag',
141 'section' => 'misc', 151 'section' => 'misc',
142 ], 152 ],
153 [
154 'name' => 'download_images_enabled',
155 'value' => '0',
156 'section' => 'misc',
157 ],
143 ]; 158 ];
144 159
145 foreach ($settings as $setting) { 160 foreach ($settings as $setting) {
@@ -158,6 +173,6 @@ class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface
158 */ 173 */
159 public function getOrder() 174 public function getOrder()
160 { 175 {
161 return 50; 176 return 29;
162 } 177 }
163} 178}
diff --git a/src/Wallabag/CoreBundle/Entity/Config.php b/src/Wallabag/CoreBundle/Entity/Config.php
index 69393ac9..e04f0a7b 100644
--- a/src/Wallabag/CoreBundle/Entity/Config.php
+++ b/src/Wallabag/CoreBundle/Entity/Config.php
@@ -16,6 +16,9 @@ use Wallabag\UserBundle\Entity\User;
16 */ 16 */
17class Config 17class Config
18{ 18{
19 const REDIRECT_TO_HOMEPAGE = 0;
20 const REDIRECT_TO_CURRENT_PAGE = 1;
21
19 /** 22 /**
20 * @var int 23 * @var int
21 * 24 *
@@ -88,6 +91,13 @@ class Config
88 private $pocketConsumerKey; 91 private $pocketConsumerKey;
89 92
90 /** 93 /**
94 * @var int
95 *
96 * @ORM\Column(name="action_mark_as_read", type="integer", nullable=true)
97 */
98 private $actionMarkAsRead;
99
100 /**
91 * @ORM\OneToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="config") 101 * @ORM\OneToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="config")
92 */ 102 */
93 private $user; 103 private $user;
@@ -310,6 +320,26 @@ class Config
310 } 320 }
311 321
312 /** 322 /**
323 * @return int
324 */
325 public function getActionMarkAsRead()
326 {
327 return $this->actionMarkAsRead;
328 }
329
330 /**
331 * @param int $actionMarkAsRead
332 *
333 * @return Config
334 */
335 public function setActionMarkAsRead($actionMarkAsRead)
336 {
337 $this->actionMarkAsRead = $actionMarkAsRead;
338
339 return $this;
340 }
341
342 /**
313 * @param TaggingRule $rule 343 * @param TaggingRule $rule
314 * 344 *
315 * @return Config 345 * @return Config
diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php
index f2da3f4d..3cf9ac1a 100644
--- a/src/Wallabag/CoreBundle/Entity/Entry.php
+++ b/src/Wallabag/CoreBundle/Entity/Entry.php
@@ -19,7 +19,11 @@ use Wallabag\AnnotationBundle\Entity\Annotation;
19 * 19 *
20 * @XmlRoot("entry") 20 * @XmlRoot("entry")
21 * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\EntryRepository") 21 * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\EntryRepository")
22 * @ORM\Table(name="`entry`") 22 * @ORM\Table(
23 * name="`entry`",
24 * options={"collate"="utf8mb4_unicode_ci", "charset"="utf8mb4"},
25 * indexes={@ORM\Index(name="created_at", columns={"created_at"})}
26 * )
23 * @ORM\HasLifecycleCallbacks() 27 * @ORM\HasLifecycleCallbacks()
24 * @Hateoas\Relation("self", href = "expr('/api/entries/' ~ object.getId())") 28 * @Hateoas\Relation("self", href = "expr('/api/entries/' ~ object.getId())")
25 */ 29 */
@@ -177,6 +181,15 @@ class Entry
177 private $isPublic; 181 private $isPublic;
178 182
179 /** 183 /**
184 * @var string
185 *
186 * @ORM\Column(name="http_status", type="text", nullable=true)
187 *
188 * @Groups({"entries_for_user", "export_all"})
189 */
190 private $httpStatus;
191
192 /**
180 * @Exclude 193 * @Exclude
181 * 194 *
182 * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="entries") 195 * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="entries")
@@ -190,10 +203,10 @@ class Entry
190 * @ORM\JoinTable( 203 * @ORM\JoinTable(
191 * name="entry_tag", 204 * name="entry_tag",
192 * joinColumns={ 205 * joinColumns={
193 * @ORM\JoinColumn(name="entry_id", referencedColumnName="id") 206 * @ORM\JoinColumn(name="entry_id", referencedColumnName="id", onDelete="cascade")
194 * }, 207 * },
195 * inverseJoinColumns={ 208 * inverseJoinColumns={
196 * @ORM\JoinColumn(name="tag_id", referencedColumnName="id") 209 * @ORM\JoinColumn(name="tag_id", referencedColumnName="id", onDelete="cascade")
197 * } 210 * }
198 * ) 211 * )
199 */ 212 */
@@ -665,4 +678,24 @@ class Entry
665 { 678 {
666 $this->uuid = null; 679 $this->uuid = null;
667 } 680 }
681
682 /**
683 * @return int
684 */
685 public function getHttpStatus()
686 {
687 return $this->httpStatus;
688 }
689
690 /**
691 * @param int $httpStatus
692 *
693 * @return Entry
694 */
695 public function setHttpStatus($httpStatus)
696 {
697 $this->httpStatus = $httpStatus;
698
699 return $this;
700 }
668} 701}
diff --git a/src/Wallabag/CoreBundle/Event/EntryDeletedEvent.php b/src/Wallabag/CoreBundle/Event/EntryDeletedEvent.php
new file mode 100644
index 00000000..e9061d04
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Event/EntryDeletedEvent.php
@@ -0,0 +1,26 @@
1<?php
2
3namespace Wallabag\CoreBundle\Event;
4
5use Symfony\Component\EventDispatcher\Event;
6use Wallabag\CoreBundle\Entity\Entry;
7
8/**
9 * This event is fired as soon as an entry is deleted.
10 */
11class EntryDeletedEvent extends Event
12{
13 const NAME = 'entry.deleted';
14
15 protected $entry;
16
17 public function __construct(Entry $entry)
18 {
19 $this->entry = $entry;
20 }
21
22 public function getEntry()
23 {
24 return $this->entry;
25 }
26}
diff --git a/src/Wallabag/CoreBundle/Event/EntrySavedEvent.php b/src/Wallabag/CoreBundle/Event/EntrySavedEvent.php
new file mode 100644
index 00000000..5fdb5221
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Event/EntrySavedEvent.php
@@ -0,0 +1,26 @@
1<?php
2
3namespace Wallabag\CoreBundle\Event;
4
5use Symfony\Component\EventDispatcher\Event;
6use Wallabag\CoreBundle\Entity\Entry;
7
8/**
9 * This event is fired as soon as an entry was saved.
10 */
11class EntrySavedEvent extends Event
12{
13 const NAME = 'entry.saved';
14
15 protected $entry;
16
17 public function __construct(Entry $entry)
18 {
19 $this->entry = $entry;
20 }
21
22 public function getEntry()
23 {
24 return $this->entry;
25 }
26}
diff --git a/src/Wallabag/CoreBundle/EventListener/LocaleListener.php b/src/Wallabag/CoreBundle/Event/Listener/LocaleListener.php
index a1c7e5ab..b435d99e 100644
--- a/src/Wallabag/CoreBundle/EventListener/LocaleListener.php
+++ b/src/Wallabag/CoreBundle/Event/Listener/LocaleListener.php
@@ -1,6 +1,6 @@
1<?php 1<?php
2 2
3namespace Wallabag\CoreBundle\EventListener; 3namespace Wallabag\CoreBundle\Event\Listener;
4 4
5use Symfony\Component\EventDispatcher\EventSubscriberInterface; 5use Symfony\Component\EventDispatcher\EventSubscriberInterface;
6use Symfony\Component\HttpKernel\Event\GetResponseEvent; 6use Symfony\Component\HttpKernel\Event\GetResponseEvent;
diff --git a/src/Wallabag/CoreBundle/EventListener/UserLocaleListener.php b/src/Wallabag/CoreBundle/Event/Listener/UserLocaleListener.php
index 82d1a63a..367cdfb0 100644
--- a/src/Wallabag/CoreBundle/EventListener/UserLocaleListener.php
+++ b/src/Wallabag/CoreBundle/Event/Listener/UserLocaleListener.php
@@ -1,6 +1,6 @@
1<?php 1<?php
2 2
3namespace Wallabag\CoreBundle\EventListener; 3namespace Wallabag\CoreBundle\Event\Listener;
4 4
5use Symfony\Component\HttpFoundation\Session\Session; 5use Symfony\Component\HttpFoundation\Session\Session;
6use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; 6use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
diff --git a/src/Wallabag/CoreBundle/Event/Subscriber/DownloadImagesSubscriber.php b/src/Wallabag/CoreBundle/Event/Subscriber/DownloadImagesSubscriber.php
new file mode 100644
index 00000000..4ebe837b
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Event/Subscriber/DownloadImagesSubscriber.php
@@ -0,0 +1,121 @@
1<?php
2
3namespace Wallabag\CoreBundle\Event\Subscriber;
4
5use Symfony\Component\EventDispatcher\EventSubscriberInterface;
6use Psr\Log\LoggerInterface;
7use Wallabag\CoreBundle\Helper\DownloadImages;
8use Wallabag\CoreBundle\Entity\Entry;
9use Wallabag\CoreBundle\Event\EntrySavedEvent;
10use Wallabag\CoreBundle\Event\EntryDeletedEvent;
11use Doctrine\ORM\EntityManager;
12
13class DownloadImagesSubscriber implements EventSubscriberInterface
14{
15 private $em;
16 private $downloadImages;
17 private $enabled;
18 private $logger;
19
20 public function __construct(EntityManager $em, DownloadImages $downloadImages, $enabled, LoggerInterface $logger)
21 {
22 $this->em = $em;
23 $this->downloadImages = $downloadImages;
24 $this->enabled = $enabled;
25 $this->logger = $logger;
26 }
27
28 public static function getSubscribedEvents()
29 {
30 return [
31 EntrySavedEvent::NAME => 'onEntrySaved',
32 EntryDeletedEvent::NAME => 'onEntryDeleted',
33 ];
34 }
35
36 /**
37 * Download images and updated the data into the entry.
38 *
39 * @param EntrySavedEvent $event
40 */
41 public function onEntrySaved(EntrySavedEvent $event)
42 {
43 if (!$this->enabled) {
44 $this->logger->debug('DownloadImagesSubscriber: disabled.');
45
46 return;
47 }
48
49 $entry = $event->getEntry();
50
51 $html = $this->downloadImages($entry);
52 if (false !== $html) {
53 $this->logger->debug('DownloadImagesSubscriber: updated html.');
54
55 $entry->setContent($html);
56 }
57
58 // update preview picture
59 $previewPicture = $this->downloadPreviewImage($entry);
60 if (false !== $previewPicture) {
61 $this->logger->debug('DownloadImagesSubscriber: update preview picture.');
62
63 $entry->setPreviewPicture($previewPicture);
64 }
65
66 $this->em->persist($entry);
67 $this->em->flush();
68 }
69
70 /**
71 * Remove images related to the entry.
72 *
73 * @param EntryDeletedEvent $event
74 */
75 public function onEntryDeleted(EntryDeletedEvent $event)
76 {
77 if (!$this->enabled) {
78 $this->logger->debug('DownloadImagesSubscriber: disabled.');
79
80 return;
81 }
82
83 $this->downloadImages->removeImages($event->getEntry()->getId());
84 }
85
86 /**
87 * Download all images from the html.
88 *
89 * @todo If we want to add async download, it should be done in that method
90 *
91 * @param Entry $entry
92 *
93 * @return string|false False in case of async
94 */
95 private function downloadImages(Entry $entry)
96 {
97 return $this->downloadImages->processHtml(
98 $entry->getId(),
99 $entry->getContent(),
100 $entry->getUrl()
101 );
102 }
103
104 /**
105 * Download the preview picture.
106 *
107 * @todo If we want to add async download, it should be done in that method
108 *
109 * @param Entry $entry
110 *
111 * @return string|false False in case of async
112 */
113 private function downloadPreviewImage(Entry $entry)
114 {
115 return $this->downloadImages->processSingleImage(
116 $entry->getId(),
117 $entry->getPreviewPicture(),
118 $entry->getUrl()
119 );
120 }
121}
diff --git a/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php b/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php
new file mode 100644
index 00000000..3b4c4cf9
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php
@@ -0,0 +1,70 @@
1<?php
2
3namespace Wallabag\CoreBundle\Event\Subscriber;
4
5use Doctrine\Common\EventSubscriber;
6use Doctrine\ORM\Event\LifecycleEventArgs;
7use Wallabag\CoreBundle\Entity\Entry;
8use Doctrine\Bundle\DoctrineBundle\Registry;
9
10/**
11 * SQLite doesn't care about cascading remove, so we need to manually remove associated stuf for an Entry.
12 * Foreign Key Support can be enabled by running `PRAGMA foreign_keys = ON;` at runtime (AT RUNTIME !).
13 * But it needs a compilation flag that not all SQLite instance has ...
14 *
15 * @see https://www.sqlite.org/foreignkeys.html#fk_enable
16 */
17class SQLiteCascadeDeleteSubscriber implements EventSubscriber
18{
19 private $doctrine;
20
21 /**
22 * @param \Doctrine\Bundle\DoctrineBundle\Registry $doctrine
23 */
24 public function __construct(Registry $doctrine)
25 {
26 $this->doctrine = $doctrine;
27 }
28
29 /**
30 * @return array
31 */
32 public function getSubscribedEvents()
33 {
34 return [
35 'preRemove',
36 ];
37 }
38
39 /**
40 * We removed everything related to the upcoming removed entry because SQLite can't handle it on it own.
41 * We do it in the preRemove, because we can't retrieve tags in the postRemove (because the entry id is gone).
42 *
43 * @param LifecycleEventArgs $args
44 */
45 public function preRemove(LifecycleEventArgs $args)
46 {
47 $entity = $args->getEntity();
48
49 if (!$this->doctrine->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver ||
50 !$entity instanceof Entry) {
51 return;
52 }
53
54 $em = $this->doctrine->getManager();
55
56 if (null !== $entity->getTags()) {
57 foreach ($entity->getTags() as $tag) {
58 $entity->removeTag($tag);
59 }
60 }
61
62 if (null !== $entity->getAnnotations()) {
63 foreach ($entity->getAnnotations() as $annotation) {
64 $em->remove($annotation);
65 }
66 }
67
68 $em->flush();
69 }
70}
diff --git a/src/Wallabag/CoreBundle/Subscriber/TablePrefixSubscriber.php b/src/Wallabag/CoreBundle/Event/Subscriber/TablePrefixSubscriber.php
index 0379ad6a..711c3bf8 100644
--- a/src/Wallabag/CoreBundle/Subscriber/TablePrefixSubscriber.php
+++ b/src/Wallabag/CoreBundle/Event/Subscriber/TablePrefixSubscriber.php
@@ -1,6 +1,6 @@
1<?php 1<?php
2 2
3namespace Wallabag\CoreBundle\Subscriber; 3namespace Wallabag\CoreBundle\Event\Subscriber;
4 4
5use Doctrine\Common\EventSubscriber; 5use Doctrine\Common\EventSubscriber;
6use Doctrine\ORM\Event\LoadClassMetadataEventArgs; 6use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
@@ -39,7 +39,7 @@ class TablePrefixSubscriber implements EventSubscriber
39 return; 39 return;
40 } 40 }
41 41
42 $classMetadata->setTableName($this->prefix.$classMetadata->getTableName()); 42 $classMetadata->setPrimaryTable(['name' => $this->prefix.$classMetadata->getTableName()]);
43 43
44 foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) { 44 foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) {
45 if ($mapping['type'] === ClassMetadataInfo::MANY_TO_MANY && isset($classMetadata->associationMappings[$fieldName]['joinTable']['name'])) { 45 if ($mapping['type'] === ClassMetadataInfo::MANY_TO_MANY && isset($classMetadata->associationMappings[$fieldName]['joinTable']['name'])) {
diff --git a/src/Wallabag/CoreBundle/Form/Type/ConfigType.php b/src/Wallabag/CoreBundle/Form/Type/ConfigType.php
index 0bac2874..7e3b9dd4 100644
--- a/src/Wallabag/CoreBundle/Form/Type/ConfigType.php
+++ b/src/Wallabag/CoreBundle/Form/Type/ConfigType.php
@@ -7,6 +7,7 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
7use Symfony\Component\Form\Extension\Core\Type\SubmitType; 7use Symfony\Component\Form\Extension\Core\Type\SubmitType;
8use Symfony\Component\Form\FormBuilderInterface; 8use Symfony\Component\Form\FormBuilderInterface;
9use Symfony\Component\OptionsResolver\OptionsResolver; 9use Symfony\Component\OptionsResolver\OptionsResolver;
10use Wallabag\CoreBundle\Entity\Config;
10 11
11class ConfigType extends AbstractType 12class ConfigType extends AbstractType
12{ 13{
@@ -48,6 +49,13 @@ class ConfigType extends AbstractType
48 'config.form_settings.reading_speed.400_word' => '2', 49 'config.form_settings.reading_speed.400_word' => '2',
49 ], 50 ],
50 ]) 51 ])
52 ->add('action_mark_as_read', ChoiceType::class, [
53 'label' => 'config.form_settings.action_mark_as_read.label',
54 'choices' => [
55 'config.form_settings.action_mark_as_read.redirect_homepage' => Config::REDIRECT_TO_HOMEPAGE,
56 'config.form_settings.action_mark_as_read.redirect_current_page' => Config::REDIRECT_TO_CURRENT_PAGE,
57 ],
58 ])
51 ->add('language', ChoiceType::class, [ 59 ->add('language', ChoiceType::class, [
52 'choices' => array_flip($this->languages), 60 'choices' => array_flip($this->languages),
53 'label' => 'config.form_settings.language_label', 61 'label' => 'config.form_settings.language_label',
diff --git a/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php b/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php
index a3e36fdd..7b02f85c 100644
--- a/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php
+++ b/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php
@@ -11,6 +11,7 @@ use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\CheckboxFilterType;
11use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\ChoiceFilterType; 11use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\ChoiceFilterType;
12use Symfony\Component\Form\AbstractType; 12use Symfony\Component\Form\AbstractType;
13use Symfony\Component\Form\FormBuilderInterface; 13use Symfony\Component\Form\FormBuilderInterface;
14use Symfony\Component\HttpFoundation\Response;
14use Symfony\Component\OptionsResolver\OptionsResolver; 15use Symfony\Component\OptionsResolver\OptionsResolver;
15use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; 16use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
16 17
@@ -95,6 +96,21 @@ class EntryFilterType extends AbstractType
95 }, 96 },
96 'label' => 'entry.filters.domain_label', 97 'label' => 'entry.filters.domain_label',
97 ]) 98 ])
99 ->add('httpStatus', TextFilterType::class, [
100 'apply_filter' => function (QueryInterface $filterQuery, $field, $values) {
101 $value = $values['value'];
102 if (false === array_key_exists($value, Response::$statusTexts)) {
103 return;
104 }
105
106 $paramName = sprintf('%s', str_replace('.', '_', $field));
107 $expression = $filterQuery->getExpr()->eq($field, ':'.$paramName);
108 $parameters = array($paramName => $value);
109
110 return $filterQuery->createCondition($expression, $parameters);
111 },
112 'label' => 'entry.filters.http_status_label',
113 ])
98 ->add('isArchived', CheckboxFilterType::class, [ 114 ->add('isArchived', CheckboxFilterType::class, [
99 'label' => 'entry.filters.archived_label', 115 'label' => 'entry.filters.archived_label',
100 ]) 116 ])
diff --git a/src/Wallabag/CoreBundle/Form/Type/NewTagType.php b/src/Wallabag/CoreBundle/Form/Type/NewTagType.php
index 3db4105f..e830ade4 100644
--- a/src/Wallabag/CoreBundle/Form/Type/NewTagType.php
+++ b/src/Wallabag/CoreBundle/Form/Type/NewTagType.php
@@ -3,6 +3,7 @@
3namespace Wallabag\CoreBundle\Form\Type; 3namespace Wallabag\CoreBundle\Form\Type;
4 4
5use Symfony\Component\Form\AbstractType; 5use Symfony\Component\Form\AbstractType;
6use Symfony\Component\Form\Extension\Core\Type\SubmitType;
6use Symfony\Component\Form\Extension\Core\Type\TextType; 7use Symfony\Component\Form\Extension\Core\Type\TextType;
7use Symfony\Component\Form\FormBuilderInterface; 8use Symfony\Component\Form\FormBuilderInterface;
8use Symfony\Component\OptionsResolver\OptionsResolver; 9use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -12,7 +13,15 @@ class NewTagType extends AbstractType
12 public function buildForm(FormBuilderInterface $builder, array $options) 13 public function buildForm(FormBuilderInterface $builder, array $options)
13 { 14 {
14 $builder 15 $builder
15 ->add('label', TextType::class, ['required' => true]) 16 ->add('label', TextType::class, [
17 'required' => true,
18 'attr' => [
19 'placeholder' => 'tag.new.placeholder',
20 ],
21 ])
22 ->add('add', SubmitType::class, [
23 'label' => 'tag.new.add',
24 ])
16 ; 25 ;
17 } 26 }
18 27
diff --git a/src/Wallabag/CoreBundle/Helper/ContentProxy.php b/src/Wallabag/CoreBundle/Helper/ContentProxy.php
index bbd5db5d..fd059325 100644
--- a/src/Wallabag/CoreBundle/Helper/ContentProxy.php
+++ b/src/Wallabag/CoreBundle/Helper/ContentProxy.php
@@ -3,7 +3,7 @@
3namespace Wallabag\CoreBundle\Helper; 3namespace Wallabag\CoreBundle\Helper;
4 4
5use Graby\Graby; 5use Graby\Graby;
6use Psr\Log\LoggerInterface as Logger; 6use Psr\Log\LoggerInterface;
7use Wallabag\CoreBundle\Entity\Entry; 7use Wallabag\CoreBundle\Entity\Entry;
8use Wallabag\CoreBundle\Entity\Tag; 8use Wallabag\CoreBundle\Entity\Tag;
9use Wallabag\CoreBundle\Tools\Utils; 9use Wallabag\CoreBundle\Tools\Utils;
@@ -22,7 +22,7 @@ class ContentProxy
22 protected $tagRepository; 22 protected $tagRepository;
23 protected $mimeGuesser; 23 protected $mimeGuesser;
24 24
25 public function __construct(Graby $graby, RuleBasedTagger $tagger, TagRepository $tagRepository, Logger $logger) 25 public function __construct(Graby $graby, RuleBasedTagger $tagger, TagRepository $tagRepository, LoggerInterface $logger)
26 { 26 {
27 $this->graby = $graby; 27 $this->graby = $graby;
28 $this->tagger = $tagger; 28 $this->tagger = $tagger;
@@ -69,6 +69,8 @@ class ContentProxy
69 $entry->setUrl($content['url'] ?: $url); 69 $entry->setUrl($content['url'] ?: $url);
70 $entry->setTitle($title); 70 $entry->setTitle($title);
71 $entry->setContent($html); 71 $entry->setContent($html);
72 $entry->setHttpStatus(isset($content['status']) ? $content['status'] : '');
73
72 $entry->setLanguage($content['language']); 74 $entry->setLanguage($content['language']);
73 $entry->setMimetype($content['content_type']); 75 $entry->setMimetype($content['content_type']);
74 $entry->setReadingTime(Utils::getReadingTime($html)); 76 $entry->setReadingTime(Utils::getReadingTime($html));
diff --git a/src/Wallabag/CoreBundle/Helper/DownloadImages.php b/src/Wallabag/CoreBundle/Helper/DownloadImages.php
new file mode 100644
index 00000000..264bc6a3
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Helper/DownloadImages.php
@@ -0,0 +1,233 @@
1<?php
2
3namespace Wallabag\CoreBundle\Helper;
4
5use Psr\Log\LoggerInterface;
6use Symfony\Component\DomCrawler\Crawler;
7use GuzzleHttp\Client;
8use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeExtensionGuesser;
9use Symfony\Component\Finder\Finder;
10
11class DownloadImages
12{
13 const REGENERATE_PICTURES_QUALITY = 80;
14
15 private $client;
16 private $baseFolder;
17 private $logger;
18 private $mimeGuesser;
19 private $wallabagUrl;
20
21 public function __construct(Client $client, $baseFolder, $wallabagUrl, LoggerInterface $logger)
22 {
23 $this->client = $client;
24 $this->baseFolder = $baseFolder;
25 $this->wallabagUrl = rtrim($wallabagUrl, '/');
26 $this->logger = $logger;
27 $this->mimeGuesser = new MimeTypeExtensionGuesser();
28
29 $this->setFolder();
30 }
31
32 /**
33 * Setup base folder where all images are going to be saved.
34 */
35 private function setFolder()
36 {
37 // if folder doesn't exist, attempt to create one and store the folder name in property $folder
38 if (!file_exists($this->baseFolder)) {
39 mkdir($this->baseFolder, 0777, true);
40 }
41 }
42
43 /**
44 * Process the html and extract image from it, save them to local and return the updated html.
45 *
46 * @param int $entryId ID of the entry
47 * @param string $html
48 * @param string $url Used as a base path for relative image and folder
49 *
50 * @return string
51 */
52 public function processHtml($entryId, $html, $url)
53 {
54 $crawler = new Crawler($html);
55 $result = $crawler
56 ->filterXpath('//img')
57 ->extract(array('src'));
58
59 $relativePath = $this->getRelativePath($entryId);
60
61 // download and save the image to the folder
62 foreach ($result as $image) {
63 $imagePath = $this->processSingleImage($entryId, $image, $url, $relativePath);
64
65 if (false === $imagePath) {
66 continue;
67 }
68
69 $html = str_replace($image, $imagePath, $html);
70 }
71
72 return $html;
73 }
74
75 /**
76 * Process a single image:
77 * - retrieve it
78 * - re-saved it (for security reason)
79 * - return the new local path.
80 *
81 * @param int $entryId ID of the entry
82 * @param string $imagePath Path to the image to retrieve
83 * @param string $url Url from where the image were found
84 * @param string $relativePath Relative local path to saved the image
85 *
86 * @return string Relative url to access the image from the web
87 */
88 public function processSingleImage($entryId, $imagePath, $url, $relativePath = null)
89 {
90 if (null === $relativePath) {
91 $relativePath = $this->getRelativePath($entryId);
92 }
93
94 $this->logger->debug('DownloadImages: working on image: '.$imagePath);
95
96 $folderPath = $this->baseFolder.'/'.$relativePath;
97
98 // build image path
99 $absolutePath = $this->getAbsoluteLink($url, $imagePath);
100 if (false === $absolutePath) {
101 $this->logger->error('DownloadImages: Can not determine the absolute path for that image, skipping.');
102
103 return false;
104 }
105
106 try {
107 $res = $this->client->get($absolutePath);
108 } catch (\Exception $e) {
109 $this->logger->error('DownloadImages: Can not retrieve image, skipping.', ['exception' => $e]);
110
111 return false;
112 }
113
114 $ext = $this->mimeGuesser->guess($res->getHeader('content-type'));
115 $this->logger->debug('DownloadImages: Checking extension', ['ext' => $ext, 'header' => $res->getHeader('content-type')]);
116 if (!in_array($ext, ['jpeg', 'jpg', 'gif', 'png'], true)) {
117 $this->logger->error('DownloadImages: Processed image with not allowed extension. Skipping '.$imagePath);
118
119 return false;
120 }
121 $hashImage = hash('crc32', $absolutePath);
122 $localPath = $folderPath.'/'.$hashImage.'.'.$ext;
123
124 try {
125 $im = imagecreatefromstring($res->getBody());
126 } catch (\Exception $e) {
127 $im = false;
128 }
129
130 if (false === $im) {
131 $this->logger->error('DownloadImages: Error while regenerating image', ['path' => $localPath]);
132
133 return false;
134 }
135
136 switch ($ext) {
137 case 'gif':
138 imagegif($im, $localPath);
139 $this->logger->debug('DownloadImages: Re-creating gif');
140 break;
141 case 'jpeg':
142 case 'jpg':
143 imagejpeg($im, $localPath, self::REGENERATE_PICTURES_QUALITY);
144 $this->logger->debug('DownloadImages: Re-creating jpg');
145 break;
146 case 'png':
147 imagepng($im, $localPath, ceil(self::REGENERATE_PICTURES_QUALITY / 100 * 9));
148 $this->logger->debug('DownloadImages: Re-creating png');
149 }
150
151 imagedestroy($im);
152
153 return $this->wallabagUrl.'/assets/images/'.$relativePath.'/'.$hashImage.'.'.$ext;
154 }
155
156 /**
157 * Remove all images for the given entry id.
158 *
159 * @param int $entryId ID of the entry
160 */
161 public function removeImages($entryId)
162 {
163 $relativePath = $this->getRelativePath($entryId);
164 $folderPath = $this->baseFolder.'/'.$relativePath;
165
166 $finder = new Finder();
167 $finder
168 ->files()
169 ->ignoreDotFiles(true)
170 ->in($folderPath);
171
172 foreach ($finder as $file) {
173 @unlink($file->getRealPath());
174 }
175
176 @rmdir($folderPath);
177 }
178
179 /**
180 * Generate the folder where we are going to save images based on the entry url.
181 *
182 * @param int $entryId ID of the entry
183 *
184 * @return string
185 */
186 private function getRelativePath($entryId)
187 {
188 $hashId = hash('crc32', $entryId);
189 $relativePath = $hashId[0].'/'.$hashId[1].'/'.$hashId;
190 $folderPath = $this->baseFolder.'/'.$relativePath;
191
192 if (!file_exists($folderPath)) {
193 mkdir($folderPath, 0777, true);
194 }
195
196 $this->logger->debug('DownloadImages: Folder used for that Entry id', ['folder' => $folderPath, 'entryId' => $entryId]);
197
198 return $relativePath;
199 }
200
201 /**
202 * Make an $url absolute based on the $base.
203 *
204 * @see Graby->makeAbsoluteStr
205 *
206 * @param string $base Base url
207 * @param string $url Url to make it absolute
208 *
209 * @return false|string
210 */
211 private function getAbsoluteLink($base, $url)
212 {
213 if (preg_match('!^https?://!i', $url)) {
214 // already absolute
215 return $url;
216 }
217
218 $base = new \SimplePie_IRI($base);
219
220 // remove '//' in URL path (causes URLs not to resolve properly)
221 if (isset($base->ipath)) {
222 $base->ipath = preg_replace('!//+!', '/', $base->ipath);
223 }
224
225 if ($absolute = \SimplePie_IRI::absolutize($base, $url)) {
226 return $absolute->get_uri();
227 }
228
229 $this->logger->error('DownloadImages: Can not make an absolute link', ['base' => $base, 'url' => $url]);
230
231 return false;
232 }
233}
diff --git a/src/Wallabag/CoreBundle/Helper/Redirect.php b/src/Wallabag/CoreBundle/Helper/Redirect.php
index c14c79d1..f78b7fe0 100644
--- a/src/Wallabag/CoreBundle/Helper/Redirect.php
+++ b/src/Wallabag/CoreBundle/Helper/Redirect.php
@@ -3,6 +3,8 @@
3namespace Wallabag\CoreBundle\Helper; 3namespace Wallabag\CoreBundle\Helper;
4 4
5use Symfony\Component\Routing\Router; 5use Symfony\Component\Routing\Router;
6use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
7use Wallabag\CoreBundle\Entity\Config;
6 8
7/** 9/**
8 * Manage redirections to avoid redirecting to empty routes. 10 * Manage redirections to avoid redirecting to empty routes.
@@ -10,10 +12,12 @@ use Symfony\Component\Routing\Router;
10class Redirect 12class Redirect
11{ 13{
12 private $router; 14 private $router;
15 private $tokenStorage;
13 16
14 public function __construct(Router $router) 17 public function __construct(Router $router, TokenStorageInterface $tokenStorage)
15 { 18 {
16 $this->router = $router; 19 $this->router = $router;
20 $this->tokenStorage = $tokenStorage;
17 } 21 }
18 22
19 /** 23 /**
@@ -24,6 +28,16 @@ class Redirect
24 */ 28 */
25 public function to($url, $fallback = '') 29 public function to($url, $fallback = '')
26 { 30 {
31 $user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;
32
33 if (null === $user || !is_object($user)) {
34 return $url;
35 }
36
37 if (Config::REDIRECT_TO_HOMEPAGE === $user->getConfig()->getActionMarkAsRead()) {
38 return $this->router->generate('homepage');
39 }
40
27 if (null !== $url) { 41 if (null !== $url) {
28 return $url; 42 return $url;
29 } 43 }
diff --git a/src/Wallabag/CoreBundle/Repository/EntryRepository.php b/src/Wallabag/CoreBundle/Repository/EntryRepository.php
index 4f03ae0f..61be5220 100644
--- a/src/Wallabag/CoreBundle/Repository/EntryRepository.php
+++ b/src/Wallabag/CoreBundle/Repository/EntryRepository.php
@@ -329,4 +329,18 @@ class EntryRepository extends EntityRepository
329 329
330 return $qb->getQuery()->getSingleScalarResult(); 330 return $qb->getQuery()->getSingleScalarResult();
331 } 331 }
332
333 /**
334 * Remove all entries for a user id.
335 * Used when a user want to reset all informations.
336 *
337 * @param int $userId
338 */
339 public function removeAllByUserId($userId)
340 {
341 $this->getEntityManager()
342 ->createQuery('DELETE FROM Wallabag\CoreBundle\Entity\Entry e WHERE e.user = :userId')
343 ->setParameter('userId', $userId)
344 ->execute();
345 }
332} 346}
diff --git a/src/Wallabag/CoreBundle/Repository/TagRepository.php b/src/Wallabag/CoreBundle/Repository/TagRepository.php
index e76878d4..81445989 100644
--- a/src/Wallabag/CoreBundle/Repository/TagRepository.php
+++ b/src/Wallabag/CoreBundle/Repository/TagRepository.php
@@ -34,6 +34,9 @@ class TagRepository extends EntityRepository
34 34
35 /** 35 /**
36 * Find all tags per user. 36 * Find all tags per user.
37 * Instead of just left joined on the Entry table, we select only id and group by id to avoid tag multiplication in results.
38 * Once we have all tags id, we can safely request them one by one.
39 * This'll still be fastest than the previous query.
37 * 40 *
38 * @param int $userId 41 * @param int $userId
39 * 42 *
@@ -41,15 +44,20 @@ class TagRepository extends EntityRepository
41 */ 44 */
42 public function findAllTags($userId) 45 public function findAllTags($userId)
43 { 46 {
44 return $this->createQueryBuilder('t') 47 $ids = $this->createQueryBuilder('t')
45 ->select('t.slug', 't.label', 't.id') 48 ->select('t.id')
46 ->leftJoin('t.entries', 'e') 49 ->leftJoin('t.entries', 'e')
47 ->where('e.user = :userId')->setParameter('userId', $userId) 50 ->where('e.user = :userId')->setParameter('userId', $userId)
48 ->groupBy('t.slug') 51 ->groupBy('t.id')
49 ->addGroupBy('t.label')
50 ->addGroupBy('t.id')
51 ->getQuery() 52 ->getQuery()
52 ->getArrayResult(); 53 ->getArrayResult();
54
55 $tags = [];
56 foreach ($ids as $id) {
57 $tags[] = $this->find($id);
58 }
59
60 return $tags;
53 } 61 }
54 62
55 /** 63 /**
diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml
index ed66d2be..0036f45e 100644
--- a/src/Wallabag/CoreBundle/Resources/config/services.yml
+++ b/src/Wallabag/CoreBundle/Resources/config/services.yml
@@ -30,7 +30,7 @@ services:
30 - "@doctrine" 30 - "@doctrine"
31 31
32 wallabag_core.subscriber.table_prefix: 32 wallabag_core.subscriber.table_prefix:
33 class: Wallabag\CoreBundle\Subscriber\TablePrefixSubscriber 33 class: Wallabag\CoreBundle\Event\Subscriber\TablePrefixSubscriber
34 arguments: 34 arguments:
35 - "%database_table_prefix%" 35 - "%database_table_prefix%"
36 tags: 36 tags:
@@ -94,6 +94,7 @@ services:
94 class: Wallabag\CoreBundle\Helper\Redirect 94 class: Wallabag\CoreBundle\Helper\Redirect
95 arguments: 95 arguments:
96 - "@router" 96 - "@router"
97 - "@security.token_storage"
97 98
98 wallabag_core.helper.prepare_pager_for_entries: 99 wallabag_core.helper.prepare_pager_for_entries:
99 class: Wallabag\CoreBundle\Helper\PreparePagerForEntries 100 class: Wallabag\CoreBundle\Helper\PreparePagerForEntries
@@ -115,3 +116,31 @@ services:
115 arguments: 116 arguments:
116 - '@twig' 117 - '@twig'
117 - '%kernel.debug%' 118 - '%kernel.debug%'
119
120 wallabag_core.subscriber.sqlite_cascade_delete:
121 class: Wallabag\CoreBundle\Event\Subscriber\SQLiteCascadeDeleteSubscriber
122 arguments:
123 - "@doctrine"
124 tags:
125 - { name: doctrine.event_subscriber }
126
127 wallabag_core.subscriber.download_images:
128 class: Wallabag\CoreBundle\Event\Subscriber\DownloadImagesSubscriber
129 arguments:
130 - "@doctrine.orm.default_entity_manager"
131 - "@wallabag_core.entry.download_images"
132 - '@=service(''craue_config'').get(''download_images_enabled'')'
133 - "@logger"
134 tags:
135 - { name: kernel.event_subscriber }
136
137 wallabag_core.entry.download_images:
138 class: Wallabag\CoreBundle\Helper\DownloadImages
139 arguments:
140 - "@wallabag_core.entry.download_images.client"
141 - "%kernel.root_dir%/../web/assets/images"
142 - '@=service(''craue_config'').get(''wallabag_url'')'
143 - "@logger"
144
145 wallabag_core.entry.download_images.client:
146 class: GuzzleHttp\Client
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
index a1bee173..b456fff0 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
@@ -70,7 +70,12 @@ config:
70 # 200_word: 'I read ~200 words per minute' 70 # 200_word: 'I read ~200 words per minute'
71 # 300_word: 'I read ~300 words per minute' 71 # 300_word: 'I read ~300 words per minute'
72 # 400_word: 'I read ~400 words per minute' 72 # 400_word: 'I read ~400 words per minute'
73 action_mark_as_read:
74 # label: 'Where do you to be redirected after mark an article as read?'
75 # redirect_homepage: 'To the homepage'
76 # redirect_current_page: 'To the current page'
73 pocket_consumer_key_label: Brugers nøgle til Pocket for at importere materialer 77 pocket_consumer_key_label: Brugers nøgle til Pocket for at importere materialer
78 # android_configuration: Configure your Android application
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'Emailadresse' 99 email_label: 'Emailadresse'
95 # twoFactorAuthentication_label: 'Two factor authentication' 100 # twoFactorAuthentication_label: 'Two factor authentication'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 # title: Delete my account (a.k.a danger zone)
104 # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
105 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
106 # button: Delete my account
107 reset:
108 # title: Reset area (a.k.a danger zone)
109 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
110 # annotations: Remove ALL annotations
111 # tags: Remove ALL tags
112 # entries: Remove ALL entries
113 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Gammel adgangskode' 116 old_password_label: 'Gammel adgangskode'
@@ -168,6 +185,7 @@ entry:
168 preview_picture_label: 'Har et vist billede' 185 preview_picture_label: 'Har et vist billede'
169 preview_picture_help: 'Forhåndsvis billede' 186 preview_picture_help: 'Forhåndsvis billede'
170 language_label: 'Sprog' 187 language_label: 'Sprog'
188 # http_status_label: 'HTTP status'
171 reading_time: 189 reading_time:
172 label: 'Læsetid i minutter' 190 label: 'Læsetid i minutter'
173 from: 'fra' 191 from: 'fra'
@@ -329,6 +347,9 @@ tag:
329 list: 347 list:
330 # number_on_the_page: '{0} There is no tag.|{1} There is one tag.|]1,Inf[ There are %count% tags.' 348 # number_on_the_page: '{0} There is no tag.|{1} There is one tag.|]1,Inf[ There are %count% tags.'
331 # see_untagged_entries: 'See untagged entries' 349 # see_untagged_entries: 'See untagged entries'
350 new:
351 # add: 'Add'
352 # placeholder: 'You can add several tags, separated by a comma.'
332 353
333import: 354import:
334 # page_title: 'Import' 355 # page_title: 'Import'
@@ -362,6 +383,7 @@ import:
362 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 383 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
363 worker: 384 worker:
364 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:" 385 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
386 # download_images_warning: "You enabled downloading images for your articles. Combined with classic import it can take ages to proceed (or maybe failed). We <strong>strongly recommend</strong> to enable asynchronous import to avoid errors."
365 # firefox: 387 # firefox:
366 # page_title: 'Import > Firefox' 388 # page_title: 'Import > Firefox'
367 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." 389 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file."
@@ -374,6 +396,10 @@ import:
374 # page_title: 'Import > Instapaper' 396 # page_title: 'Import > Instapaper'
375 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").' 397 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").'
376 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.' 398 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
399 pinboard:
400 # page_title: "Import > Pinboard"
401 # description: 'This importer will import all your Instapaper articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
402 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 403
378developer: 404developer:
379 # page_title: 'Developer' 405 # page_title: 'Developer'
@@ -465,8 +491,10 @@ flashes:
465 rss_updated: 'RSS-oplysninger opdateret' 491 rss_updated: 'RSS-oplysninger opdateret'
466 # tagging_rules_updated: 'Tagging rules updated' 492 # tagging_rules_updated: 'Tagging rules updated'
467 # tagging_rules_deleted: 'Tagging rule deleted' 493 # tagging_rules_deleted: 'Tagging rule deleted'
468 # user_added: 'User "%username%" added'
469 # rss_token_updated: 'RSS token updated' 494 # rss_token_updated: 'RSS token updated'
495 # annotations_reset: Annotations reset
496 # tags_reset: Tags reset
497 # entries_reset: Entries reset
470 entry: 498 entry:
471 notice: 499 notice:
472 # entry_already_saved: 'Entry already saved on %date%' 500 # entry_already_saved: 'Entry already saved on %date%'
@@ -496,3 +524,8 @@ flashes:
496 notice: 524 notice:
497 # client_created: 'New client created.' 525 # client_created: 'New client created.'
498 # client_deleted: 'Client deleted' 526 # client_deleted: 'Client deleted'
527 user:
528 notice:
529 # added: 'User "%username%" added'
530 # updated: 'User "%username%" updated'
531 # deleted: 'User "%username%" deleted'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
index c9625d06..08ed1a11 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
@@ -70,7 +70,12 @@ config:
70 200_word: 'Ich lese ~200 Wörter pro Minute' 70 200_word: 'Ich lese ~200 Wörter pro Minute'
71 300_word: 'Ich lese ~300 Wörter pro Minute' 71 300_word: 'Ich lese ~300 Wörter pro Minute'
72 400_word: 'Ich lese ~400 Wörter pro Minute' 72 400_word: 'Ich lese ~400 Wörter pro Minute'
73 action_mark_as_read:
74 label: 'Wohin soll nach dem Gelesenmarkieren eines Artikels weitergeleitet werden?'
75 redirect_homepage: 'Zur Homepage'
76 redirect_current_page: 'Zur aktuellen Seite'
73 pocket_consumer_key_label: Consumer-Key für Pocket, um Inhalte zu importieren 77 pocket_consumer_key_label: Consumer-Key für Pocket, um Inhalte zu importieren
78 android_configuration: Konfiguriere deine Android Application
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'E-Mail-Adresse' 99 email_label: 'E-Mail-Adresse'
95 twoFactorAuthentication_label: 'Zwei-Faktor-Authentifizierung' 100 twoFactorAuthentication_label: 'Zwei-Faktor-Authentifizierung'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 title: Lösche mein Konto (a.k.a Gefahrenzone)
104 description: Wenn du dein Konto löschst, werden ALL deine Artikel, ALL deine Tags, ALL deine Anmerkungen und dein Konto dauerhaft gelöscht (kann NICHT RÜCKGÄNGIG gemacht werden). Du wirst anschließend ausgeloggt.
105 confirm: Bist du wirklich sicher? (DIES KANN NICHT RÜCKGÄNGIG GEMACHT WERDEN)
106 button: Lösche mein Konto
107 reset:
108 title: Zurücksetzen (a.k.a Gefahrenzone)
109 description: Beim Nutzen der folgenden Schaltflächenhast du die Möglichkeit, einige Informationen von deinem Konto zu entfernen. Sei dir bewusst, dass dies NICHT RÜCKGÄNGIG zu machen ist.
110 annotations: Entferne ALLE Annotationen
111 tags: Entferne ALLE Tags
112 entries: Entferne ALLE Einträge
113 confirm: Bist du wirklich sicher? (DIES KANN NICHT RÜCKGÄNGIG GEMACHT WERDEN)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Altes Kennwort' 116 old_password_label: 'Altes Kennwort'
@@ -168,6 +185,7 @@ entry:
168 preview_picture_label: 'Vorschaubild vorhanden' 185 preview_picture_label: 'Vorschaubild vorhanden'
169 preview_picture_help: 'Vorschaubild' 186 preview_picture_help: 'Vorschaubild'
170 language_label: 'Sprache' 187 language_label: 'Sprache'
188 # http_status_label: 'HTTP status'
171 reading_time: 189 reading_time:
172 label: 'Lesezeit in Minuten' 190 label: 'Lesezeit in Minuten'
173 from: 'von' 191 from: 'von'
@@ -329,6 +347,9 @@ tag:
329 list: 347 list:
330 number_on_the_page: '{0} Es gibt keine Tags.|{1} Es gibt einen Tag.|]1,Inf[ Es gibt %count% Tags.' 348 number_on_the_page: '{0} Es gibt keine Tags.|{1} Es gibt einen Tag.|]1,Inf[ Es gibt %count% Tags.'
331 see_untagged_entries: 'Zeige nicht getaggte Einträge' 349 see_untagged_entries: 'Zeige nicht getaggte Einträge'
350 new:
351 # add: 'Add'
352 # placeholder: 'You can add several tags, separated by a comma.'
332 353
333import: 354import:
334 page_title: 'Importieren' 355 page_title: 'Importieren'
@@ -362,6 +383,7 @@ import:
362 how_to: 'Bitte wähle deinen Readability Export aus und klicke den unteren Button für das Hochladen und Importieren dessen.' 383 how_to: 'Bitte wähle deinen Readability Export aus und klicke den unteren Button für das Hochladen und Importieren dessen.'
363 worker: 384 worker:
364 enabled: "Der Import erfolgt asynchron. Sobald der Import gestartet ist, wird diese Aufgabe extern abgearbeitet. Der aktuelle Service dafür ist:" 385 enabled: "Der Import erfolgt asynchron. Sobald der Import gestartet ist, wird diese Aufgabe extern abgearbeitet. Der aktuelle Service dafür ist:"
386 download_images_warning: "Du hast das Herunterladen von Bildern für deine Artikel aktiviert. Verbunden mit dem klassischen Import kann es ewig dauern fortzufahren (oder sogar fehlschlagen). Wir <strong>empfehlen</strong> den asynchronen Import zu aktivieren, um Fehler zu vermeiden."
365 firefox: 387 firefox:
366 page_title: 'Aus Firefox importieren' 388 page_title: 'Aus Firefox importieren'
367 description: "Dieser Import wird all deine Lesezeichen aus Firefox importieren. Gehe zu deinen Lesezeichen (Strg+Shift+O), dann auf \"Importen und Sichern\", wähle \"Sichern…\". Du erhälst eine .json Datei." 389 description: "Dieser Import wird all deine Lesezeichen aus Firefox importieren. Gehe zu deinen Lesezeichen (Strg+Shift+O), dann auf \"Importen und Sichern\", wähle \"Sichern…\". Du erhälst eine .json Datei."
@@ -374,6 +396,10 @@ import:
374 page_title: 'Aus Instapaper importieren' 396 page_title: 'Aus Instapaper importieren'
375 description: 'Dieser Import wird all deine Instapaper Artikel importieren. Auf der Einstellungsseite (https://www.instapaper.com/user) klickst du auf "Download .CSV Datei" in dem Abschnitt "Export". Eine CSV Datei wird heruntergeladen (z.B. "instapaper-export.csv").' 397 description: 'Dieser Import wird all deine Instapaper Artikel importieren. Auf der Einstellungsseite (https://www.instapaper.com/user) klickst du auf "Download .CSV Datei" in dem Abschnitt "Export". Eine CSV Datei wird heruntergeladen (z.B. "instapaper-export.csv").'
376 how_to: "Bitte wähle deine Instapaper Sicherungsdatei aus und klicke den nachfolgenden Button zum Importieren." 398 how_to: "Bitte wähle deine Instapaper Sicherungsdatei aus und klicke den nachfolgenden Button zum Importieren."
399 pinboard:
400 page_title: "Aus Pinboard importieren"
401 description: 'Dieser Import wird all deine Pinboard Artikel importieren. Auf der Seite Backup (https://pinboard.in/settings/backup) klickst du auf "JSON" in dem Abschnitt "Lesezeichen". Eine JSON Datei wird dann heruntergeladen (z.B. "pinboard_export").'
402 how_to: 'Bitte wähle deinen Pinboard Export aus und klicke den nachfolgenden Button zum Importieren.'
377 403
378developer: 404developer:
379 page_title: 'Entwickler' 405 page_title: 'Entwickler'
@@ -453,7 +479,7 @@ user:
453 back_to_list: Zurück zur Liste 479 back_to_list: Zurück zur Liste
454 480
455error: 481error:
456 # page_title: An error occurred 482 page_title: Ein Fehler ist aufgetreten
457 483
458flashes: 484flashes:
459 config: 485 config:
@@ -465,8 +491,10 @@ flashes:
465 rss_updated: 'RSS-Informationen aktualisiert' 491 rss_updated: 'RSS-Informationen aktualisiert'
466 tagging_rules_updated: 'Tagging-Regeln aktualisiert' 492 tagging_rules_updated: 'Tagging-Regeln aktualisiert'
467 tagging_rules_deleted: 'Tagging-Regel gelöscht' 493 tagging_rules_deleted: 'Tagging-Regel gelöscht'
468 user_added: 'Benutzer "%username%" erstellt'
469 rss_token_updated: 'RSS-Token aktualisiert' 494 rss_token_updated: 'RSS-Token aktualisiert'
495 annotations_reset: Anmerkungen zurücksetzen
496 tags_reset: Tags zurücksetzen
497 entries_reset: Einträge zurücksetzen
470 entry: 498 entry:
471 notice: 499 notice:
472 entry_already_saved: 'Eintrag bereits am %date% gespeichert' 500 entry_already_saved: 'Eintrag bereits am %date% gespeichert'
@@ -496,3 +524,8 @@ flashes:
496 notice: 524 notice:
497 client_created: 'Neuer Client erstellt.' 525 client_created: 'Neuer Client erstellt.'
498 client_deleted: 'Client gelöscht' 526 client_deleted: 'Client gelöscht'
527 user:
528 notice:
529 added: 'Benutzer "%username%" hinzugefügt'
530 updated: 'Benutzer "%username%" aktualisiert'
531 deleted: 'Benutzer "%username%" gelöscht'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
index 139cdc24..20e83a07 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
@@ -70,7 +70,12 @@ config:
70 200_word: 'I read ~200 words per minute' 70 200_word: 'I read ~200 words per minute'
71 300_word: 'I read ~300 words per minute' 71 300_word: 'I read ~300 words per minute'
72 400_word: 'I read ~400 words per minute' 72 400_word: 'I read ~400 words per minute'
73 action_mark_as_read:
74 label: 'Where do you want to be redirected after mark an article as read?'
75 redirect_homepage: 'To the homepage'
76 redirect_current_page: 'To the current page'
73 pocket_consumer_key_label: Consumer key for Pocket to import contents 77 pocket_consumer_key_label: Consumer key for Pocket to import contents
78 android_configuration: Configure your Android application
74 help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 help_items_per_page: "You can change the number of articles displayed on each page." 80 help_items_per_page: "You can change the number of articles displayed on each page."
76 help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'Email' 99 email_label: 'Email'
95 twoFactorAuthentication_label: 'Two factor authentication' 100 twoFactorAuthentication_label: 'Two factor authentication'
96 help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 title: Delete my account (a.k.a danger zone)
104 description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
105 confirm: Are you really sure? (THIS CAN'T BE UNDONE)
106 button: Delete my account
107 reset:
108 title: Reset area (a.k.a danger zone)
109 description: By hitting buttons below you'll have ability to remove some information from your account. Be aware that these actions are IRREVERSIBLE.
110 annotations: Remove ALL annotations
111 tags: Remove ALL tags
112 entries: Remove ALL entries
113 confirm: Are you really sure? (THIS CAN'T BE UNDONE)
97 form_password: 114 form_password:
98 description: "You can change your password here. Your new password should by at least 8 characters long." 115 description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Current password' 116 old_password_label: 'Current password'
@@ -168,6 +185,7 @@ entry:
168 preview_picture_label: 'Has a preview picture' 185 preview_picture_label: 'Has a preview picture'
169 preview_picture_help: 'Preview picture' 186 preview_picture_help: 'Preview picture'
170 language_label: 'Language' 187 language_label: 'Language'
188 http_status_label: 'HTTP status'
171 reading_time: 189 reading_time:
172 label: 'Reading time in minutes' 190 label: 'Reading time in minutes'
173 from: 'from' 191 from: 'from'
@@ -329,6 +347,9 @@ tag:
329 list: 347 list:
330 number_on_the_page: '{0} There are no tags.|{1} There is one tag.|]1,Inf[ There are %count% tags.' 348 number_on_the_page: '{0} There are no tags.|{1} There is one tag.|]1,Inf[ There are %count% tags.'
331 see_untagged_entries: 'See untagged entries' 349 see_untagged_entries: 'See untagged entries'
350 new:
351 add: 'Add'
352 placeholder: 'You can add several tags, separated by a comma.'
332 353
333import: 354import:
334 page_title: 'Import' 355 page_title: 'Import'
@@ -362,6 +383,7 @@ import:
362 how_to: 'Please select your Readability export and click on the below button to upload and import it.' 383 how_to: 'Please select your Readability export and click on the below button to upload and import it.'
363 worker: 384 worker:
364 enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:" 385 enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
386 download_images_warning: "You enabled downloading images for your articles. Combined with classic import it can take ages to proceed (or maybe failed). We <strong>strongly recommend</strong> to enable asynchronous import to avoid errors."
365 firefox: 387 firefox:
366 page_title: 'Import > Firefox' 388 page_title: 'Import > Firefox'
367 description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." 389 description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file."
@@ -374,6 +396,10 @@ import:
374 page_title: 'Import > Instapaper' 396 page_title: 'Import > Instapaper'
375 description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").' 397 description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").'
376 how_to: 'Please select your Instapaper export and click on the below button to upload and import it.' 398 how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
399 pinboard:
400 page_title: "Import > Pinboard"
401 description: 'This importer will import all your Pinboard articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
402 how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 403
378developer: 404developer:
379 page_title: 'Developer' 405 page_title: 'Developer'
@@ -466,6 +492,9 @@ flashes:
466 tagging_rules_updated: 'Tagging rules updated' 492 tagging_rules_updated: 'Tagging rules updated'
467 tagging_rules_deleted: 'Tagging rule deleted' 493 tagging_rules_deleted: 'Tagging rule deleted'
468 rss_token_updated: 'RSS token updated' 494 rss_token_updated: 'RSS token updated'
495 annotations_reset: Annotations reset
496 tags_reset: Tags reset
497 entries_reset: Entries reset
469 entry: 498 entry:
470 notice: 499 notice:
471 entry_already_saved: 'Entry already saved on %date%' 500 entry_already_saved: 'Entry already saved on %date%'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
index 70e64bec..5507ccae 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
@@ -70,7 +70,12 @@ config:
70 200_word: 'Leo ~200 palabras por minuto' 70 200_word: 'Leo ~200 palabras por minuto'
71 300_word: 'Leo ~300 palabras por minuto' 71 300_word: 'Leo ~300 palabras por minuto'
72 400_word: 'Leo ~400 palabras por minuto' 72 400_word: 'Leo ~400 palabras por minuto'
73 action_mark_as_read:
74 # label: 'Where do you to be redirected after mark an article as read?'
75 # redirect_homepage: 'To the homepage'
76 # redirect_current_page: 'To the current page'
73 # pocket_consumer_key_label: Consumer key for Pocket to import contents 77 # pocket_consumer_key_label: Consumer key for Pocket to import contents
78 # android_configuration: Configure your Android application
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'Direccion e-mail' 99 email_label: 'Direccion e-mail'
95 twoFactorAuthentication_label: 'Autentificación de dos factores' 100 twoFactorAuthentication_label: 'Autentificación de dos factores'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 # title: Delete my account (a.k.a danger zone)
104 # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
105 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
106 # button: Delete my account
107 reset:
108 # title: Reset area (a.k.a danger zone)
109 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
110 # annotations: Remove ALL annotations
111 # tags: Remove ALL tags
112 # entries: Remove ALL entries
113 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Contraseña actual' 116 old_password_label: 'Contraseña actual'
@@ -168,6 +185,7 @@ entry:
168 preview_picture_label: 'Hay una foto' 185 preview_picture_label: 'Hay una foto'
169 preview_picture_help: 'Foto de preview' 186 preview_picture_help: 'Foto de preview'
170 language_label: 'Idioma' 187 language_label: 'Idioma'
188 # http_status_label: 'HTTP status'
171 reading_time: 189 reading_time:
172 label: 'Duración de lectura en minutos' 190 label: 'Duración de lectura en minutos'
173 from: 'de' 191 from: 'de'
@@ -329,6 +347,9 @@ tag:
329 list: 347 list:
330 number_on_the_page: '{0} No hay ninguna etiqueta.|{1} Hay una etiqueta.|]1,Inf[ Hay %count% etiquetas.' 348 number_on_the_page: '{0} No hay ninguna etiqueta.|{1} Hay una etiqueta.|]1,Inf[ Hay %count% etiquetas.'
331 # see_untagged_entries: 'See untagged entries' 349 # see_untagged_entries: 'See untagged entries'
350 new:
351 # add: 'Add'
352 # placeholder: 'You can add several tags, separated by a comma.'
332 353
333import: 354import:
334 page_title: 'Importar' 355 page_title: 'Importar'
@@ -362,6 +383,7 @@ import:
362 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 383 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
363 worker: 384 worker:
364 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:" 385 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
386 # download_images_warning: "You enabled downloading images for your articles. Combined with classic import it can take ages to proceed (or maybe failed). We <strong>strongly recommend</strong> to enable asynchronous import to avoid errors."
365 firefox: 387 firefox:
366 page_title: 'Importar > Firefox' 388 page_title: 'Importar > Firefox'
367 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." 389 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file."
@@ -374,6 +396,10 @@ import:
374 page_title: 'Importar > Instapaper' 396 page_title: 'Importar > Instapaper'
375 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").' 397 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").'
376 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.' 398 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
399 pinboard:
400 page_title: "Importar > Pinboard"
401 # description: 'This importer will import all your Instapaper articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
402 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 403
378developer: 404developer:
379 page_title: 'Promotor' 405 page_title: 'Promotor'
@@ -465,8 +491,10 @@ flashes:
465 rss_updated: 'La configuración de los feeds RSS ha sido actualizada' 491 rss_updated: 'La configuración de los feeds RSS ha sido actualizada'
466 tagging_rules_updated: 'Regla de etiquetado borrada' 492 tagging_rules_updated: 'Regla de etiquetado borrada'
467 tagging_rules_deleted: 'Regla de etiquetado actualizada' 493 tagging_rules_deleted: 'Regla de etiquetado actualizada'
468 user_added: 'Usuario "%username%" añadido'
469 rss_token_updated: 'RSS token actualizado' 494 rss_token_updated: 'RSS token actualizado'
495 # annotations_reset: Annotations reset
496 # tags_reset: Tags reset
497 # entries_reset: Entries reset
470 entry: 498 entry:
471 notice: 499 notice:
472 entry_already_saved: 'Entrada ya guardada por %fecha%' 500 entry_already_saved: 'Entrada ya guardada por %fecha%'
@@ -496,3 +524,8 @@ flashes:
496 notice: 524 notice:
497 client_created: 'Nuevo cliente creado.' 525 client_created: 'Nuevo cliente creado.'
498 client_deleted: 'Cliente suprimido' 526 client_deleted: 'Cliente suprimido'
527 user:
528 notice:
529 # added: 'User "%username%" added'
530 # updated: 'User "%username%" updated'
531 # deleted: 'User "%username%" deleted'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
index 75359901..ba8a9c35 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
@@ -70,7 +70,12 @@ config:
70 200_word: 'من تقریباً ۲۰۰ واژه را در دقیقه می‌خوانم' 70 200_word: 'من تقریباً ۲۰۰ واژه را در دقیقه می‌خوانم'
71 300_word: 'من تقریباً ۳۰۰ واژه را در دقیقه می‌خوانم' 71 300_word: 'من تقریباً ۳۰۰ واژه را در دقیقه می‌خوانم'
72 400_word: 'من تقریباً ۴۰۰ واژه را در دقیقه می‌خوانم' 72 400_word: 'من تقریباً ۴۰۰ واژه را در دقیقه می‌خوانم'
73 action_mark_as_read:
74 # label: 'Where do you to be redirected after mark an article as read?'
75 # redirect_homepage: 'To the homepage'
76 # redirect_current_page: 'To the current page'
73 pocket_consumer_key_label: کلید کاربری Pocket برای درون‌ریزی مطالب 77 pocket_consumer_key_label: کلید کاربری Pocket برای درون‌ریزی مطالب
78 # android_configuration: Configure your Android application
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'نشانی ایمیل' 99 email_label: 'نشانی ایمیل'
95 twoFactorAuthentication_label: 'تأیید ۲مرحله‌ای' 100 twoFactorAuthentication_label: 'تأیید ۲مرحله‌ای'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 # title: Delete my account (a.k.a danger zone)
104 # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
105 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
106 # button: Delete my account
107 reset:
108 # title: Reset area (a.k.a danger zone)
109 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
110 # annotations: Remove ALL annotations
111 # tags: Remove ALL tags
112 # entries: Remove ALL entries
113 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'رمز قدیمی' 116 old_password_label: 'رمز قدیمی'
@@ -168,6 +185,7 @@ entry:
168 preview_picture_label: 'دارای عکس پیش‌نمایش' 185 preview_picture_label: 'دارای عکس پیش‌نمایش'
169 preview_picture_help: 'پیش‌نمایش عکس' 186 preview_picture_help: 'پیش‌نمایش عکس'
170 language_label: 'زبان' 187 language_label: 'زبان'
188 # http_status_label: 'HTTP status'
171 reading_time: 189 reading_time:
172 label: 'زمان خواندن به دقیقه' 190 label: 'زمان خواندن به دقیقه'
173 from: 'از' 191 from: 'از'
@@ -279,6 +297,7 @@ quickstart:
279 paragraph_2: 'ادامه دهید!' 297 paragraph_2: 'ادامه دهید!'
280 configure: 298 configure:
281 title: 'برنامه را تنظیم کنید' 299 title: 'برنامه را تنظیم کنید'
300 # description: 'In order to have an application which suits you, have a look into the configuration of wallabag.'
282 language: 'زبان و نمای برنامه را تغییر دهید' 301 language: 'زبان و نمای برنامه را تغییر دهید'
283 rss: 'خوراک آر-اس-اس را فعال کنید' 302 rss: 'خوراک آر-اس-اس را فعال کنید'
284 tagging_rules: 'قانون‌های برچسب‌گذاری خودکار مقاله‌هایتان را تعریف کنید' 303 tagging_rules: 'قانون‌های برچسب‌گذاری خودکار مقاله‌هایتان را تعریف کنید'
@@ -328,6 +347,9 @@ tag:
328 list: 347 list:
329 number_on_the_page: '{0} هیچ برچسبی نیست.|{1} یک برچسب هست.|]1,Inf[ %count% برچسب هست.' 348 number_on_the_page: '{0} هیچ برچسبی نیست.|{1} یک برچسب هست.|]1,Inf[ %count% برچسب هست.'
330 # see_untagged_entries: 'See untagged entries' 349 # see_untagged_entries: 'See untagged entries'
350 new:
351 # add: 'Add'
352 # placeholder: 'You can add several tags, separated by a comma.'
331 353
332import: 354import:
333 page_title: 'درون‌ریزی' 355 page_title: 'درون‌ریزی'
@@ -361,6 +383,7 @@ import:
361 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 383 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
362 worker: 384 worker:
363 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:" 385 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
386 # download_images_warning: "You enabled downloading images for your articles. Combined with classic import it can take ages to proceed (or maybe failed). We <strong>strongly recommend</strong> to enable asynchronous import to avoid errors."
364 firefox: 387 firefox:
365 page_title: 'درون‌ریزی > Firefox' 388 page_title: 'درون‌ریزی > Firefox'
366 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." 389 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file."
@@ -373,6 +396,10 @@ import:
373 page_title: 'درون‌ریزی > Instapaper' 396 page_title: 'درون‌ریزی > Instapaper'
374 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").' 397 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").'
375 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.' 398 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
399 pinboard:
400 # page_title: "Import > Pinboard"
401 # description: 'This importer will import all your Instapaper articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
402 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
376 403
377developer: 404developer:
378 # page_title: 'Developer' 405 # page_title: 'Developer'
@@ -464,8 +491,10 @@ flashes:
464 rss_updated: 'اطلاعات آر-اس-اس به‌روز شد' 491 rss_updated: 'اطلاعات آر-اس-اس به‌روز شد'
465 tagging_rules_updated: 'برچسب‌گذاری خودکار به‌روز شد' 492 tagging_rules_updated: 'برچسب‌گذاری خودکار به‌روز شد'
466 tagging_rules_deleted: 'قانون برچسب‌گذاری پاک شد' 493 tagging_rules_deleted: 'قانون برچسب‌گذاری پاک شد'
467 user_added: 'کابر "%username%" افزوده شد'
468 rss_token_updated: 'کد آر-اس-اس به‌روز شد' 494 rss_token_updated: 'کد آر-اس-اس به‌روز شد'
495 # annotations_reset: Annotations reset
496 # tags_reset: Tags reset
497 # entries_reset: Entries reset
469 entry: 498 entry:
470 notice: 499 notice:
471 entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود' 500 entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود'
@@ -495,3 +524,8 @@ flashes:
495 notice: 524 notice:
496 # client_created: 'New client created.' 525 # client_created: 'New client created.'
497 # client_deleted: 'Client deleted' 526 # client_deleted: 'Client deleted'
527 user:
528 notice:
529 # added: 'User "%username%" added'
530 # updated: 'User "%username%" updated'
531 # deleted: 'User "%username%" deleted'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
index f2c9d8db..2932f2fd 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
@@ -70,7 +70,12 @@ config:
70 200_word: "Je lis environ 200 mots par minute" 70 200_word: "Je lis environ 200 mots par minute"
71 300_word: "Je lis environ 300 mots par minute" 71 300_word: "Je lis environ 300 mots par minute"
72 400_word: "Je lis environ 400 mots par minute" 72 400_word: "Je lis environ 400 mots par minute"
73 action_mark_as_read:
74 label: 'Où souhaitez-vous être redirigé après avoir marqué un article comme lu ?'
75 redirect_homepage: "À la page d'accueil"
76 redirect_current_page: 'À la page courante'
73 pocket_consumer_key_label: Clé d’authentification Pocket pour importer les données 77 pocket_consumer_key_label: Clé d’authentification Pocket pour importer les données
78 android_configuration: Configurez votre application Android
74 help_theme: "L'affichage de wallabag est personnalisable. C'est ici que vous choisissez le thème que vous préférez." 79 help_theme: "L'affichage de wallabag est personnalisable. C'est ici que vous choisissez le thème que vous préférez."
75 help_items_per_page: "Vous pouvez définir le nombre d'articles affichés sur chaque page." 80 help_items_per_page: "Vous pouvez définir le nombre d'articles affichés sur chaque page."
76 help_reading_speed: "wallabag calcule une durée de lecture pour chaque article. Vous pouvez définir ici, grâce à cette liste déroulante, si vous lisez plus ou moins vite. wallabag recalculera la durée de lecture de chaque article." 81 help_reading_speed: "wallabag calcule une durée de lecture pour chaque article. Vous pouvez définir ici, grâce à cette liste déroulante, si vous lisez plus ou moins vite. wallabag recalculera la durée de lecture de chaque article."
@@ -94,6 +99,18 @@ config:
94 email_label: "Adresse courriel" 99 email_label: "Adresse courriel"
95 twoFactorAuthentication_label: "Double authentification" 100 twoFactorAuthentication_label: "Double authentification"
96 help_twoFactorAuthentication: "Si vous activez 2FA, à chaque tentative de connexion à wallabag, vous recevrez un code par email." 101 help_twoFactorAuthentication: "Si vous activez 2FA, à chaque tentative de connexion à wallabag, vous recevrez un code par email."
102 delete:
103 title: Supprimer mon compte (attention danger !)
104 description: Si vous confirmez la suppression de votre compte, TOUS les articles, TOUS les tags, TOUTES les annotations et votre compte seront DÉFINITIVEMENT supprimé (c'est IRRÉVERSIBLE). Vous serez ensuite déconnecté.
105 confirm: Vous êtes vraiment sûr ? (C'EST IRRÉVERSIBLE)
106 button: 'Supprimer mon compte'
107 reset:
108 title: Réinitialisation (attention danger !)
109 description: En cliquant sur les boutons ci-dessous vous avez la possibilité de supprimer certaines informations de votre compte. Attention, ces actions sont IRRÉVERSIBLES !
110 annotations: Supprimer TOUTES les annotations
111 tags: Supprimer TOUS les tags
112 entries: Supprimer TOUS les articles
113 confirm: Êtes-vous vraiment vraiment sûr ? (C'EST IRRÉVERSIBLE)
97 form_password: 114 form_password:
98 description: "Vous pouvez changer ici votre mot de passe. Le mot de passe doit contenir au moins 8 caractères." 115 description: "Vous pouvez changer ici votre mot de passe. Le mot de passe doit contenir au moins 8 caractères."
99 old_password_label: "Mot de passe actuel" 116 old_password_label: "Mot de passe actuel"
@@ -168,6 +185,7 @@ entry:
168 preview_picture_label: "A une photo" 185 preview_picture_label: "A une photo"
169 preview_picture_help: "Photo" 186 preview_picture_help: "Photo"
170 language_label: "Langue" 187 language_label: "Langue"
188 http_status_label: 'Statut HTTP'
171 reading_time: 189 reading_time:
172 label: "Durée de lecture en minutes" 190 label: "Durée de lecture en minutes"
173 from: "de" 191 from: "de"
@@ -329,6 +347,9 @@ tag:
329 list: 347 list:
330 number_on_the_page: "{0} Il n’y a pas de tag.|{1} Il y a un tag.|]1,Inf[ Il y a %count% tags." 348 number_on_the_page: "{0} Il n’y a pas de tag.|{1} Il y a un tag.|]1,Inf[ Il y a %count% tags."
331 see_untagged_entries: "Voir les articles sans tag" 349 see_untagged_entries: "Voir les articles sans tag"
350 new:
351 add: 'Ajouter'
352 placeholder: 'Vous pouvez ajouter plusieurs tags, séparés par une virgule.'
332 353
333import: 354import:
334 page_title: "Importer" 355 page_title: "Importer"
@@ -362,6 +383,7 @@ import:
362 how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer." 383 how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer."
363 worker: 384 worker:
364 enabled: "Les imports sont asynchrones. Une fois l’import commencé un worker externe traitera les messages un par un. Le service activé est :" 385 enabled: "Les imports sont asynchrones. Une fois l’import commencé un worker externe traitera les messages un par un. Le service activé est :"
386 download_images_warning: "Vous avez configuré le téléchagement des images pour vos articles. Combiné à l'import classique, cette opération peut être très très longue (voire échouer). Nous vous conseillons <strong>vivement</strong> d'activer les imports asynchrones."
365 firefox: 387 firefox:
366 page_title: "Import > Firefox" 388 page_title: "Import > Firefox"
367 description: "Cet outil va vous permettre d’importer tous vos marques-pages de Firefox. Ouvrez le panneau des marques-pages (Ctrl+Maj+O), puis dans « Importation et sauvegarde », choisissez « Sauvegarde... ». Vous allez récupérer un fichier .json. </p>" 389 description: "Cet outil va vous permettre d’importer tous vos marques-pages de Firefox. Ouvrez le panneau des marques-pages (Ctrl+Maj+O), puis dans « Importation et sauvegarde », choisissez « Sauvegarde... ». Vous allez récupérer un fichier .json. </p>"
@@ -374,6 +396,10 @@ import:
374 page_title: "Import > Instapaper" 396 page_title: "Import > Instapaper"
375 description: "Sur la page des paramètres (https://www.instapaper.com/user), cliquez sur « Download .CSV file » dans la section « Export ». Un fichier CSV sera téléchargé (« instapaper-export.csv »)." 397 description: "Sur la page des paramètres (https://www.instapaper.com/user), cliquez sur « Download .CSV file » dans la section « Export ». Un fichier CSV sera téléchargé (« instapaper-export.csv »)."
376 how_to: "Choisissez le fichier de votre export Instapaper et cliquez sur le bouton ci-dessous pour l’importer." 398 how_to: "Choisissez le fichier de votre export Instapaper et cliquez sur le bouton ci-dessous pour l’importer."
399 pinboard:
400 page_title: "Import > Pinboard"
401 description: "Sur la page « Backup » (https://pinboard.in/settings/backup), cliquez sur « JSON » dans la section « Bookmarks ». Un fichier json (sans extension) sera téléchargé (« pinboard_export »)."
402 how_to: "Choisissez le fichier de votre export Pinboard et cliquez sur le bouton ci-dessous pour l’importer."
377 403
378developer: 404developer:
379 page_title: "Développeur" 405 page_title: "Développeur"
@@ -465,8 +491,10 @@ flashes:
465 rss_updated: "La configuration des flux RSS a bien été mise à jour" 491 rss_updated: "La configuration des flux RSS a bien été mise à jour"
466 tagging_rules_updated: "Règles mises à jour" 492 tagging_rules_updated: "Règles mises à jour"
467 tagging_rules_deleted: "Règle supprimée" 493 tagging_rules_deleted: "Règle supprimée"
468 user_added: "Utilisateur \"%username%\" ajouté"
469 rss_token_updated: "Jeton RSS mis à jour" 494 rss_token_updated: "Jeton RSS mis à jour"
495 annotations_reset: Annotations supprimées
496 tags_reset: Tags supprimés
497 entries_reset: Articles supprimés
470 entry: 498 entry:
471 notice: 499 notice:
472 entry_already_saved: "Article déjà sauvegardé le %date%" 500 entry_already_saved: "Article déjà sauvegardé le %date%"
@@ -496,3 +524,8 @@ flashes:
496 notice: 524 notice:
497 client_created: "Nouveau client %name% créé" 525 client_created: "Nouveau client %name% créé"
498 client_deleted: "Client %name% supprimé" 526 client_deleted: "Client %name% supprimé"
527 user:
528 notice:
529 added: 'Utilisateur "%username%" ajouté'
530 updated: 'Utilisateur "%username%" mis à jour'
531 deleted: 'Utilisateur "%username%" supprimé'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
index b7d066ab..538e25a6 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
@@ -70,7 +70,12 @@ config:
70 200_word: 'Leggo ~200 parole al minuto' 70 200_word: 'Leggo ~200 parole al minuto'
71 300_word: 'Leggo ~300 parole al minuto' 71 300_word: 'Leggo ~300 parole al minuto'
72 400_word: 'Leggo ~400 parole al minuto' 72 400_word: 'Leggo ~400 parole al minuto'
73 action_mark_as_read:
74 # label: 'Where do you to be redirected after mark an article as read?'
75 # redirect_homepage: 'To the homepage'
76 # redirect_current_page: 'To the current page'
73 pocket_consumer_key_label: Consumer key per Pocket per importare i contenuti 77 pocket_consumer_key_label: Consumer key per Pocket per importare i contenuti
78 # android_configuration: Configure your Android application
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'E-mail' 99 email_label: 'E-mail'
95 twoFactorAuthentication_label: 'Two factor authentication' 100 twoFactorAuthentication_label: 'Two factor authentication'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 # title: Delete my account (a.k.a danger zone)
104 # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
105 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
106 # button: Delete my account
107 reset:
108 # title: Reset area (a.k.a danger zone)
109 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
110 # annotations: Remove ALL annotations
111 # tags: Remove ALL tags
112 # entries: Remove ALL entries
113 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Password corrente' 116 old_password_label: 'Password corrente'
@@ -168,6 +185,7 @@ entry:
168 preview_picture_label: "Ha un'immagine di anteprima" 185 preview_picture_label: "Ha un'immagine di anteprima"
169 preview_picture_help: 'Immagine di anteprima' 186 preview_picture_help: 'Immagine di anteprima'
170 language_label: 'Lingua' 187 language_label: 'Lingua'
188 # http_status_label: 'HTTP status'
171 reading_time: 189 reading_time:
172 label: 'Tempo di lettura in minuti' 190 label: 'Tempo di lettura in minuti'
173 from: 'da' 191 from: 'da'
@@ -329,6 +347,9 @@ tag:
329 list: 347 list:
330 number_on_the_page: "{0} Non ci sono tag.|{1} C'è un tag.|]1,Inf[ ci sono %count% tag." 348 number_on_the_page: "{0} Non ci sono tag.|{1} C'è un tag.|]1,Inf[ ci sono %count% tag."
331 # see_untagged_entries: 'See untagged entries' 349 # see_untagged_entries: 'See untagged entries'
350 new:
351 # add: 'Add'
352 # placeholder: 'You can add several tags, separated by a comma.'
332 353
333import: 354import:
334 page_title: 'Importa' 355 page_title: 'Importa'
@@ -362,6 +383,7 @@ import:
362 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 383 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
363 worker: 384 worker:
364 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:" 385 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
386 # download_images_warning: "You enabled downloading images for your articles. Combined with classic import it can take ages to proceed (or maybe failed). We <strong>strongly recommend</strong> to enable asynchronous import to avoid errors."
365 firefox: 387 firefox:
366 page_title: 'Importa da > Firefox' 388 page_title: 'Importa da > Firefox'
367 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." 389 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file."
@@ -374,6 +396,10 @@ import:
374 page_title: 'Importa da > Instapaper' 396 page_title: 'Importa da > Instapaper'
375 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").' 397 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").'
376 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.' 398 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
399 pinboard:
400 page_title: "Importa da > Pinboard"
401 # description: 'This importer will import all your Instapaper articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
402 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 403
378developer: 404developer:
379 page_title: 'Sviluppatori' 405 page_title: 'Sviluppatori'
@@ -465,8 +491,10 @@ flashes:
465 rss_updated: 'Informazioni RSS aggiornate' 491 rss_updated: 'Informazioni RSS aggiornate'
466 tagging_rules_updated: 'Regole di tagging aggiornate' 492 tagging_rules_updated: 'Regole di tagging aggiornate'
467 tagging_rules_deleted: 'Regola di tagging aggiornate' 493 tagging_rules_deleted: 'Regola di tagging aggiornate'
468 user_added: 'Utente "%username%" aggiunto'
469 rss_token_updated: 'RSS token aggiornato' 494 rss_token_updated: 'RSS token aggiornato'
495 # annotations_reset: Annotations reset
496 # tags_reset: Tags reset
497 # entries_reset: Entries reset
470 entry: 498 entry:
471 notice: 499 notice:
472 entry_already_saved: 'Contenuto già salvato in data %date%' 500 entry_already_saved: 'Contenuto già salvato in data %date%'
@@ -496,3 +524,8 @@ flashes:
496 notice: 524 notice:
497 client_created: 'Nuovo client creato.' 525 client_created: 'Nuovo client creato.'
498 client_deleted: 'Client eliminato' 526 client_deleted: 'Client eliminato'
527 user:
528 notice:
529 # added: 'User "%username%" added'
530 # updated: 'User "%username%" updated'
531 # deleted: 'User "%username%" deleted'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
index 16ef1e5d..6f415c3b 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
@@ -25,13 +25,13 @@ menu:
25 internal_settings: 'Configuracion interna' 25 internal_settings: 'Configuracion interna'
26 import: 'Importar' 26 import: 'Importar'
27 howto: 'Ajuda' 27 howto: 'Ajuda'
28 developer: 'Desvolopador' 28 developer: 'Desvolopaire'
29 logout: 'Desconnexion' 29 logout: 'Desconnexion'
30 about: 'A prepaus' 30 about: 'A prepaus'
31 search: 'Cercar' 31 search: 'Cercar'
32 save_link: 'Enregistrar un novèl article' 32 save_link: 'Enregistrar un novèl article'
33 back_to_unread: 'Tornar als articles pas legits' 33 back_to_unread: 'Tornar als articles pas legits'
34 # users_management: 'Users management' 34 users_management: 'Gestion dels utilizaires'
35 top: 35 top:
36 add_new_entry: 'Enregistrar un novèl article' 36 add_new_entry: 'Enregistrar un novèl article'
37 search: 'Cercar' 37 search: 'Cercar'
@@ -46,7 +46,7 @@ footer:
46 social: 'Social' 46 social: 'Social'
47 powered_by: 'propulsat per' 47 powered_by: 'propulsat per'
48 about: 'A prepaus' 48 about: 'A prepaus'
49 # stats: Since %user_creation% you read %nb_archives% articles. That is about %per_day% a day! 49 stats: "Dempuèi %user_creation% avètz legit %nb_archives% articles. Es a l'entorn de %per_day% per jorn !"
50 50
51config: 51config:
52 page_title: 'Configuracion' 52 page_title: 'Configuracion'
@@ -70,7 +70,12 @@ config:
70 200_word: "Legissi a l'entorn de 200 mots per minuta" 70 200_word: "Legissi a l'entorn de 200 mots per minuta"
71 300_word: "Legissi a l'entorn de 300 mots per minuta" 71 300_word: "Legissi a l'entorn de 300 mots per minuta"
72 400_word: "Legissi a l'entorn de 400 mots per minuta" 72 400_word: "Legissi a l'entorn de 400 mots per minuta"
73 action_mark_as_read:
74 # label: 'Where do you to be redirected after mark an article as read?'
75 # redirect_homepage: 'To the homepage'
76 # redirect_current_page: 'To the current page'
73 pocket_consumer_key_label: Clau d'autentificacion Pocket per importar las donadas 77 pocket_consumer_key_label: Clau d'autentificacion Pocket per importar las donadas
78 android_configuration: Configuratz vòstra aplicacion Android
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'Adreça de corrièl' 99 email_label: 'Adreça de corrièl'
95 twoFactorAuthentication_label: 'Dobla autentificacion' 100 twoFactorAuthentication_label: 'Dobla autentificacion'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 title: Suprimir mon compte (Mèfi zòna perilhosa)
104 description: Se confirmatz la supression de vòstre compte, TOTES vòstres articles, TOTAS vòstras etiquetas, TOTAS vòstras anotacions e vòstre compte seràn suprimits per totjorn. E aquò es IRREVERSIBLE. Puèi seretz desconnectat.
105 confirm: Sètz vertadièrament segur ? (ES IRREVERSIBLE)
106 button: Suprimir mon compte
107 reset:
108 title: Zòna de reïnicializacion (Mèfi zòna perilhosa)
109 description: En clicant sul boton çai-jos auretz la possibilitat de levar qualques informacions de vòstre compte. Mèfi que totas aquelas accions son IRREVERSIBLAS.
110 annotations: Levar TOTAS las anotacions
111 tags: Levar TOTAS las etiquetas
112 entries: Levar TOTES los articles
113 confirm: Sètz vertadièrament segur ? (ES IRREVERSIBLE)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Senhal actual' 116 old_password_label: 'Senhal actual'
@@ -103,7 +120,7 @@ config:
103 if_label: 'se' 120 if_label: 'se'
104 then_tag_as_label: 'alara atribuir las etiquetas' 121 then_tag_as_label: 'alara atribuir las etiquetas'
105 delete_rule_label: 'suprimir' 122 delete_rule_label: 'suprimir'
106 # edit_rule_label: 'edit' 123 edit_rule_label: 'modificar'
107 rule_label: 'Règla' 124 rule_label: 'Règla'
108 tags_label: 'Etiquetas' 125 tags_label: 'Etiquetas'
109 faq: 126 faq:
@@ -168,6 +185,7 @@ entry:
168 preview_picture_label: 'A una fotò' 185 preview_picture_label: 'A una fotò'
169 preview_picture_help: 'Fotò' 186 preview_picture_help: 'Fotò'
170 language_label: 'Lenga' 187 language_label: 'Lenga'
188 # http_status_label: 'HTTP status'
171 reading_time: 189 reading_time:
172 label: 'Durada de lectura en minutas' 190 label: 'Durada de lectura en minutas'
173 from: 'de' 191 from: 'de'
@@ -216,7 +234,7 @@ entry:
216 is_public_label: 'Public' 234 is_public_label: 'Public'
217 save_label: 'Enregistrar' 235 save_label: 'Enregistrar'
218 public: 236 public:
219 # shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>" 237 shared_by_wallabag: "Aqueste article es estat partejat per <a href='%wallabag_instance%'>wallabag</a>"
220 238
221about: 239about:
222 page_title: 'A prepaus' 240 page_title: 'A prepaus'
@@ -272,14 +290,14 @@ howto:
272 290
273quickstart: 291quickstart:
274 page_title: 'Per ben començar' 292 page_title: 'Per ben començar'
275 # more: 'More…' 293 more: 'Mai…'
276 intro: 294 intro:
277 title: 'Benvenguda sus wallabag !' 295 title: 'Benvenguda sus wallabag !'
278 paragraph_1: "Anem vos guidar per far lo torn de la proprietat e vos presentar unas fonccionalitats que vos poirián interessar per vos apropriar aquesta aisina." 296 paragraph_1: "Anem vos guidar per far lo torn de la proprietat e vos presentar unas fonccionalitats que vos poirián interessar per vos apropriar aquesta aisina."
279 paragraph_2: 'Seguètz-nos ' 297 paragraph_2: 'Seguètz-nos '
280 configure: 298 configure:
281 title: "Configuratz l'aplicacio" 299 title: "Configuratz l'aplicacion"
282 # description: 'In order to have an application which suits you, have a look into the configuration of wallabag.' 300 description: "Per fin d'aver una aplicacion que vos va ben, anatz veire la configuracion de wallabag."
283 language: "Cambiatz la lenga e l'estil de l'aplicacion" 301 language: "Cambiatz la lenga e l'estil de l'aplicacion"
284 rss: 'Activatz los fluxes RSS' 302 rss: 'Activatz los fluxes RSS'
285 tagging_rules: 'Escrivètz de règlas per classar automaticament vòstres articles' 303 tagging_rules: 'Escrivètz de règlas per classar automaticament vòstres articles'
@@ -293,7 +311,7 @@ quickstart:
293 import: 'Configurar los impòrt' 311 import: 'Configurar los impòrt'
294 first_steps: 312 first_steps:
295 title: 'Primièrs passes' 313 title: 'Primièrs passes'
296 # description: "Now wallabag is well configured, it's time to archive the web. You can click on the top right sign + to add a link." 314 description: "Ara wallabag es ben configurat, es lo moment d'archivar lo web. Podètz clicar sul signe + a man drecha amont per ajustar un ligam."
297 new_article: 'Ajustatz vòstre primièr article' 315 new_article: 'Ajustatz vòstre primièr article'
298 unread_articles: 'E racaptatz-lo !' 316 unread_articles: 'E racaptatz-lo !'
299 migrate: 317 migrate:
@@ -305,14 +323,14 @@ quickstart:
305 readability: 'Migrar dempuèi Readability' 323 readability: 'Migrar dempuèi Readability'
306 instapaper: 'Migrar dempuèi Instapaper' 324 instapaper: 'Migrar dempuèi Instapaper'
307 developer: 325 developer:
308 title: 'Pels desvolopadors' 326 title: 'Pels desvolopaires'
309 # description: 'We also thought to the developers: Docker, API, translations, etc.' 327 description: 'Avèm tanben pensat als desvolopaires : Docker, API, traduccions, etc.'
310 create_application: 'Crear vòstra aplicacion tèrça' 328 create_application: 'Crear vòstra aplicacion tèrça'
311 # use_docker: 'Use Docker to install wallabag' 329 use_docker: 'Utilizar Docker per installar wallabag'
312 docs: 330 docs:
313 title: 'Documentacion complèta' 331 title: 'Documentacion complèta'
314 # description: "There are so much features in wallabag. Don't hesitate to read the manual to know them and to learn how to use them." 332 description: "I a un fum de fonccionalitats dins wallabag. Esitetz pas a legir lo manual per las conéisser e aprendre a las utilizar."
315 annotate: 'Anotatar vòstre article' 333 annotate: 'Anotar vòstre article'
316 export: 'Convertissètz vòstres articles en ePub o en PDF' 334 export: 'Convertissètz vòstres articles en ePub o en PDF'
317 search_filters: "Aprenètz a utilizar lo motor de recèrca e los filtres per retrobar l'article que vos interèssa" 335 search_filters: "Aprenètz a utilizar lo motor de recèrca e los filtres per retrobar l'article que vos interèssa"
318 fetching_errors: "Qué far se mon article es pas recuperat coma cal ?" 336 fetching_errors: "Qué far se mon article es pas recuperat coma cal ?"
@@ -329,6 +347,9 @@ tag:
329 list: 347 list:
330 number_on_the_page: "{0} I a pas cap d'etiquetas.|{1} I a una etiqueta.|]1,Inf[ I a %count% etiquetas." 348 number_on_the_page: "{0} I a pas cap d'etiquetas.|{1} I a una etiqueta.|]1,Inf[ I a %count% etiquetas."
331 see_untagged_entries: "Afichar las entradas sens pas cap d'etiquetas" 349 see_untagged_entries: "Afichar las entradas sens pas cap d'etiquetas"
350 new:
351 # add: 'Add'
352 # placeholder: 'You can add several tags, separated by a comma.'
332 353
333import: 354import:
334 page_title: 'Importar' 355 page_title: 'Importar'
@@ -362,6 +383,7 @@ import:
362 how_to: "Mercés de seleccionar vòstre Readability fichièr e de clicar sul boton dejós per lo telecargar e l'importar." 383 how_to: "Mercés de seleccionar vòstre Readability fichièr e de clicar sul boton dejós per lo telecargar e l'importar."
363 worker: 384 worker:
364 enabled: "L'importacion se fa de manièra asincròna. Un còp l'importacion lançada, una aisina externa s'ocuparà dels messatges un per un. Lo servici actual es : " 385 enabled: "L'importacion se fa de manièra asincròna. Un còp l'importacion lançada, una aisina externa s'ocuparà dels messatges un per un. Lo servici actual es : "
386 download_images_warning: "Avètz activat lo telecargament de los imatges de vòstres articles. Combinat amb l'importacion classica, aquò pòt tardar un long moment (o benlèu fracassar). <strong>Recomandem fòrtament</strong> l'activacion de l'importacion asincròna per evitar las errors."
365 firefox: 387 firefox:
366 page_title: 'Importar > Firefox' 388 page_title: 'Importar > Firefox'
367 description: "Aquesta aisina importarà totas vòstres favorits de Firefox. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." 389 description: "Aquesta aisina importarà totas vòstres favorits de Firefox. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file."
@@ -374,6 +396,10 @@ import:
374 page_title: 'Importar > Instapaper' 396 page_title: 'Importar > Instapaper'
375 description: "Aquesta aisina importarà totas vòstres articles d'Instapaper. Sus la pagina de paramètres (https://www.instapaper.com/user), clicatz sus \"Download .CSV file\" dins la seccion \"Export\". Un fichièr CSV serà telecargat (aital \"instapaper-export.csv\")." 397 description: "Aquesta aisina importarà totas vòstres articles d'Instapaper. Sus la pagina de paramètres (https://www.instapaper.com/user), clicatz sus \"Download .CSV file\" dins la seccion \"Export\". Un fichièr CSV serà telecargat (aital \"instapaper-export.csv\")."
376 how_to: "Mercés de causir vòstre fichièr Instapaper e de clicar sul boton dejós per lo telecargar e l'importar" 398 how_to: "Mercés de causir vòstre fichièr Instapaper e de clicar sul boton dejós per lo telecargar e l'importar"
399 pinboard:
400 # page_title: "Import > Pinboard"
401 # description: 'This importer will import all your Instapaper articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
402 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 403
378developer: 404developer:
379 page_title: 'Desvolopaire' 405 page_title: 'Desvolopaire'
@@ -397,7 +423,7 @@ developer:
397 warn_message_2: "Se suprimissètz un client, totas las aplicacions que l'emplegan foncionaràn pas mai amb vòstre compte wallabag." 423 warn_message_2: "Se suprimissètz un client, totas las aplicacions que l'emplegan foncionaràn pas mai amb vòstre compte wallabag."
398 action: 'Suprimir aqueste client' 424 action: 'Suprimir aqueste client'
399 client: 425 client:
400 page_title: 'Desvlopador > Novèl client' 426 page_title: 'Desvolopaire > Novèl client'
401 page_description: "Anatz crear un novèl client. Mercés de cumplir l'url de redireccion cap a vòstra aplicacion." 427 page_description: "Anatz crear un novèl client. Mercés de cumplir l'url de redireccion cap a vòstra aplicacion."
402 form: 428 form:
403 name_label: "Nom del client" 429 name_label: "Nom del client"
@@ -405,7 +431,7 @@ developer:
405 save_label: 'Crear un novèl client' 431 save_label: 'Crear un novèl client'
406 action_back: 'Retorn' 432 action_back: 'Retorn'
407 client_parameter: 433 client_parameter:
408 page_title: 'Desvolopador > Los paramètres de vòstre client' 434 page_title: 'Desvolopaire > Los paramètres de vòstre client'
409 page_description: 'Vaquí los paramètres de vòstre client' 435 page_description: 'Vaquí los paramètres de vòstre client'
410 field_name: 'Nom del client' 436 field_name: 'Nom del client'
411 field_id: 'ID Client' 437 field_id: 'ID Client'
@@ -413,7 +439,7 @@ developer:
413 back: 'Retour' 439 back: 'Retour'
414 read_howto: 'Legir "cossí crear ma primièra aplicacion"' 440 read_howto: 'Legir "cossí crear ma primièra aplicacion"'
415 howto: 441 howto:
416 page_title: 'Desvolopador > Cossí crear ma primièra aplicacion' 442 page_title: 'Desvolopaire > Cossí crear ma primièra aplicacion'
417 description: 443 description:
418 paragraph_1: "Las comandas seguentas utilizan la <a href=\"https://github.com/jkbrzt/httpie\">bibliotèca HTTPie</a>. Asseguratz-vos que siasqueòu installadas abans de l'utilizar." 444 paragraph_1: "Las comandas seguentas utilizan la <a href=\"https://github.com/jkbrzt/httpie\">bibliotèca HTTPie</a>. Asseguratz-vos que siasqueòu installadas abans de l'utilizar."
419 paragraph_2: "Vos cal un geton per escambiar entre vòstra aplicacion e l'API de wallabar." 445 paragraph_2: "Vos cal un geton per escambiar entre vòstra aplicacion e l'API de wallabar."
@@ -426,34 +452,34 @@ developer:
426 back: 'Retorn' 452 back: 'Retorn'
427 453
428user: 454user:
429 # page_title: Users management 455 page_title: 'Gestion dels utilizaires'
430 # new_user: Create a new user 456 new_user: 'Crear un novèl utilizaire'
431 # edit_user: Edit an existing user 457 edit_user: 'Modificar un utilizaire existent'
432 # description: "Here you can manage all users (create, edit and delete)" 458 description: "Aquí podètz gerir totes los utilizaires (crear, modificar e suprimir)"
433 # list: 459 list:
434 # actions: Actions 460 actions: 'Accions'
435 # edit_action: Edit 461 edit_action: 'Modificar'
436 # yes: Yes 462 yes: 'Òc'
437 # no: No 463 no: 'Non'
438 # create_new_one: Create a new user 464 create_new_one: 'Crear un novèl utilizaire'
439 form: 465 form:
440 username_label: "Nom d'utilizaire" 466 username_label: "Nom d'utilizaire"
441 # name_label: 'Name' 467 name_label: 'Nom'
442 password_label: 'Senhal' 468 password_label: 'Senhal'
443 repeat_new_password_label: 'Confirmatz vòstre novèl senhal' 469 repeat_new_password_label: 'Confirmatz vòstre novèl senhal'
444 plain_password_label: 'Senhal en clar' 470 plain_password_label: 'Senhal en clar'
445 email_label: 'Adreça de corrièl' 471 email_label: 'Adreça de corrièl'
446 # enabled_label: 'Enabled' 472 enabled_label: 'Actiu'
447 # locked_label: 'Locked' 473 locked_label: 'Varrolhat'
448 # last_login_label: 'Last login' 474 last_login_label: 'Darrièra connexion'
449 # twofactor_label: Two factor authentication 475 twofactor_label: 'Autentificacion doble-factor'
450 # save: Save 476 save: 'Enregistrar'
451 # delete: Delete 477 delete: 'Suprimir'
452 # delete_confirm: Are you sure? 478 delete_confirm: 'Sètz segur ?'
453 # back_to_list: Back to list 479 back_to_list: 'Tornar a la lista'
454 480
455error: 481error:
456 # page_title: An error occurred 482 page_title: Una error s'es produsida
457 483
458flashes: 484flashes:
459 config: 485 config:
@@ -465,8 +491,10 @@ flashes:
465 rss_updated: 'La configuracion dels fluxes RSS es ben estada mesa a jorn' 491 rss_updated: 'La configuracion dels fluxes RSS es ben estada mesa a jorn'
466 tagging_rules_updated: 'Règlas misa a jorn' 492 tagging_rules_updated: 'Règlas misa a jorn'
467 tagging_rules_deleted: 'Règla suprimida' 493 tagging_rules_deleted: 'Règla suprimida'
468 user_added: 'Utilizaire "%username%" apondut'
469 rss_token_updated: 'Geton RSS mes a jorn' 494 rss_token_updated: 'Geton RSS mes a jorn'
495 annotations_reset: Anotacions levadas
496 tags_reset: Etiquetas levadas
497 entries_reset: Articles levats
470 entry: 498 entry:
471 notice: 499 notice:
472 entry_already_saved: 'Article ja salvargardat lo %date%' 500 entry_already_saved: 'Article ja salvargardat lo %date%'
@@ -477,12 +505,12 @@ flashes:
477 entry_reloaded_failed: "L'article es estat cargat de nòu mai la recuperacion del contengut a fracassat" 505 entry_reloaded_failed: "L'article es estat cargat de nòu mai la recuperacion del contengut a fracassat"
478 entry_archived: 'Article marcat coma legit' 506 entry_archived: 'Article marcat coma legit'
479 entry_unarchived: 'Article marcat coma pas legit' 507 entry_unarchived: 'Article marcat coma pas legit'
480 entry_starred: 'Article apondut dins los favorits' 508 entry_starred: 'Article ajustat dins los favorits'
481 entry_unstarred: 'Article quitat dels favorits' 509 entry_unstarred: 'Article quitat dels favorits'
482 entry_deleted: 'Article suprimit' 510 entry_deleted: 'Article suprimit'
483 tag: 511 tag:
484 notice: 512 notice:
485 tag_added: 'Etiqueta aponduda' 513 tag_added: 'Etiqueta ajustada'
486 import: 514 import:
487 notice: 515 notice:
488 failed: "L'importacion a fracassat, mercés de tornar ensajar" 516 failed: "L'importacion a fracassat, mercés de tornar ensajar"
@@ -496,3 +524,8 @@ flashes:
496 notice: 524 notice:
497 client_created: 'Novèl client creat' 525 client_created: 'Novèl client creat'
498 client_deleted: 'Client suprimit' 526 client_deleted: 'Client suprimit'
527 user:
528 notice:
529 added: 'Utilizaire "%username%" ajustat'
530 updated: 'Utilizaire "%username%" mes a jorn'
531 deleted: 'Utilizaire "%username%" suprimit'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
index 73250cc0..df5ab7e5 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
@@ -70,7 +70,12 @@ config:
70 200_word: 'Czytam ~200 słów na minutę' 70 200_word: 'Czytam ~200 słów na minutę'
71 300_word: 'Czytam ~300 słów na minutę' 71 300_word: 'Czytam ~300 słów na minutę'
72 400_word: 'Czytam ~400 słów na minutę' 72 400_word: 'Czytam ~400 słów na minutę'
73 action_mark_as_read:
74 label: 'Gdzie zostaniesz przekierowany po oznaczeniu artukuły jako przeczytanego'
75 redirect_homepage: 'do strony głównej'
76 redirect_current_page: 'do bieżącej strony'
73 pocket_consumer_key_label: 'Klucz klienta Pocket do importu zawartości' 77 pocket_consumer_key_label: 'Klucz klienta Pocket do importu zawartości'
78 android_configuration: Skonfiguruj swoją androidową aplikację
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'Adres email' 99 email_label: 'Adres email'
95 twoFactorAuthentication_label: 'Autoryzacja dwuetapowa' 100 twoFactorAuthentication_label: 'Autoryzacja dwuetapowa'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 title: Usuń moje konto (niebezpieczna strefa !)
104 description: Jeżeli usuniesz swoje konto, wszystkie twoje artykuły, tagi, adnotacje, oraz konto zostaną trwale usunięte (operacja jest NIEODWRACALNA). Następnie zostaniesz wylogowany.
105 confirm: Jesteś pewien? (tej operacji NIE MOŻNA cofnąć)
106 button: Usuń moje konto
107 reset:
108 title: Reset (niebezpieczna strefa)
109 description: Poniższe przyciski pozwalają usunąć pewne informacje z twojego konta. Uważaj te operacje są NIEODWRACALNE.
110 annotations: Usuń WSZYSTKIE adnotacje
111 tags: Usuń WSZYSTKIE tagi
112 entries: usuń WSZYTSTKIE wpisy
113 confirm: Jesteś pewien? (tej operacji NIE MOŻNA cofnąć)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Stare hasło' 116 old_password_label: 'Stare hasło'
@@ -168,6 +185,7 @@ entry:
168 preview_picture_label: 'Posiada podgląd obrazu' 185 preview_picture_label: 'Posiada podgląd obrazu'
169 preview_picture_help: 'Podgląd obrazu' 186 preview_picture_help: 'Podgląd obrazu'
170 language_label: 'Język' 187 language_label: 'Język'
188 # http_status_label: 'HTTP status'
171 reading_time: 189 reading_time:
172 label: 'Czas czytania w minutach' 190 label: 'Czas czytania w minutach'
173 from: 'od' 191 from: 'od'
@@ -329,6 +347,9 @@ tag:
329 list: 347 list:
330 number_on_the_page: '{0} Nie ma tagów.|{1} Jest jeden tag.|]1,Inf[ Są %count% tagi.' 348 number_on_the_page: '{0} Nie ma tagów.|{1} Jest jeden tag.|]1,Inf[ Są %count% tagi.'
331 see_untagged_entries: 'Zobacz nieotagowane wpisy' 349 see_untagged_entries: 'Zobacz nieotagowane wpisy'
350 new:
351 add: 'Dodaj'
352 placeholder: 'Możesz dodać kilka tagów, oddzielając je przecinkami.'
332 353
333import: 354import:
334 page_title: 'Import' 355 page_title: 'Import'
@@ -362,6 +383,7 @@ import:
362 how_to: 'Wybierz swój plik eksportu z Readability i kliknij poniższy przycisk, aby go załadować.' 383 how_to: 'Wybierz swój plik eksportu z Readability i kliknij poniższy przycisk, aby go załadować.'
363 worker: 384 worker:
364 enabled: "Import jest wykonywany asynchronicznie. Od momentu rozpoczęcia importu, zewnętrzna usługa może zajmować się na raz tylko jednym zadaniem. Bieżącą usługą jest:" 385 enabled: "Import jest wykonywany asynchronicznie. Od momentu rozpoczęcia importu, zewnętrzna usługa może zajmować się na raz tylko jednym zadaniem. Bieżącą usługą jest:"
386 download_images_warning: "Włączyłeś pobieranie obrazów dla swoich artykułów. W połączeniu z klasycznym importem, może to zająć dużo czasu (lub zakończyć się niepowodzeniem).<strong>Zdecydowanie zalecamy</strong> włączenie asynchronicznego importu, w celu uniknięcia błędów."
365 firefox: 387 firefox:
366 page_title: 'Import > Firefox' 388 page_title: 'Import > Firefox'
367 description: "Ten importer zaimportuje wszystkie twoje zakładki z Firefoksa. Idź do twoich zakładek (Ctrl+Shift+O), następnie w \"Import i kopie zapasowe\", wybierz \"Utwórz kopię zapasową...\". Uzyskasz plik .json." 389 description: "Ten importer zaimportuje wszystkie twoje zakładki z Firefoksa. Idź do twoich zakładek (Ctrl+Shift+O), następnie w \"Import i kopie zapasowe\", wybierz \"Utwórz kopię zapasową...\". Uzyskasz plik .json."
@@ -374,6 +396,10 @@ import:
374 page_title: 'Import > Instapaper' 396 page_title: 'Import > Instapaper'
375 description: 'Ten importer, zaimportuje wszystkie twoje artykuły z Instapaper. W ustawieniach (https://www.instapaper.com/user), kliknij na "Download .CSV file" w sekcji "Export". Otrzymasz plik CSV.' 397 description: 'Ten importer, zaimportuje wszystkie twoje artykuły z Instapaper. W ustawieniach (https://www.instapaper.com/user), kliknij na "Download .CSV file" w sekcji "Export". Otrzymasz plik CSV.'
376 how_to: 'Wybierz swój plik eksportu z Instapaper i kliknij poniższy przycisk, aby go załadować.' 398 how_to: 'Wybierz swój plik eksportu z Instapaper i kliknij poniższy przycisk, aby go załadować.'
399 pinboard:
400 page_title: "Import > Pinboard"
401 description: 'Ten importer, zaimportuje wszystkie twoje artykuły z Pinboard. W ustawieniach kopii zapasowej (https://pinboard.in/settings/backup), kliknij na "JSON" w sekcji "Bookmarks". Otrzymasz plik "pinboard_export".'
402 how_to: 'Wybierz swój plik eksportu z Pinboard i kliknij poniższy przycisk, aby go załadować.'
377 403
378developer: 404developer:
379 page_title: 'Deweloper' 405 page_title: 'Deweloper'
@@ -453,7 +479,7 @@ user:
453 back_to_list: Powrót do listy 479 back_to_list: Powrót do listy
454 480
455error: 481error:
456 # page_title: An error occurred 482 page_title: Wystąpił błąd
457 483
458flashes: 484flashes:
459 config: 485 config:
@@ -465,8 +491,10 @@ flashes:
465 rss_updated: 'Informacje RSS zaktualizowane' 491 rss_updated: 'Informacje RSS zaktualizowane'
466 tagging_rules_updated: 'Reguły tagowania zaktualizowane' 492 tagging_rules_updated: 'Reguły tagowania zaktualizowane'
467 tagging_rules_deleted: 'Reguła tagowania usunięta' 493 tagging_rules_deleted: 'Reguła tagowania usunięta'
468 user_added: 'Użytkownik "%username%" dodany'
469 rss_token_updated: 'Token kanału RSS zaktualizowany' 494 rss_token_updated: 'Token kanału RSS zaktualizowany'
495 annotations_reset: Zresetuj adnotacje
496 tags_reset: Zresetuj tagi
497 entries_reset: Zresetuj wpisy
470 entry: 498 entry:
471 notice: 499 notice:
472 entry_already_saved: 'Wpis już został dodany %date%' 500 entry_already_saved: 'Wpis już został dodany %date%'
@@ -496,3 +524,8 @@ flashes:
496 notice: 524 notice:
497 client_created: 'Nowy klient utworzony.' 525 client_created: 'Nowy klient utworzony.'
498 client_deleted: 'Klient usunięty' 526 client_deleted: 'Klient usunięty'
527 user:
528 notice:
529 added: 'Użytkownik "%username%" dodany'
530 updated: 'Użytkownik "%username%" zaktualizowany'
531 deleted: 'Użytkownik "%username%" usunięty'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
index a375ac7b..1c1c9a78 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
@@ -70,7 +70,12 @@ config:
70 200_word: 'Posso ler ~200 palavras por minuto' 70 200_word: 'Posso ler ~200 palavras por minuto'
71 300_word: 'Posso ler ~300 palavras por minuto' 71 300_word: 'Posso ler ~300 palavras por minuto'
72 400_word: 'Posso ler ~400 palavras por minuto' 72 400_word: 'Posso ler ~400 palavras por minuto'
73 action_mark_as_read:
74 # label: 'Where do you to be redirected after mark an article as read?'
75 # redirect_homepage: 'To the homepage'
76 # redirect_current_page: 'To the current page'
73 pocket_consumer_key_label: 'Chave do consumidor do Pocket para importar conteúdo' 77 pocket_consumer_key_label: 'Chave do consumidor do Pocket para importar conteúdo'
78 # android_configuration: Configure your Android application
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'E-mail' 99 email_label: 'E-mail'
95 twoFactorAuthentication_label: 'Autenticação de dois passos' 100 twoFactorAuthentication_label: 'Autenticação de dois passos'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 # title: Delete my account (a.k.a danger zone)
104 # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
105 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
106 # button: Delete my account
107 reset:
108 # title: Reset area (a.k.a danger zone)
109 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
110 # annotations: Remove ALL annotations
111 # tags: Remove ALL tags
112 # entries: Remove ALL entries
113 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Senha atual' 116 old_password_label: 'Senha atual'
@@ -168,6 +185,7 @@ entry:
168 preview_picture_label: 'Possui uma imagem de preview' 185 preview_picture_label: 'Possui uma imagem de preview'
169 preview_picture_help: 'Imagem de preview' 186 preview_picture_help: 'Imagem de preview'
170 language_label: 'Idioma' 187 language_label: 'Idioma'
188 # http_status_label: 'HTTP status'
171 reading_time: 189 reading_time:
172 label: 'Tempo de leitura em minutos' 190 label: 'Tempo de leitura em minutos'
173 from: 'de' 191 from: 'de'
@@ -329,6 +347,9 @@ tag:
329 list: 347 list:
330 number_on_the_page: '{0} Não existem tags.|{1} Uma tag.|]1,Inf[ Existem %count% tags.' 348 number_on_the_page: '{0} Não existem tags.|{1} Uma tag.|]1,Inf[ Existem %count% tags.'
331 see_untagged_entries: 'Ver entradas sem tags' 349 see_untagged_entries: 'Ver entradas sem tags'
350 new:
351 # add: 'Add'
352 # placeholder: 'You can add several tags, separated by a comma.'
332 353
333import: 354import:
334 page_title: 'Importar' 355 page_title: 'Importar'
@@ -362,6 +383,7 @@ import:
362 how_to: 'Por favor, selecione sua exportação do Readability e clique no botão abaixo para importá-la.' 383 how_to: 'Por favor, selecione sua exportação do Readability e clique no botão abaixo para importá-la.'
363 worker: 384 worker:
364 enabled: "A importação é feita assíncronamente. Uma vez que a tarefa de importação é iniciada, um trabalho externo pode executar tarefas uma por vez. O serviço atual é:" 385 enabled: "A importação é feita assíncronamente. Uma vez que a tarefa de importação é iniciada, um trabalho externo pode executar tarefas uma por vez. O serviço atual é:"
386 # download_images_warning: "You enabled downloading images for your articles. Combined with classic import it can take ages to proceed (or maybe failed). We <strong>strongly recommend</strong> to enable asynchronous import to avoid errors."
365 firefox: 387 firefox:
366 page_title: 'Importar > Firefox' 388 page_title: 'Importar > Firefox'
367 description: "Com este importador você importa todos os favoritos de seu Firefox. Somente vá até seus favoritos (Ctrl+Maj+O), e em \"Importar e Backup\" e escolha \"Backup...\". Você terá então um arquivo .json." 389 description: "Com este importador você importa todos os favoritos de seu Firefox. Somente vá até seus favoritos (Ctrl+Maj+O), e em \"Importar e Backup\" e escolha \"Backup...\". Você terá então um arquivo .json."
@@ -374,6 +396,10 @@ import:
374 page_title: 'Importar > Instapaper' 396 page_title: 'Importar > Instapaper'
375 description: 'Este importador pode importar todos os artigos do seu Instapaper. Nas página de configurações (https://www.instapaper.com/user), clique em "Download .CSV file" na seção "Export". Um arquivo CSV será baixado (algo como "instapaper-export.csv").' 397 description: 'Este importador pode importar todos os artigos do seu Instapaper. Nas página de configurações (https://www.instapaper.com/user), clique em "Download .CSV file" na seção "Export". Um arquivo CSV será baixado (algo como "instapaper-export.csv").'
376 how_to: 'Por favor, selecione sua exportação do seu Instapaper e clique no botão abaixo para importá-la.' 398 how_to: 'Por favor, selecione sua exportação do seu Instapaper e clique no botão abaixo para importá-la.'
399 pinboard:
400 # page_title: "Import > Pinboard"
401 # description: 'This importer will import all your Instapaper articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
402 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 403
378developer: 404developer:
379 page_title: 'Desenvolvedor' 405 page_title: 'Desenvolvedor'
@@ -452,17 +478,23 @@ user:
452 delete_confirm: 'Tem certeza?' 478 delete_confirm: 'Tem certeza?'
453 back_to_list: 'Voltar para a lista' 479 back_to_list: 'Voltar para a lista'
454 480
481error:
482 # page_title: An error occurred
483
455flashes: 484flashes:
456 config: 485 config:
457 notice: 486 notice:
458 config_saved: 'Configiração salva.' 487 config_saved: 'Configiração salva.'
459 password_updated: 'Senha atualizada' 488 password_updated: 'Senha atualizada'
460 password_not_updated_demo: 'Em modo de demonstração, você não pode alterar a senha deste usuário.' 489 password_not_updated_demo: 'Em modo de demonstração, você não pode alterar a senha deste usuário.'
461 user_updated: 'Informação atualizada' 490 # user_updated: 'Information updated'
462 rss_updated: 'Informação de RSS atualizada' 491 rss_updated: 'Informação de RSS atualizada'
463 tagging_rules_updated: 'Regras de tags atualizadas' 492 tagging_rules_updated: 'Regras de tags atualizadas'
464 tagging_rules_deleted: 'Regra de tag apagada' 493 tagging_rules_deleted: 'Regra de tag apagada'
465 rss_token_updated: 'Token RSS atualizado' 494 rss_token_updated: 'Token RSS atualizado'
495 # annotations_reset: Annotations reset
496 # tags_reset: Tags reset
497 # entries_reset: Entries reset
466 entry: 498 entry:
467 notice: 499 notice:
468 entry_already_saved: 'Entrada já foi salva em %date%' 500 entry_already_saved: 'Entrada já foi salva em %date%'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
index 7d8fcea3..8da04d95 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
@@ -70,7 +70,12 @@ config:
70 # 200_word: 'I read ~200 words per minute' 70 # 200_word: 'I read ~200 words per minute'
71 # 300_word: 'I read ~300 words per minute' 71 # 300_word: 'I read ~300 words per minute'
72 # 400_word: 'I read ~400 words per minute' 72 # 400_word: 'I read ~400 words per minute'
73 action_mark_as_read:
74 # label: 'Where do you to be redirected after mark an article as read?'
75 # redirect_homepage: 'To the homepage'
76 # redirect_current_page: 'To the current page'
73 pocket_consumer_key_label: Cheie consumator pentru importarea contentului din Pocket 77 pocket_consumer_key_label: Cheie consumator pentru importarea contentului din Pocket
78 # android_configuration: Configure your Android application
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'E-mail' 99 email_label: 'E-mail'
95 # twoFactorAuthentication_label: 'Two factor authentication' 100 # twoFactorAuthentication_label: 'Two factor authentication'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 # title: Delete my account (a.k.a danger zone)
104 # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
105 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
106 # button: Delete my account
107 reset:
108 # title: Reset area (a.k.a danger zone)
109 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
110 # annotations: Remove ALL annotations
111 # tags: Remove ALL tags
112 # entries: Remove ALL entries
113 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Parola veche' 116 old_password_label: 'Parola veche'
@@ -168,6 +185,7 @@ entry:
168 preview_picture_label: 'Are o imagine de previzualizare' 185 preview_picture_label: 'Are o imagine de previzualizare'
169 preview_picture_help: 'Previzualizare imagine' 186 preview_picture_help: 'Previzualizare imagine'
170 language_label: 'Limbă' 187 language_label: 'Limbă'
188 # http_status_label: 'HTTP status'
171 reading_time: 189 reading_time:
172 label: 'Timp de citire în minute' 190 label: 'Timp de citire în minute'
173 from: 'de la' 191 from: 'de la'
@@ -329,6 +347,9 @@ tag:
329 list: 347 list:
330 # number_on_the_page: '{0} There is no tag.|{1} There is one tag.|]1,Inf[ There are %count% tags.' 348 # number_on_the_page: '{0} There is no tag.|{1} There is one tag.|]1,Inf[ There are %count% tags.'
331 # see_untagged_entries: 'See untagged entries' 349 # see_untagged_entries: 'See untagged entries'
350 new:
351 # add: 'Add'
352 # placeholder: 'You can add several tags, separated by a comma.'
332 353
333import: 354import:
334 # page_title: 'Import' 355 # page_title: 'Import'
@@ -362,6 +383,7 @@ import:
362 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 383 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
363 worker: 384 worker:
364 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:" 385 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
386 # download_images_warning: "You enabled downloading images for your articles. Combined with classic import it can take ages to proceed (or maybe failed). We <strong>strongly recommend</strong> to enable asynchronous import to avoid errors."
365 # firefox: 387 # firefox:
366 # page_title: 'Import > Firefox' 388 # page_title: 'Import > Firefox'
367 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." 389 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file."
@@ -374,6 +396,10 @@ import:
374 # page_title: 'Import > Instapaper' 396 # page_title: 'Import > Instapaper'
375 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").' 397 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").'
376 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.' 398 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
399 pinboard:
400 # page_title: "Import > Pinboard"
401 # description: 'This importer will import all your Instapaper articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
402 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
377 403
378developer: 404developer:
379 # page_title: 'Developer' 405 # page_title: 'Developer'
@@ -465,8 +491,10 @@ flashes:
465 rss_updated: 'Informație RSS actualizată' 491 rss_updated: 'Informație RSS actualizată'
466 # tagging_rules_updated: 'Tagging rules updated' 492 # tagging_rules_updated: 'Tagging rules updated'
467 # tagging_rules_deleted: 'Tagging rule deleted' 493 # tagging_rules_deleted: 'Tagging rule deleted'
468 # user_added: 'User "%username%" added'
469 # rss_token_updated: 'RSS token updated' 494 # rss_token_updated: 'RSS token updated'
495 # annotations_reset: Annotations reset
496 # tags_reset: Tags reset
497 # entries_reset: Entries reset
470 entry: 498 entry:
471 notice: 499 notice:
472 # entry_already_saved: 'Entry already saved on %date%' 500 # entry_already_saved: 'Entry already saved on %date%'
@@ -496,3 +524,8 @@ flashes:
496 notice: 524 notice:
497 # client_created: 'New client created.' 525 # client_created: 'New client created.'
498 # client_deleted: 'Client deleted' 526 # client_deleted: 'Client deleted'
527 user:
528 notice:
529 # added: 'User "%username%" added'
530 # updated: 'User "%username%" updated'
531 # deleted: 'User "%username%" deleted'
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
index 357aa2ae..8d94044c 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
@@ -70,7 +70,12 @@ config:
70 # 200_word: 'I read ~200 words per minute' 70 # 200_word: 'I read ~200 words per minute'
71 # 300_word: 'I read ~300 words per minute' 71 # 300_word: 'I read ~300 words per minute'
72 # 400_word: 'I read ~400 words per minute' 72 # 400_word: 'I read ~400 words per minute'
73 action_mark_as_read:
74 # label: 'Where do you to be redirected after mark an article as read?'
75 # redirect_homepage: 'To the homepage'
76 # redirect_current_page: 'To the current page'
73 # pocket_consumer_key_label: Consumer key for Pocket to import contents 77 # pocket_consumer_key_label: Consumer key for Pocket to import contents
78 # android_configuration: Configure your Android application
74 # help_theme: "wallabag is customizable. You can choose your prefered theme here." 79 # help_theme: "wallabag is customizable. You can choose your prefered theme here."
75 # help_items_per_page: "You can change the number of articles displayed on each page." 80 # help_items_per_page: "You can change the number of articles displayed on each page."
76 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." 81 # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article."
@@ -94,6 +99,18 @@ config:
94 email_label: 'E-posta' 99 email_label: 'E-posta'
95 twoFactorAuthentication_label: 'İki adımlı doğrulama' 100 twoFactorAuthentication_label: 'İki adımlı doğrulama'
96 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email." 101 # help_twoFactorAuthentication: "If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email."
102 delete:
103 # title: Delete my account (a.k.a danger zone)
104 # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out.
105 # confirm: Are you really sure? (THIS CAN'T BE UNDONE)
106 # button: Delete my account
107 reset:
108 # title: Reset area (a.k.a danger zone)
109 # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE.
110 # annotations: Remove ALL annotations
111 # tags: Remove ALL tags
112 # entries: Remove ALL entries
113 # confirm: Are you really really sure? (THIS CAN'T BE UNDONE)
97 form_password: 114 form_password:
98 # description: "You can change your password here. Your new password should by at least 8 characters long." 115 # description: "You can change your password here. Your new password should by at least 8 characters long."
99 old_password_label: 'Eski şifre' 116 old_password_label: 'Eski şifre'
@@ -103,6 +120,7 @@ config:
103 # if_label: 'if' 120 # if_label: 'if'
104 # then_tag_as_label: 'then tag as' 121 # then_tag_as_label: 'then tag as'
105 # delete_rule_label: 'delete' 122 # delete_rule_label: 'delete'
123 # edit_rule_label: 'edit'
106 rule_label: 'Kural' 124 rule_label: 'Kural'
107 tags_label: 'Etiketler' 125 tags_label: 'Etiketler'
108 faq: 126 faq:
@@ -167,6 +185,7 @@ entry:
167 preview_picture_label: 'Resim önizlemesi varsa' 185 preview_picture_label: 'Resim önizlemesi varsa'
168 preview_picture_help: 'Resim önizlemesi' 186 preview_picture_help: 'Resim önizlemesi'
169 language_label: 'Dil' 187 language_label: 'Dil'
188 # http_status_label: 'HTTP status'
170 reading_time: 189 reading_time:
171 label: 'Dakika cinsinden okuma süresi' 190 label: 'Dakika cinsinden okuma süresi'
172 from: 'başlangıç' 191 from: 'başlangıç'
@@ -328,6 +347,9 @@ tag:
328 list: 347 list:
329 number_on_the_page: '{0} Herhangi bir etiket yok.|{1} Burada bir adet etiket var.|]1,Inf[ Burada %count% adet etiket var.' 348 number_on_the_page: '{0} Herhangi bir etiket yok.|{1} Burada bir adet etiket var.|]1,Inf[ Burada %count% adet etiket var.'
330 # see_untagged_entries: 'See untagged entries' 349 # see_untagged_entries: 'See untagged entries'
350 new:
351 # add: 'Add'
352 # placeholder: 'You can add several tags, separated by a comma.'
331 353
332import: 354import:
333 page_title: 'İçe Aktar' 355 page_title: 'İçe Aktar'
@@ -361,6 +383,7 @@ import:
361 # how_to: 'Please select your Readability export and click on the below button to upload and import it.' 383 # how_to: 'Please select your Readability export and click on the below button to upload and import it.'
362 worker: 384 worker:
363 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:" 385 # enabled: "Import is made asynchronously. Once the import task is started, an external worker will handle jobs one at a time. The current service is:"
386 # download_images_warning: "You enabled downloading images for your articles. Combined with classic import it can take ages to proceed (or maybe failed). We <strong>strongly recommend</strong> to enable asynchronous import to avoid errors."
364 firefox: 387 firefox:
365 page_title: 'İçe Aktar > Firefox' 388 page_title: 'İçe Aktar > Firefox'
366 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file." 389 # description: "This importer will import all your Firefox bookmarks. Just go to your bookmarks (Ctrl+Maj+O), then into \"Import and backup\", choose \"Backup...\". You will obtain a .json file."
@@ -373,6 +396,10 @@ import:
373 page_title: 'İçe Aktar > Instapaper' 396 page_title: 'İçe Aktar > Instapaper'
374 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").' 397 # description: 'This importer will import all your Instapaper articles. On the settings (https://www.instapaper.com/user) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like "instapaper-export.csv").'
375 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.' 398 # how_to: 'Please select your Instapaper export and click on the below button to upload and import it.'
399 pinboard:
400 # page_title: "Import > Pinboard"
401 # description: 'This importer will import all your Instapaper articles. On the backup (https://pinboard.in/settings/backup) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like "pinboard_export").'
402 # how_to: 'Please select your Pinboard export and click on the below button to upload and import it.'
376 403
377developer: 404developer:
378 # page_title: 'Developer' 405 # page_title: 'Developer'
@@ -464,8 +491,10 @@ flashes:
464 rss_updated: 'RSS bilgiler güncellendi' 491 rss_updated: 'RSS bilgiler güncellendi'
465 tagging_rules_updated: 'Tagging rules updated' 492 tagging_rules_updated: 'Tagging rules updated'
466 tagging_rules_deleted: 'Tagging rule deleted' 493 tagging_rules_deleted: 'Tagging rule deleted'
467 user_added: 'User "%username%" added'
468 rss_token_updated: 'RSS token updated' 494 rss_token_updated: 'RSS token updated'
495 # annotations_reset: Annotations reset
496 # tags_reset: Tags reset
497 # entries_reset: Entries reset
469 entry: 498 entry:
470 notice: 499 notice:
471 entry_already_saved: 'Entry already saved on %date%' 500 entry_already_saved: 'Entry already saved on %date%'
@@ -495,3 +524,8 @@ flashes:
495 notice: 524 notice:
496 # client_created: 'New client created.' 525 # client_created: 'New client created.'
497 # client_deleted: 'Client deleted' 526 # client_deleted: 'Client deleted'
527 user:
528 notice:
529 # added: 'User "%username%" added'
530 # updated: 'User "%username%" updated'
531 # deleted: 'User "%username%" deleted'
diff --git a/src/Wallabag/CoreBundle/Resources/views/base.html.twig b/src/Wallabag/CoreBundle/Resources/views/base.html.twig
index a1a9a136..289458d4 100644
--- a/src/Wallabag/CoreBundle/Resources/views/base.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/base.html.twig
@@ -41,6 +41,8 @@
41 {% block css %} 41 {% block css %}
42 {% endblock %} 42 {% endblock %}
43 {% block scripts %} 43 {% block scripts %}
44 <script src="{{ asset('bundles/fosjsrouting/js/router.js') }}"></script>
45 <script src="{{ path('fos_js_routing_js', { callback: 'fos.Router.setData' }) }}"></script>
44 {% endblock %} 46 {% endblock %}
45 47
46 <title>{% block title %}{% endblock %} – wallabag</title> 48 <title>{% block title %}{% endblock %} – wallabag</title>
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 98b0e119..3548f590 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
@@ -47,6 +47,14 @@
47 47
48 <fieldset class="w500p inline"> 48 <fieldset class="w500p inline">
49 <div class="row"> 49 <div class="row">
50 {{ form_label(form.config.action_mark_as_read) }}
51 {{ form_errors(form.config.action_mark_as_read) }}
52 {{ form_widget(form.config.action_mark_as_read) }}
53 </div>
54 </fieldset>
55
56 <fieldset class="w500p inline">
57 <div class="row">
50 {{ form_label(form.config.language) }} 58 {{ form_label(form.config.language) }}
51 {{ form_errors(form.config.language) }} 59 {{ form_errors(form.config.language) }}
52 {{ form_widget(form.config.language) }} 60 {{ form_widget(form.config.language) }}
@@ -71,6 +79,19 @@
71 </a> 79 </a>
72 </fieldset> 80 </fieldset>
73 81
82 <fieldset class="w500p inline">
83 <div class="row">
84 <h3>{{ 'config.form_settings.android_configuration'|trans }}</h3>
85 <a href="wallabag://{{ app.user.username }}@{{ wallabag_url }}" >Touch here to prefill your Android application</a>
86 <br/>
87 <img id="androidQrcode" />
88 <script>
89 const imgBase64 = jrQrcode.getQrBase64('wallabag://{{ app.user.username }}@{{ wallabag_url }}');
90 document.getElementById('androidQrcode').src = imgBase64;
91 </script>
92 </div>
93 </fieldset>
94
74 {{ form_rest(form.config) }} 95 {{ form_rest(form.config) }}
75 </form> 96 </form>
76 97
@@ -164,10 +185,41 @@
164 </fieldset> 185 </fieldset>
165 {% endif %} 186 {% endif %}
166 187
188 <h2>{{ 'config.reset.title'|trans }}</h2>
189 <fieldset class="w500p inline">
190 <p>{{ 'config.reset.description'|trans }}</p>
191 <ul>
192 <li>
193 <a href="{{ path('config_reset', { type: 'annotations'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
194 {{ 'config.reset.annotations'|trans }}
195 </a>
196 </li>
197 <li>
198 <a href="{{ path('config_reset', { type: 'tags'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
199 {{ 'config.reset.tags'|trans }}
200 </a>
201 </li>
202 <li>
203 <a href="{{ path('config_reset', { type: 'entries'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
204 {{ 'config.reset.entries'|trans }}
205 </a>
206 </li>
207 </ul>
208 </fieldset>
209
167 {{ form_widget(form.user._token) }} 210 {{ form_widget(form.user._token) }}
168 {{ form_widget(form.user.save) }} 211 {{ form_widget(form.user.save) }}
169 </form> 212 </form>
170 213
214 {% if enabled_users > 1 %}
215 <h2>{{ 'config.form_user.delete.title'|trans }}</h2>
216
217 <p>{{ 'config.form_user.delete.description'|trans }}</p>
218 <a href="{{ path('delete_account') }}" onclick="return confirm('{{ 'config.form_user.delete.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red delete-account">
219 {{ 'config.form_user.delete.button'|trans }}
220 </a>
221 {% endif %}
222
171 <h2>{{ 'config.tab_menu.password'|trans }}</h2> 223 <h2>{{ 'config.tab_menu.password'|trans }}</h2>
172 224
173 {{ form_start(form.pwd) }} 225 {{ form_start(form.pwd) }}
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig
index 56a0faac..d1baa283 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig
@@ -131,6 +131,13 @@
131 </div> 131 </div>
132 </div> 132 </div>
133 133
134 <div id="filter-http-status" class="filter-group">
135 {{ form_label(form.httpStatus) }}
136 <div class="input-field ">
137 {{ form_widget(form.httpStatus) }}
138 </div>
139 </div>
140
134 <div id="filter-reading-time" class="filter-group"> 141 <div id="filter-reading-time" class="filter-group">
135 <div class=""> 142 <div class="">
136 {{ form_label(form.readingTime) }} 143 {{ form_label(form.readingTime) }}
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig
index 3689159b..2e9673d5 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig
@@ -11,7 +11,7 @@
11 <div id="article_toolbar"> 11 <div id="article_toolbar">
12 <ul class="links"> 12 <ul class="links">
13 <li class="topPosF"><a href="#top" title="{{ 'entry.view.left_menu.back_to_top'|trans }}" class="tool top icon icon-arrow-up-thick"><span>{{ 'entry.view.left_menu.set_as_read'|trans }}</span></a></li> 13 <li class="topPosF"><a href="#top" title="{{ 'entry.view.left_menu.back_to_top'|trans }}" class="tool top icon icon-arrow-up-thick"><span>{{ 'entry.view.left_menu.set_as_read'|trans }}</span></a></li>
14 <li><a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e }}" class="tool link icon icon-link"><span>{{ entry.domainName|removeWww }}</span></a></li> 14 <li><a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e }}" class="tool link icon icon-link original"><span>{{ entry.domainName|removeWww }}</span></a></li>
15 <li><a title="{{ 'entry.view.left_menu.re_fetch_content'|trans }}" class="tool icon icon-reload" href="{{ path('reload_entry', { 'id': entry.id }) }}"><span>{{ 'entry.view.left_menu.re_fetch_content'|trans }}</span></a></li> 15 <li><a title="{{ 'entry.view.left_menu.re_fetch_content'|trans }}" class="tool icon icon-reload" href="{{ path('reload_entry', { 'id': entry.id }) }}"><span>{{ 'entry.view.left_menu.re_fetch_content'|trans }}</span></a></li>
16 16
17 {% set markAsReadLabel = 'entry.view.left_menu.set_as_unread' %} 17 {% set markAsReadLabel = 'entry.view.left_menu.set_as_unread' %}
@@ -19,8 +19,8 @@
19 {% set markAsReadLabel = 'entry.view.left_menu.set_as_read' %} 19 {% set markAsReadLabel = 'entry.view.left_menu.set_as_read' %}
20 {% endif %} 20 {% endif %}
21 21
22 <li><a title="{{ markAsReadLabel|trans }}" class="tool icon icon-check {% if entry.isArchived == 0 %}archive-off{% else %}archive{% endif %}" href="{{ path('archive_entry', { 'id': entry.id }) }}"><span>{{ markAsReadLabel|trans }}</span></a></li> 22 <li><a title="{{ markAsReadLabel|trans }}" class="tool icon icon-check {% if entry.isArchived == 0 %}archive-off{% else %}archive{% endif %} markasread" href="{{ path('archive_entry', { 'id': entry.id }) }}"><span>{{ markAsReadLabel|trans }}</span></a></li>
23 <li><a title="{{ 'entry.view.left_menu.set_as_starred'|trans }}" class="tool icon icon-star {% if entry.isStarred == 0 %}fav-off{% else %}fav{% endif %}" href="{{ path('star_entry', { 'id': entry.id }) }}"><span>{{ 'entry.view.left_menu.set_as_starred'|trans }}</span></a></li> 23 <li><a title="{{ 'entry.view.left_menu.set_as_starred'|trans }}" class="tool icon icon-star {% if entry.isStarred == 0 %}fav-off{% else %}fav{% endif %} favorite" href="{{ path('star_entry', { 'id': entry.id }) }}"><span>{{ 'entry.view.left_menu.set_as_starred'|trans }}</span></a></li>
24 <li><a id="nav-btn-add-tag" class="tool icon icon-price-tags" title="{{ 'entry.view.left_menu.add_a_tag'|trans }}"><span>{{ 'entry.view.left_menu.add_a_tag'|trans }}</span></a></li> 24 <li><a id="nav-btn-add-tag" class="tool icon icon-price-tags" title="{{ 'entry.view.left_menu.add_a_tag'|trans }}"><span>{{ 'entry.view.left_menu.add_a_tag'|trans }}</span></a></li>
25 <li><a title="{{ 'entry.view.left_menu.delete'|trans }}" class="tool delete icon icon-trash" href="{{ path('delete_entry', { 'id': entry.id }) }}"><span>{{ 'entry.view.left_menu.delete'|trans }}</span></a></li> 25 <li><a title="{{ 'entry.view.left_menu.delete'|trans }}" class="tool delete icon icon-trash" href="{{ path('delete_entry', { 'id': entry.id }) }}"><span>{{ 'entry.view.left_menu.delete'|trans }}</span></a></li>
26 {% if craue_setting('share_public') %} 26 {% if craue_setting('share_public') %}
@@ -31,6 +31,7 @@
31 {% if craue_setting('share_mail') %}<li><a href="mailto:?subject={{ entry.title|url_encode }}&amp;body={{ entry.url|url_encode }}%20via%20@wallabagapp" class="tool email icon icon-mail" title="Email"><span>Email</span></a></li>{% endif %} 31 {% if craue_setting('share_mail') %}<li><a href="mailto:?subject={{ entry.title|url_encode }}&amp;body={{ entry.url|url_encode }}%20via%20@wallabagapp" class="tool email icon icon-mail" title="Email"><span>Email</span></a></li>{% endif %}
32 {% if craue_setting('share_shaarli') %}<li><a href="{{ craue_setting('shaarli_url') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}" target="_blank" class="tool icon-image icon-image--shaarli" title="shaarli"><span>shaarli</span></a></li>{% endif %} 32 {% if craue_setting('share_shaarli') %}<li><a href="{{ craue_setting('shaarli_url') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}" target="_blank" class="tool icon-image icon-image--shaarli" title="shaarli"><span>shaarli</span></a></li>{% endif %}
33 {% if craue_setting('share_diaspora') %}<li><a href="{{ craue_setting('diaspora_url') }}/bookmarklet?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}&notes=&v=1&noui=1&jump=doclose" target="_blank" class="tool diaspora icon-image icon-image--diaspora" title="diaspora"><span>diaspora</span></a></li>{% endif %} 33 {% if craue_setting('share_diaspora') %}<li><a href="{{ craue_setting('diaspora_url') }}/bookmarklet?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}&notes=&v=1&noui=1&jump=doclose" target="_blank" class="tool diaspora icon-image icon-image--diaspora" title="diaspora"><span>diaspora</span></a></li>{% endif %}
34 {% if craue_setting('share_unmark') %}<li><a href="{{ craue_setting('unmark_url') }}/mark/add?url={{ entry.url|url_encode }}&amp;title={{entry.title|url_encode}}&amp;v=6" target="_blank" class="tool unmark icon-image icon-image--unmark" title="unmark"><span>unmark.it</span></a></li>{% endif %}
34 {% if craue_setting('carrot') %}<li><a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}" class="tool carrot icon-image icon-image--carrot" target="_blank" title="carrot"><span>Carrot</span></a></li>{% endif %} 35 {% if craue_setting('carrot') %}<li><a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}" class="tool carrot icon-image icon-image--carrot" target="_blank" title="carrot"><span>Carrot</span></a></li>{% endif %}
35 {% if craue_setting('show_printlink') %}<li><a title="{{ 'entry.view.left_menu.print'|trans }}" class="tool icon icon-print" href="javascript: window.print();"><span>{{ 'entry.view.left_menu.print'|trans }}</span></a></li>{% endif %} 36 {% if craue_setting('show_printlink') %}<li><a title="{{ 'entry.view.left_menu.print'|trans }}" class="tool icon icon-print" href="javascript: window.print();"><span>{{ 'entry.view.left_menu.print'|trans }}</span></a></li>{% endif %}
36 {% if craue_setting('export_epub') %}<li><a href="?epub&amp;method=id&amp;value={{ entry.id }}" title="Generate ePub file">EPUB</a></li>{% endif %} 37 {% if craue_setting('export_epub') %}<li><a href="?epub&amp;method=id&amp;value={{ entry.id }}" title="Generate ePub file">EPUB</a></li>{% endif %}
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 9f67217b..5d411fdd 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
@@ -66,6 +66,14 @@
66 </div> 66 </div>
67 </div> 67 </div>
68 68
69 <div class="row">
70 <div class="input-field col s12">
71 {{ form_label(form.config.action_mark_as_read) }}
72 {{ form_errors(form.config.action_mark_as_read) }}
73 {{ form_widget(form.config.action_mark_as_read) }}
74 </div>
75 </div>
76
69 <div class="row"> 77 <div class="row">
70 <div class="input-field col s11"> 78 <div class="input-field col s11">
71 {{ form_label(form.config.language) }} 79 {{ form_label(form.config.language) }}
@@ -96,6 +104,18 @@
96 </div> 104 </div>
97 </div> 105 </div>
98 106
107 <div class="row">
108 <div class="input-field col s12">
109 <h5>{{ 'config.form_settings.android_configuration'|trans }}</h5>
110 <a href="wallabag://{{ app.user.username }}@{{ wallabag_url }}" class="waves-effect waves-light btn hide-on-large-only">Touch here to prefill your Android application</a>
111 <img id="androidQrcode" class="hide-on-med-and-down" />
112 </div>
113 <script>
114 const imgBase64 = jrQrcode.getQrBase64('wallabag://{{ app.user.username }}@{{ wallabag_url }}');
115 document.getElementById('androidQrcode').src = imgBase64;
116 </script>
117 </div>
118
99 {{ form_widget(form.config.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} 119 {{ form_widget(form.config.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
100 {{ form_rest(form.config) }} 120 {{ form_rest(form.config) }}
101 </form> 121 </form>
@@ -197,6 +217,34 @@
197 {{ form_widget(form.user.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} 217 {{ form_widget(form.user.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
198 {{ form_widget(form.user._token) }} 218 {{ form_widget(form.user._token) }}
199 </form> 219 </form>
220
221 <br /><hr /><br />
222
223 <div class="row">
224 <h5>{{ 'config.reset.title'|trans }}</h5>
225 <p>{{ 'config.reset.description'|trans }}</p>
226 <a href="{{ path('config_reset', { type: 'annotations'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
227 {{ 'config.reset.annotations'|trans }}
228 </a>
229 <a href="{{ path('config_reset', { type: 'tags'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
230 {{ 'config.reset.tags'|trans }}
231 </a>
232 <a href="{{ path('config_reset', { type: 'entries'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red">
233 {{ 'config.reset.entries'|trans }}
234 </a>
235 </div>
236
237 {% if enabled_users > 1 %}
238 <br /><hr /><br />
239
240 <div class="row">
241 <h5>{{ 'config.form_user.delete.title'|trans }}</h5>
242 <p>{{ 'config.form_user.delete.description'|trans }}</p>
243 <a href="{{ path('delete_account') }}" onclick="return confirm('{{ 'config.form_user.delete.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red delete-account">
244 {{ 'config.form_user.delete.button'|trans }}
245 </a>
246 </div>
247 {% endif %}
200 </div> 248 </div>
201 249
202 <div id="set4" class="col s12"> 250 <div id="set4" class="col s12">
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig
index c610c8d2..160dbf3d 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig
@@ -104,6 +104,14 @@
104 </div> 104 </div>
105 105
106 <div class="col s12"> 106 <div class="col s12">
107 {{ form_label(form.httpStatus) }}
108 </div>
109
110 <div class="input-field col s12">
111 {{ form_widget(form.httpStatus) }}
112 </div>
113
114 <div class="col s12">
107 {{ form_label(form.readingTime) }} 115 {{ form_label(form.readingTime) }}
108 </div> 116 </div>
109 <div class="input-field col s6"> 117 <div class="input-field col s6">
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig
index c615a907..bed0fdec 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig
@@ -46,14 +46,14 @@
46 </li> 46 </li>
47 47
48 <li class="bold border-bottom hide-on-med-and-down"> 48 <li class="bold border-bottom hide-on-med-and-down">
49 <a class="waves-effect collapsible-header" href="{{ entry.url|e }}"> 49 <a class="waves-effect collapsible-header original" href="{{ entry.url|e }}" target="_blank">
50 <i class="material-icons small">link</i> 50 <i class="material-icons small">link</i>
51 <span>{{ 'entry.view.left_menu.view_original_article'|trans }}</span> 51 <span>{{ 'entry.view.left_menu.view_original_article'|trans }}</span>
52 </a> 52 </a>
53 <div class="collapsible-body"></div> 53 <div class="collapsible-body"></div>
54 </li> 54 </li>
55 55
56 <li class="bold hide-on-med-and-down"> 56 <li class="bold">
57 <a class="waves-effect collapsible-header" title="{{ 'entry.view.left_menu.re_fetch_content'|trans }}" href="{{ path('reload_entry', { 'id': entry.id }) }}" id="reload"> 57 <a class="waves-effect collapsible-header" title="{{ 'entry.view.left_menu.re_fetch_content'|trans }}" href="{{ path('reload_entry', { 'id': entry.id }) }}" id="reload">
58 <i class="material-icons small">autorenew</i> 58 <i class="material-icons small">autorenew</i>
59 <span>{{ 'entry.view.left_menu.re_fetch_content'|trans }}</span> 59 <span>{{ 'entry.view.left_menu.re_fetch_content'|trans }}</span>
@@ -67,7 +67,7 @@
67 {% endif %} 67 {% endif %}
68 68
69 <li class="bold hide-on-med-and-down"> 69 <li class="bold hide-on-med-and-down">
70 <a class="waves-effect collapsible-header" title="{{ markAsReadLabel|trans }}" href="{{ path('archive_entry', { 'id': entry.id }) }}" id="markAsRead"> 70 <a class="waves-effect collapsible-header markasread" title="{{ markAsReadLabel|trans }}" href="{{ path('archive_entry', { 'id': entry.id }) }}" id="markAsRead">
71 <i class="material-icons small">{% if entry.isArchived == 0 %}done{% else %}redo{% endif %}</i> 71 <i class="material-icons small">{% if entry.isArchived == 0 %}done{% else %}redo{% endif %}</i>
72 <span>{{ markAsReadLabel|trans }}</span> 72 <span>{{ markAsReadLabel|trans }}</span>
73 </a> 73 </a>
@@ -75,21 +75,21 @@
75 </li> 75 </li>
76 76
77 <li class="bold hide-on-med-and-down"> 77 <li class="bold hide-on-med-and-down">
78 <a class="waves-effect collapsible-header" title="{{ 'entry.view.left_menu.set_as_starred'|trans }}" href="{{ path('star_entry', { 'id': entry.id }) }}" id="setFav"> 78 <a class="waves-effect collapsible-header favorite" title="{{ 'entry.view.left_menu.set_as_starred'|trans }}" href="{{ path('star_entry', { 'id': entry.id }) }}" id="setFav">
79 <i class="material-icons spall">{% if entry.isStarred == 0 %}star_outline{% else %}star{% endif %}</i> 79 <i class="material-icons spall">{% if entry.isStarred == 0 %}star_outline{% else %}star{% endif %}</i>
80 <span>{{ 'entry.view.left_menu.set_as_starred'|trans }}</span> 80 <span>{{ 'entry.view.left_menu.set_as_starred'|trans }}</span>
81 </a> 81 </a>
82 <div class="collapsible-body"></div> 82 <div class="collapsible-body"></div>
83 </li> 83 </li>
84 <li class="bold border-bottom hide-on-med-and-down"> 84 <li class="bold border-bottom">
85 <a class="waves-effect collapsible-header" title="{{ 'entry.view.left_menu.delete'|trans }}" href="{{ path('delete_entry', { 'id': entry.id }) }}"> 85 <a class="waves-effect collapsible-header delete" title="{{ 'entry.view.left_menu.delete'|trans }}" href="{{ path('delete_entry', { 'id': entry.id }) }}">
86 <i class="material-icons small">delete</i> 86 <i class="material-icons small">delete</i>
87 <span>{{ 'entry.view.left_menu.delete'|trans }}</span> 87 <span>{{ 'entry.view.left_menu.delete'|trans }}</span>
88 </a> 88 </a>
89 <div class="collapsible-body"></div> 89 <div class="collapsible-body"></div>
90 </li> 90 </li>
91 91
92 <li class="bold border-bottom hide-on-med-and-down"> 92 <li class="bold border-bottom">
93 <a class="waves-effect collapsible-header" id="nav-btn-add-tag"> 93 <a class="waves-effect collapsible-header" id="nav-btn-add-tag">
94 <i class="material-icons small">label_outline</i> 94 <i class="material-icons small">label_outline</i>
95 <span>{{ 'entry.view.left_menu.add_a_tag'|trans }}</span> 95 <span>{{ 'entry.view.left_menu.add_a_tag'|trans }}</span>
@@ -139,6 +139,14 @@
139 </a> 139 </a>
140 </li> 140 </li>
141 {% endif %} 141 {% endif %}
142 {% if craue_setting('share_unmark') %}
143 <li>
144 <a href="{{ craue_setting('unmark_url') }}/mark/add?url={{ entry.url|url_encode }}&amp;title={{entry.title|url_encode}}&amp;v=6" target="_blank">
145 <i class="tool icon-image icon-image--unmark" title="unmark"></i>
146 <span>unmark.it</span>
147 </a>
148 </li>
149 {% endif %}
142 {% if craue_setting('carrot') %} 150 {% if craue_setting('carrot') %}
143 <li> 151 <li>
144 <a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}" target="_blank" title="carrot"> 152 <a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}" target="_blank" title="carrot">
@@ -186,14 +194,6 @@
186 </div> 194 </div>
187 </li> 195 </li>
188 196
189 <li class="bold hide-on-large-only">
190 <a class="waves-effect collapsible-header" title="{{ 'entry.view.left_menu.delete'|trans }}" href="{{ path('delete_entry', { 'id': entry.id }) }}">
191 <i class="material-icons small">delete</i>
192 <span>{{ 'entry.view.left_menu.delete'|trans }}</span>
193 </a>
194 <div class="collapsible-body"></div>
195 </li>
196
197 <li class="bold"> 197 <li class="bold">
198 <a class="waves-effect collapsible-header" href="mailto:hello@wallabag.org?subject=Wrong%20display%20in%20wallabag&amp;body={{ entry.url|url_encode }}" title="{{ 'entry.view.left_menu.problem.description'|trans }}"> 198 <a class="waves-effect collapsible-header" href="mailto:hello@wallabag.org?subject=Wrong%20display%20in%20wallabag&amp;body={{ entry.url|url_encode }}" title="{{ 'entry.view.left_menu.problem.description'|trans }}">
199 <i class="material-icons small">error</i> 199 <i class="material-icons small">error</i>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig
index 6e552560..b702c4b6 100644
--- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig
@@ -9,5 +9,6 @@
9 9
10 {{ form_widget(form.label, { 'attr': {'autocomplete': 'off'} }) }} 10 {{ form_widget(form.label, { 'attr': {'autocomplete': 'off'} }) }}
11 11
12 {{ form_rest(form) }} 12 {{ form_widget(form.add, {'attr': {'class': 'btn waves-effect waves-light hide-on-large-only'}}) }}
13 {{ form_widget(form._token) }}
13</form> 14</form>
diff --git a/src/Wallabag/ImportBundle/Command/ImportCommand.php b/src/Wallabag/ImportBundle/Command/ImportCommand.php
index d1325338..28d01715 100644
--- a/src/Wallabag/ImportBundle/Command/ImportCommand.php
+++ b/src/Wallabag/ImportBundle/Command/ImportCommand.php
@@ -14,10 +14,10 @@ class ImportCommand extends ContainerAwareCommand
14 { 14 {
15 $this 15 $this
16 ->setName('wallabag:import') 16 ->setName('wallabag:import')
17 ->setDescription('Import entries from a JSON export from a wallabag v1 instance') 17 ->setDescription('Import entries from a JSON export')
18 ->addArgument('userId', InputArgument::REQUIRED, 'User ID to populate') 18 ->addArgument('userId', InputArgument::REQUIRED, 'User ID to populate')
19 ->addArgument('filepath', InputArgument::REQUIRED, 'Path to the JSON file') 19 ->addArgument('filepath', InputArgument::REQUIRED, 'Path to the JSON file')
20 ->addOption('importer', null, InputArgument::OPTIONAL, 'The importer to use: wallabag v1, v2, firefox or chrome', 'v1') 20 ->addOption('importer', null, InputArgument::OPTIONAL, 'The importer to use: v1, v2, instapaper, pinboard, readability, firefox or chrome', 'v1')
21 ->addOption('markAsRead', null, InputArgument::OPTIONAL, 'Mark all entries as read', false) 21 ->addOption('markAsRead', null, InputArgument::OPTIONAL, 'Mark all entries as read', false)
22 ; 22 ;
23 } 23 }
@@ -42,32 +42,36 @@ class ImportCommand extends ContainerAwareCommand
42 42
43 switch ($input->getOption('importer')) { 43 switch ($input->getOption('importer')) {
44 case 'v2': 44 case 'v2':
45 $wallabag = $this->getContainer()->get('wallabag_import.wallabag_v2.import'); 45 $import = $this->getContainer()->get('wallabag_import.wallabag_v2.import');
46 break; 46 break;
47 case 'firefox': 47 case 'firefox':
48 $wallabag = $this->getContainer()->get('wallabag_import.firefox.import'); 48 $import = $this->getContainer()->get('wallabag_import.firefox.import');
49 break; 49 break;
50 case 'chrome': 50 case 'chrome':
51 $wallabag = $this->getContainer()->get('wallabag_import.chrome.import'); 51 $import = $this->getContainer()->get('wallabag_import.chrome.import');
52 break;
53 case 'readability':
54 $import = $this->getContainer()->get('wallabag_import.readability.import');
52 break; 55 break;
53 case 'instapaper': 56 case 'instapaper':
54 $wallabag = $this->getContainer()->get('wallabag_import.instapaper.import'); 57 $import = $this->getContainer()->get('wallabag_import.instapaper.import');
55 break; 58 break;
56 case 'v1': 59 case 'pinboard':
57 default: 60 $import = $this->getContainer()->get('wallabag_import.pinboard.import');
58 $wallabag = $this->getContainer()->get('wallabag_import.wallabag_v1.import');
59 break; 61 break;
62 default:
63 $import = $this->getContainer()->get('wallabag_import.wallabag_v1.import');
60 } 64 }
61 65
62 $wallabag->setMarkAsRead($input->getOption('markAsRead')); 66 $import->setMarkAsRead($input->getOption('markAsRead'));
63 $wallabag->setUser($user); 67 $import->setUser($user);
64 68
65 $res = $wallabag 69 $res = $import
66 ->setFilepath($input->getArgument('filepath')) 70 ->setFilepath($input->getArgument('filepath'))
67 ->import(); 71 ->import();
68 72
69 if (true === $res) { 73 if (true === $res) {
70 $summary = $wallabag->getSummary(); 74 $summary = $import->getSummary();
71 $output->writeln('<info>'.$summary['imported'].' imported</info>'); 75 $output->writeln('<info>'.$summary['imported'].' imported</info>');
72 $output->writeln('<comment>'.$summary['skipped'].' already saved</comment>'); 76 $output->writeln('<comment>'.$summary['skipped'].' already saved</comment>');
73 } 77 }
diff --git a/src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php b/src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php
index c2c11f11..f793a314 100644
--- a/src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php
+++ b/src/Wallabag/ImportBundle/Command/RedisWorkerCommand.php
@@ -17,7 +17,7 @@ class RedisWorkerCommand extends ContainerAwareCommand
17 $this 17 $this
18 ->setName('wallabag:import:redis-worker') 18 ->setName('wallabag:import:redis-worker')
19 ->setDescription('Launch Redis worker') 19 ->setDescription('Launch Redis worker')
20 ->addArgument('serviceName', InputArgument::REQUIRED, 'Service to use: wallabag_v1, wallabag_v2, pocket, readability, firefox, chrome or instapaper') 20 ->addArgument('serviceName', InputArgument::REQUIRED, 'Service to use: wallabag_v1, wallabag_v2, pocket, readability, pinboard, firefox, chrome or instapaper')
21 ->addOption('maxIterations', '', InputOption::VALUE_OPTIONAL, 'Number of iterations before stoping', false) 21 ->addOption('maxIterations', '', InputOption::VALUE_OPTIONAL, 'Number of iterations before stoping', false)
22 ; 22 ;
23 } 23 }
diff --git a/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php b/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php
index b893ea29..fc175f67 100644
--- a/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php
+++ b/src/Wallabag/ImportBundle/Consumer/AbstractConsumer.php
@@ -9,19 +9,23 @@ use Wallabag\CoreBundle\Entity\Entry;
9use Wallabag\CoreBundle\Entity\Tag; 9use Wallabag\CoreBundle\Entity\Tag;
10use Psr\Log\LoggerInterface; 10use Psr\Log\LoggerInterface;
11use Psr\Log\NullLogger; 11use Psr\Log\NullLogger;
12use Symfony\Component\EventDispatcher\EventDispatcherInterface;
13use Wallabag\CoreBundle\Event\EntrySavedEvent;
12 14
13abstract class AbstractConsumer 15abstract class AbstractConsumer
14{ 16{
15 protected $em; 17 protected $em;
16 protected $userRepository; 18 protected $userRepository;
17 protected $import; 19 protected $import;
20 protected $eventDispatcher;
18 protected $logger; 21 protected $logger;
19 22
20 public function __construct(EntityManager $em, UserRepository $userRepository, AbstractImport $import, LoggerInterface $logger = null) 23 public function __construct(EntityManager $em, UserRepository $userRepository, AbstractImport $import, EventDispatcherInterface $eventDispatcher, LoggerInterface $logger = null)
21 { 24 {
22 $this->em = $em; 25 $this->em = $em;
23 $this->userRepository = $userRepository; 26 $this->userRepository = $userRepository;
24 $this->import = $import; 27 $this->import = $import;
28 $this->eventDispatcher = $eventDispatcher;
25 $this->logger = $logger ?: new NullLogger(); 29 $this->logger = $logger ?: new NullLogger();
26 } 30 }
27 31
@@ -59,6 +63,9 @@ abstract class AbstractConsumer
59 try { 63 try {
60 $this->em->flush(); 64 $this->em->flush();
61 65
66 // entry saved, dispatch event about it!
67 $this->eventDispatcher->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
68
62 // clear only affected entities 69 // clear only affected entities
63 $this->em->clear(Entry::class); 70 $this->em->clear(Entry::class);
64 $this->em->clear(Tag::class); 71 $this->em->clear(Tag::class);
diff --git a/src/Wallabag/ImportBundle/Controller/ImportController.php b/src/Wallabag/ImportBundle/Controller/ImportController.php
index 15de75ff..237c748e 100644
--- a/src/Wallabag/ImportBundle/Controller/ImportController.php
+++ b/src/Wallabag/ImportBundle/Controller/ImportController.php
@@ -42,6 +42,7 @@ class ImportController extends Controller
42 + $this->getTotalMessageInRabbitQueue('firefox') 42 + $this->getTotalMessageInRabbitQueue('firefox')
43 + $this->getTotalMessageInRabbitQueue('chrome') 43 + $this->getTotalMessageInRabbitQueue('chrome')
44 + $this->getTotalMessageInRabbitQueue('instapaper') 44 + $this->getTotalMessageInRabbitQueue('instapaper')
45 + $this->getTotalMessageInRabbitQueue('pinboard')
45 ; 46 ;
46 } catch (\Exception $e) { 47 } catch (\Exception $e) {
47 $rabbitNotInstalled = true; 48 $rabbitNotInstalled = true;
@@ -57,6 +58,7 @@ class ImportController extends Controller
57 + $redis->llen('wallabag.import.firefox') 58 + $redis->llen('wallabag.import.firefox')
58 + $redis->llen('wallabag.import.chrome') 59 + $redis->llen('wallabag.import.chrome')
59 + $redis->llen('wallabag.import.instapaper') 60 + $redis->llen('wallabag.import.instapaper')
61 + $redis->llen('wallabag.import.pinboard')
60 ; 62 ;
61 } catch (\Exception $e) { 63 } catch (\Exception $e) {
62 $redisNotInstalled = true; 64 $redisNotInstalled = true;
diff --git a/src/Wallabag/ImportBundle/Controller/PinboardController.php b/src/Wallabag/ImportBundle/Controller/PinboardController.php
new file mode 100644
index 00000000..9c3f98d6
--- /dev/null
+++ b/src/Wallabag/ImportBundle/Controller/PinboardController.php
@@ -0,0 +1,77 @@
1<?php
2
3namespace Wallabag\ImportBundle\Controller;
4
5use Symfony\Bundle\FrameworkBundle\Controller\Controller;
6use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
7use Symfony\Component\HttpFoundation\Request;
8use Wallabag\ImportBundle\Form\Type\UploadImportType;
9
10class PinboardController extends Controller
11{
12 /**
13 * @Route("/pinboard", name="import_pinboard")
14 */
15 public function indexAction(Request $request)
16 {
17 $form = $this->createForm(UploadImportType::class);
18 $form->handleRequest($request);
19
20 $pinboard = $this->get('wallabag_import.pinboard.import');
21 $pinboard->setUser($this->getUser());
22
23 if ($this->get('craue_config')->get('import_with_rabbitmq')) {
24 $pinboard->setProducer($this->get('old_sound_rabbit_mq.import_pinboard_producer'));
25 } elseif ($this->get('craue_config')->get('import_with_redis')) {
26 $pinboard->setProducer($this->get('wallabag_import.producer.redis.pinboard'));
27 }
28
29 if ($form->isValid()) {
30 $file = $form->get('file')->getData();
31 $markAsRead = $form->get('mark_as_read')->getData();
32 $name = 'pinboard_'.$this->getUser()->getId().'.json';
33
34 if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes')) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
35 $res = $pinboard
36 ->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name)
37 ->setMarkAsRead($markAsRead)
38 ->import();
39
40 $message = 'flashes.import.notice.failed';
41
42 if (true === $res) {
43 $summary = $pinboard->getSummary();
44 $message = $this->get('translator')->trans('flashes.import.notice.summary', [
45 '%imported%' => $summary['imported'],
46 '%skipped%' => $summary['skipped'],
47 ]);
48
49 if (0 < $summary['queued']) {
50 $message = $this->get('translator')->trans('flashes.import.notice.summary_with_queue', [
51 '%queued%' => $summary['queued'],
52 ]);
53 }
54
55 unlink($this->getParameter('wallabag_import.resource_dir').'/'.$name);
56 }
57
58 $this->get('session')->getFlashBag()->add(
59 'notice',
60 $message
61 );
62
63 return $this->redirect($this->generateUrl('homepage'));
64 } else {
65 $this->get('session')->getFlashBag()->add(
66 'notice',
67 'flashes.import.notice.failed_on_file'
68 );
69 }
70 }
71
72 return $this->render('WallabagImportBundle:Pinboard:index.html.twig', [
73 'form' => $form->createView(),
74 'import' => $pinboard,
75 ]);
76 }
77}
diff --git a/src/Wallabag/ImportBundle/Import/AbstractImport.php b/src/Wallabag/ImportBundle/Import/AbstractImport.php
index 764b390a..1d4a6e27 100644
--- a/src/Wallabag/ImportBundle/Import/AbstractImport.php
+++ b/src/Wallabag/ImportBundle/Import/AbstractImport.php
@@ -10,12 +10,15 @@ use Wallabag\CoreBundle\Entity\Entry;
10use Wallabag\CoreBundle\Entity\Tag; 10use Wallabag\CoreBundle\Entity\Tag;
11use Wallabag\UserBundle\Entity\User; 11use Wallabag\UserBundle\Entity\User;
12use OldSound\RabbitMqBundle\RabbitMq\ProducerInterface; 12use OldSound\RabbitMqBundle\RabbitMq\ProducerInterface;
13use Symfony\Component\EventDispatcher\EventDispatcherInterface;
14use Wallabag\CoreBundle\Event\EntrySavedEvent;
13 15
14abstract class AbstractImport implements ImportInterface 16abstract class AbstractImport implements ImportInterface
15{ 17{
16 protected $em; 18 protected $em;
17 protected $logger; 19 protected $logger;
18 protected $contentProxy; 20 protected $contentProxy;
21 protected $eventDispatcher;
19 protected $producer; 22 protected $producer;
20 protected $user; 23 protected $user;
21 protected $markAsRead; 24 protected $markAsRead;
@@ -23,11 +26,12 @@ abstract class AbstractImport implements ImportInterface
23 protected $importedEntries = 0; 26 protected $importedEntries = 0;
24 protected $queuedEntries = 0; 27 protected $queuedEntries = 0;
25 28
26 public function __construct(EntityManager $em, ContentProxy $contentProxy) 29 public function __construct(EntityManager $em, ContentProxy $contentProxy, EventDispatcherInterface $eventDispatcher)
27 { 30 {
28 $this->em = $em; 31 $this->em = $em;
29 $this->logger = new NullLogger(); 32 $this->logger = new NullLogger();
30 $this->contentProxy = $contentProxy; 33 $this->contentProxy = $contentProxy;
34 $this->eventDispatcher = $eventDispatcher;
31 } 35 }
32 36
33 public function setLogger(LoggerInterface $logger) 37 public function setLogger(LoggerInterface $logger)
@@ -104,6 +108,7 @@ abstract class AbstractImport implements ImportInterface
104 protected function parseEntries($entries) 108 protected function parseEntries($entries)
105 { 109 {
106 $i = 1; 110 $i = 1;
111 $entryToBeFlushed = [];
107 112
108 foreach ($entries as $importedEntry) { 113 foreach ($entries as $importedEntry) {
109 if ($this->markAsRead) { 114 if ($this->markAsRead) {
@@ -116,10 +121,21 @@ abstract class AbstractImport implements ImportInterface
116 continue; 121 continue;
117 } 122 }
118 123
124 // store each entry to be flushed so we can trigger the entry.saved event for each of them
125 // entry.saved needs the entry to be persisted in db because it needs it id to generate
126 // images (at least)
127 $entryToBeFlushed[] = $entry;
128
119 // flush every 20 entries 129 // flush every 20 entries
120 if (($i % 20) === 0) { 130 if (($i % 20) === 0) {
121 $this->em->flush(); 131 $this->em->flush();
122 132
133 foreach ($entryToBeFlushed as $entry) {
134 $this->eventDispatcher->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
135 }
136
137 $entryToBeFlushed = [];
138
123 // clear only affected entities 139 // clear only affected entities
124 $this->em->clear(Entry::class); 140 $this->em->clear(Entry::class);
125 $this->em->clear(Tag::class); 141 $this->em->clear(Tag::class);
@@ -128,6 +144,12 @@ abstract class AbstractImport implements ImportInterface
128 } 144 }
129 145
130 $this->em->flush(); 146 $this->em->flush();
147
148 if (!empty($entryToBeFlushed)) {
149 foreach ($entryToBeFlushed as $entry) {
150 $this->eventDispatcher->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
151 }
152 }
131 } 153 }
132 154
133 /** 155 /**
diff --git a/src/Wallabag/ImportBundle/Import/BrowserImport.php b/src/Wallabag/ImportBundle/Import/BrowserImport.php
index 2ca1683b..8bf7d92e 100644
--- a/src/Wallabag/ImportBundle/Import/BrowserImport.php
+++ b/src/Wallabag/ImportBundle/Import/BrowserImport.php
@@ -5,6 +5,7 @@ namespace Wallabag\ImportBundle\Import;
5use Wallabag\CoreBundle\Entity\Entry; 5use Wallabag\CoreBundle\Entity\Entry;
6use Wallabag\UserBundle\Entity\User; 6use Wallabag\UserBundle\Entity\User;
7use Wallabag\CoreBundle\Helper\ContentProxy; 7use Wallabag\CoreBundle\Helper\ContentProxy;
8use Wallabag\CoreBundle\Event\EntrySavedEvent;
8 9
9abstract class BrowserImport extends AbstractImport 10abstract class BrowserImport extends AbstractImport
10{ 11{
@@ -81,6 +82,7 @@ abstract class BrowserImport extends AbstractImport
81 protected function parseEntries($entries) 82 protected function parseEntries($entries)
82 { 83 {
83 $i = 1; 84 $i = 1;
85 $entryToBeFlushed = [];
84 86
85 foreach ($entries as $importedEntry) { 87 foreach ($entries as $importedEntry) {
86 if ((array) $importedEntry !== $importedEntry) { 88 if ((array) $importedEntry !== $importedEntry) {
@@ -93,14 +95,29 @@ abstract class BrowserImport extends AbstractImport
93 continue; 95 continue;
94 } 96 }
95 97
98 // @see AbstractImport
99 $entryToBeFlushed[] = $entry;
100
96 // flush every 20 entries 101 // flush every 20 entries
97 if (($i % 20) === 0) { 102 if (($i % 20) === 0) {
98 $this->em->flush(); 103 $this->em->flush();
104
105 foreach ($entryToBeFlushed as $entry) {
106 $this->eventDispatcher->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
107 }
108
109 $entryToBeFlushed = [];
99 } 110 }
100 ++$i; 111 ++$i;
101 } 112 }
102 113
103 $this->em->flush(); 114 $this->em->flush();
115
116 if (!empty($entryToBeFlushed)) {
117 foreach ($entryToBeFlushed as $entry) {
118 $this->eventDispatcher->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
119 }
120 }
104 } 121 }
105 122
106 /** 123 /**
diff --git a/src/Wallabag/ImportBundle/Import/PinboardImport.php b/src/Wallabag/ImportBundle/Import/PinboardImport.php
new file mode 100644
index 00000000..9bcfbc36
--- /dev/null
+++ b/src/Wallabag/ImportBundle/Import/PinboardImport.php
@@ -0,0 +1,143 @@
1<?php
2
3namespace Wallabag\ImportBundle\Import;
4
5use Wallabag\CoreBundle\Entity\Entry;
6
7class PinboardImport extends AbstractImport
8{
9 private $filepath;
10
11 /**
12 * {@inheritdoc}
13 */
14 public function getName()
15 {
16 return 'Pinboard';
17 }
18
19 /**
20 * {@inheritdoc}
21 */
22 public function getUrl()
23 {
24 return 'import_pinboard';
25 }
26
27 /**
28 * {@inheritdoc}
29 */
30 public function getDescription()
31 {
32 return 'import.pinboard.description';
33 }
34
35 /**
36 * Set file path to the json file.
37 *
38 * @param string $filepath
39 */
40 public function setFilepath($filepath)
41 {
42 $this->filepath = $filepath;
43
44 return $this;
45 }
46
47 /**
48 * {@inheritdoc}
49 */
50 public function import()
51 {
52 if (!$this->user) {
53 $this->logger->error('PinboardImport: user is not defined');
54
55 return false;
56 }
57
58 if (!file_exists($this->filepath) || !is_readable($this->filepath)) {
59 $this->logger->error('PinboardImport: unable to read file', ['filepath' => $this->filepath]);
60
61 return false;
62 }
63
64 $data = json_decode(file_get_contents($this->filepath), true);
65
66 if (empty($data)) {
67 $this->logger->error('PinboardImport: no entries in imported file');
68
69 return false;
70 }
71
72 if ($this->producer) {
73 $this->parseEntriesForProducer($data);
74
75 return true;
76 }
77
78 $this->parseEntries($data);
79
80 return true;
81 }
82
83 /**
84 * {@inheritdoc}
85 */
86 public function parseEntry(array $importedEntry)
87 {
88 $existingEntry = $this->em
89 ->getRepository('WallabagCoreBundle:Entry')
90 ->findByUrlAndUserId($importedEntry['href'], $this->user->getId());
91
92 if (false !== $existingEntry) {
93 ++$this->skippedEntries;
94
95 return;
96 }
97
98 $data = [
99 'title' => $importedEntry['description'],
100 'url' => $importedEntry['href'],
101 'content_type' => '',
102 'language' => '',
103 'is_archived' => ('no' === $importedEntry['toread']) || $this->markAsRead,
104 'is_starred' => false,
105 'created_at' => $importedEntry['time'],
106 'tags' => explode(' ', $importedEntry['tags']),
107 ];
108
109 $entry = new Entry($this->user);
110 $entry->setUrl($data['url']);
111 $entry->setTitle($data['title']);
112
113 // update entry with content (in case fetching failed, the given entry will be return)
114 $entry = $this->fetchContent($entry, $data['url'], $data);
115
116 if (!empty($data['tags'])) {
117 $this->contentProxy->assignTagsToEntry(
118 $entry,
119 $data['tags'],
120 $this->em->getUnitOfWork()->getScheduledEntityInsertions()
121 );
122 }
123
124 $entry->setArchived($data['is_archived']);
125 $entry->setStarred($data['is_starred']);
126 $entry->setCreatedAt(new \DateTime($data['created_at']));
127
128 $this->em->persist($entry);
129 ++$this->importedEntries;
130
131 return $entry;
132 }
133
134 /**
135 * {@inheritdoc}
136 */
137 protected function setEntryAsRead(array $importedEntry)
138 {
139 $importedEntry['toread'] = 'no';
140
141 return $importedEntry;
142 }
143}
diff --git a/src/Wallabag/ImportBundle/Import/PocketImport.php b/src/Wallabag/ImportBundle/Import/PocketImport.php
index 327e2500..33093480 100644
--- a/src/Wallabag/ImportBundle/Import/PocketImport.php
+++ b/src/Wallabag/ImportBundle/Import/PocketImport.php
@@ -2,8 +2,6 @@
2 2
3namespace Wallabag\ImportBundle\Import; 3namespace Wallabag\ImportBundle\Import;
4 4
5use Psr\Log\NullLogger;
6use Doctrine\ORM\EntityManager;
7use GuzzleHttp\Client; 5use GuzzleHttp\Client;
8use GuzzleHttp\Exception\RequestException; 6use GuzzleHttp\Exception\RequestException;
9use Wallabag\CoreBundle\Entity\Entry; 7use Wallabag\CoreBundle\Entity\Entry;
@@ -16,13 +14,6 @@ class PocketImport extends AbstractImport
16 14
17 const NB_ELEMENTS = 5000; 15 const NB_ELEMENTS = 5000;
18 16
19 public function __construct(EntityManager $em, ContentProxy $contentProxy)
20 {
21 $this->em = $em;
22 $this->contentProxy = $contentProxy;
23 $this->logger = new NullLogger();
24 }
25
26 /** 17 /**
27 * Only used for test purpose. 18 * Only used for test purpose.
28 * 19 *
diff --git a/src/Wallabag/ImportBundle/Resources/config/rabbit.yml b/src/Wallabag/ImportBundle/Resources/config/rabbit.yml
index 70b8a0d4..e9ecb846 100644
--- a/src/Wallabag/ImportBundle/Resources/config/rabbit.yml
+++ b/src/Wallabag/ImportBundle/Resources/config/rabbit.yml
@@ -6,6 +6,7 @@ services:
6 - "@doctrine.orm.entity_manager" 6 - "@doctrine.orm.entity_manager"
7 - "@wallabag_user.user_repository" 7 - "@wallabag_user.user_repository"
8 - "@wallabag_import.pocket.import" 8 - "@wallabag_import.pocket.import"
9 - "@event_dispatcher"
9 - "@logger" 10 - "@logger"
10 wallabag_import.consumer.amqp.readability: 11 wallabag_import.consumer.amqp.readability:
11 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer 12 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
@@ -13,6 +14,7 @@ services:
13 - "@doctrine.orm.entity_manager" 14 - "@doctrine.orm.entity_manager"
14 - "@wallabag_user.user_repository" 15 - "@wallabag_user.user_repository"
15 - "@wallabag_import.readability.import" 16 - "@wallabag_import.readability.import"
17 - "@event_dispatcher"
16 - "@logger" 18 - "@logger"
17 wallabag_import.consumer.amqp.instapaper: 19 wallabag_import.consumer.amqp.instapaper:
18 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer 20 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
@@ -20,6 +22,15 @@ services:
20 - "@doctrine.orm.entity_manager" 22 - "@doctrine.orm.entity_manager"
21 - "@wallabag_user.user_repository" 23 - "@wallabag_user.user_repository"
22 - "@wallabag_import.instapaper.import" 24 - "@wallabag_import.instapaper.import"
25 - "@event_dispatcher"
26 - "@logger"
27 wallabag_import.consumer.amqp.pinboard:
28 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
29 arguments:
30 - "@doctrine.orm.entity_manager"
31 - "@wallabag_user.user_repository"
32 - "@wallabag_import.pinboard.import"
33 - "@event_dispatcher"
23 - "@logger" 34 - "@logger"
24 wallabag_import.consumer.amqp.wallabag_v1: 35 wallabag_import.consumer.amqp.wallabag_v1:
25 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer 36 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
@@ -27,6 +38,7 @@ services:
27 - "@doctrine.orm.entity_manager" 38 - "@doctrine.orm.entity_manager"
28 - "@wallabag_user.user_repository" 39 - "@wallabag_user.user_repository"
29 - "@wallabag_import.wallabag_v1.import" 40 - "@wallabag_import.wallabag_v1.import"
41 - "@event_dispatcher"
30 - "@logger" 42 - "@logger"
31 wallabag_import.consumer.amqp.wallabag_v2: 43 wallabag_import.consumer.amqp.wallabag_v2:
32 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer 44 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
@@ -34,6 +46,7 @@ services:
34 - "@doctrine.orm.entity_manager" 46 - "@doctrine.orm.entity_manager"
35 - "@wallabag_user.user_repository" 47 - "@wallabag_user.user_repository"
36 - "@wallabag_import.wallabag_v2.import" 48 - "@wallabag_import.wallabag_v2.import"
49 - "@event_dispatcher"
37 - "@logger" 50 - "@logger"
38 wallabag_import.consumer.amqp.firefox: 51 wallabag_import.consumer.amqp.firefox:
39 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer 52 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
@@ -41,6 +54,7 @@ services:
41 - "@doctrine.orm.entity_manager" 54 - "@doctrine.orm.entity_manager"
42 - "@wallabag_user.user_repository" 55 - "@wallabag_user.user_repository"
43 - "@wallabag_import.firefox.import" 56 - "@wallabag_import.firefox.import"
57 - "@event_dispatcher"
44 - "@logger" 58 - "@logger"
45 wallabag_import.consumer.amqp.chrome: 59 wallabag_import.consumer.amqp.chrome:
46 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer 60 class: Wallabag\ImportBundle\Consumer\AMQPEntryConsumer
@@ -48,4 +62,5 @@ services:
48 - "@doctrine.orm.entity_manager" 62 - "@doctrine.orm.entity_manager"
49 - "@wallabag_user.user_repository" 63 - "@wallabag_user.user_repository"
50 - "@wallabag_import.chrome.import" 64 - "@wallabag_import.chrome.import"
65 - "@event_dispatcher"
51 - "@logger" 66 - "@logger"
diff --git a/src/Wallabag/ImportBundle/Resources/config/redis.yml b/src/Wallabag/ImportBundle/Resources/config/redis.yml
index 0a81e1b5..091cdba0 100644
--- a/src/Wallabag/ImportBundle/Resources/config/redis.yml
+++ b/src/Wallabag/ImportBundle/Resources/config/redis.yml
@@ -18,6 +18,7 @@ services:
18 - "@doctrine.orm.entity_manager" 18 - "@doctrine.orm.entity_manager"
19 - "@wallabag_user.user_repository" 19 - "@wallabag_user.user_repository"
20 - "@wallabag_import.readability.import" 20 - "@wallabag_import.readability.import"
21 - "@event_dispatcher"
21 - "@logger" 22 - "@logger"
22 23
23 # instapaper 24 # instapaper
@@ -38,6 +39,28 @@ services:
38 - "@doctrine.orm.entity_manager" 39 - "@doctrine.orm.entity_manager"
39 - "@wallabag_user.user_repository" 40 - "@wallabag_user.user_repository"
40 - "@wallabag_import.instapaper.import" 41 - "@wallabag_import.instapaper.import"
42 - "@event_dispatcher"
43 - "@logger"
44
45 # pinboard
46 wallabag_import.queue.redis.pinboard:
47 class: Simpleue\Queue\RedisQueue
48 arguments:
49 - "@wallabag_core.redis.client"
50 - "wallabag.import.pinboard"
51
52 wallabag_import.producer.redis.pinboard:
53 class: Wallabag\ImportBundle\Redis\Producer
54 arguments:
55 - "@wallabag_import.queue.redis.pinboard"
56
57 wallabag_import.consumer.redis.pinboard:
58 class: Wallabag\ImportBundle\Consumer\RedisEntryConsumer
59 arguments:
60 - "@doctrine.orm.entity_manager"
61 - "@wallabag_user.user_repository"
62 - "@wallabag_import.pinboard.import"
63 - "@event_dispatcher"
41 - "@logger" 64 - "@logger"
42 65
43 # pocket 66 # pocket
@@ -58,6 +81,7 @@ services:
58 - "@doctrine.orm.entity_manager" 81 - "@doctrine.orm.entity_manager"
59 - "@wallabag_user.user_repository" 82 - "@wallabag_user.user_repository"
60 - "@wallabag_import.pocket.import" 83 - "@wallabag_import.pocket.import"
84 - "@event_dispatcher"
61 - "@logger" 85 - "@logger"
62 86
63 # wallabag v1 87 # wallabag v1
@@ -78,6 +102,7 @@ services:
78 - "@doctrine.orm.entity_manager" 102 - "@doctrine.orm.entity_manager"
79 - "@wallabag_user.user_repository" 103 - "@wallabag_user.user_repository"
80 - "@wallabag_import.wallabag_v1.import" 104 - "@wallabag_import.wallabag_v1.import"
105 - "@event_dispatcher"
81 - "@logger" 106 - "@logger"
82 107
83 # wallabag v2 108 # wallabag v2
@@ -98,6 +123,7 @@ services:
98 - "@doctrine.orm.entity_manager" 123 - "@doctrine.orm.entity_manager"
99 - "@wallabag_user.user_repository" 124 - "@wallabag_user.user_repository"
100 - "@wallabag_import.wallabag_v2.import" 125 - "@wallabag_import.wallabag_v2.import"
126 - "@event_dispatcher"
101 - "@logger" 127 - "@logger"
102 128
103 # firefox 129 # firefox
@@ -118,6 +144,7 @@ services:
118 - "@doctrine.orm.entity_manager" 144 - "@doctrine.orm.entity_manager"
119 - "@wallabag_user.user_repository" 145 - "@wallabag_user.user_repository"
120 - "@wallabag_import.firefox.import" 146 - "@wallabag_import.firefox.import"
147 - "@event_dispatcher"
121 - "@logger" 148 - "@logger"
122 149
123 # chrome 150 # chrome
@@ -138,4 +165,5 @@ services:
138 - "@doctrine.orm.entity_manager" 165 - "@doctrine.orm.entity_manager"
139 - "@wallabag_user.user_repository" 166 - "@wallabag_user.user_repository"
140 - "@wallabag_import.chrome.import" 167 - "@wallabag_import.chrome.import"
168 - "@event_dispatcher"
141 - "@logger" 169 - "@logger"
diff --git a/src/Wallabag/ImportBundle/Resources/config/services.yml b/src/Wallabag/ImportBundle/Resources/config/services.yml
index d600be0f..c4fe3f92 100644
--- a/src/Wallabag/ImportBundle/Resources/config/services.yml
+++ b/src/Wallabag/ImportBundle/Resources/config/services.yml
@@ -20,6 +20,7 @@ services:
20 arguments: 20 arguments:
21 - "@doctrine.orm.entity_manager" 21 - "@doctrine.orm.entity_manager"
22 - "@wallabag_core.content_proxy" 22 - "@wallabag_core.content_proxy"
23 - "@event_dispatcher"
23 calls: 24 calls:
24 - [ setClient, [ "@wallabag_import.pocket.client" ] ] 25 - [ setClient, [ "@wallabag_import.pocket.client" ] ]
25 - [ setLogger, [ "@logger" ]] 26 - [ setLogger, [ "@logger" ]]
@@ -31,6 +32,7 @@ services:
31 arguments: 32 arguments:
32 - "@doctrine.orm.entity_manager" 33 - "@doctrine.orm.entity_manager"
33 - "@wallabag_core.content_proxy" 34 - "@wallabag_core.content_proxy"
35 - "@event_dispatcher"
34 calls: 36 calls:
35 - [ setLogger, [ "@logger" ]] 37 - [ setLogger, [ "@logger" ]]
36 tags: 38 tags:
@@ -41,6 +43,7 @@ services:
41 arguments: 43 arguments:
42 - "@doctrine.orm.entity_manager" 44 - "@doctrine.orm.entity_manager"
43 - "@wallabag_core.content_proxy" 45 - "@wallabag_core.content_proxy"
46 - "@event_dispatcher"
44 calls: 47 calls:
45 - [ setLogger, [ "@logger" ]] 48 - [ setLogger, [ "@logger" ]]
46 tags: 49 tags:
@@ -51,6 +54,7 @@ services:
51 arguments: 54 arguments:
52 - "@doctrine.orm.entity_manager" 55 - "@doctrine.orm.entity_manager"
53 - "@wallabag_core.content_proxy" 56 - "@wallabag_core.content_proxy"
57 - "@event_dispatcher"
54 calls: 58 calls:
55 - [ setLogger, [ "@logger" ]] 59 - [ setLogger, [ "@logger" ]]
56 tags: 60 tags:
@@ -61,16 +65,29 @@ services:
61 arguments: 65 arguments:
62 - "@doctrine.orm.entity_manager" 66 - "@doctrine.orm.entity_manager"
63 - "@wallabag_core.content_proxy" 67 - "@wallabag_core.content_proxy"
68 - "@event_dispatcher"
64 calls: 69 calls:
65 - [ setLogger, [ "@logger" ]] 70 - [ setLogger, [ "@logger" ]]
66 tags: 71 tags:
67 - { name: wallabag_import.import, alias: instapaper } 72 - { name: wallabag_import.import, alias: instapaper }
68 73
74 wallabag_import.pinboard.import:
75 class: Wallabag\ImportBundle\Import\PinboardImport
76 arguments:
77 - "@doctrine.orm.entity_manager"
78 - "@wallabag_core.content_proxy"
79 - "@event_dispatcher"
80 calls:
81 - [ setLogger, [ "@logger" ]]
82 tags:
83 - { name: wallabag_import.import, alias: pinboard }
84
69 wallabag_import.firefox.import: 85 wallabag_import.firefox.import:
70 class: Wallabag\ImportBundle\Import\FirefoxImport 86 class: Wallabag\ImportBundle\Import\FirefoxImport
71 arguments: 87 arguments:
72 - "@doctrine.orm.entity_manager" 88 - "@doctrine.orm.entity_manager"
73 - "@wallabag_core.content_proxy" 89 - "@wallabag_core.content_proxy"
90 - "@event_dispatcher"
74 calls: 91 calls:
75 - [ setLogger, [ "@logger" ]] 92 - [ setLogger, [ "@logger" ]]
76 tags: 93 tags:
@@ -80,6 +97,7 @@ services:
80 arguments: 97 arguments:
81 - "@doctrine.orm.entity_manager" 98 - "@doctrine.orm.entity_manager"
82 - "@wallabag_core.content_proxy" 99 - "@wallabag_core.content_proxy"
100 - "@event_dispatcher"
83 calls: 101 calls:
84 - [ setLogger, [ "@logger" ]] 102 - [ setLogger, [ "@logger" ]]
85 tags: 103 tags:
diff --git a/src/Wallabag/ImportBundle/Resources/views/Chrome/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Chrome/index.html.twig
index ead828c6..93b08540 100644
--- a/src/Wallabag/ImportBundle/Resources/views/Chrome/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/Chrome/index.html.twig
@@ -6,6 +6,8 @@
6<div class="row"> 6<div class="row">
7 <div class="col s12"> 7 <div class="col s12">
8 <div class="card-panel settings"> 8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_information.html.twig' %}
10
9 <div class="row"> 11 <div class="row">
10 <blockquote>{{ import.description|trans|raw }}</blockquote> 12 <blockquote>{{ import.description|trans|raw }}</blockquote>
11 <p>{{ 'import.chrome.how_to'|trans }}</p> 13 <p>{{ 'import.chrome.how_to'|trans }}</p>
diff --git a/src/Wallabag/ImportBundle/Resources/views/Firefox/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Firefox/index.html.twig
index f975da3f..ced3f008 100644
--- a/src/Wallabag/ImportBundle/Resources/views/Firefox/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/Firefox/index.html.twig
@@ -6,6 +6,8 @@
6<div class="row"> 6<div class="row">
7 <div class="col s12"> 7 <div class="col s12">
8 <div class="card-panel settings"> 8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_information.html.twig' %}
10
9 <div class="row"> 11 <div class="row">
10 <blockquote>{{ import.description|trans|raw }}</blockquote> 12 <blockquote>{{ import.description|trans|raw }}</blockquote>
11 <p>{{ 'import.firefox.how_to'|trans }}</p> 13 <p>{{ 'import.firefox.how_to'|trans }}</p>
diff --git a/src/Wallabag/ImportBundle/Resources/views/Import/_workerEnabled.html.twig b/src/Wallabag/ImportBundle/Resources/views/Import/_information.html.twig
index 2390a41f..48bbcfe7 100644
--- a/src/Wallabag/ImportBundle/Resources/views/Import/_workerEnabled.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/Import/_information.html.twig
@@ -1,8 +1,15 @@
1{% set redis = craue_setting('import_with_redis') %} 1{% set redis = craue_setting('import_with_redis') %}
2{% set rabbit = craue_setting('import_with_rabbitmq') %} 2{% set rabbit = craue_setting('import_with_rabbitmq') %}
3{% set downloadImages = craue_setting('download_images_enabled') %}
3 4
4{% if redis or rabbit %} 5{% if redis or rabbit %}
5 <div class="card-panel yellow darken-1 black-text"> 6 <div class="card-panel yellow darken-1 black-text">
6 {{ 'import.worker.enabled'|trans }} <strong>{% if rabbit %}RabbitMQ{% elseif redis %}Redis{% endif %}</strong> 7 {{ 'import.worker.enabled'|trans }} <strong>{% if rabbit %}RabbitMQ{% elseif redis %}Redis{% endif %}</strong>
7 </div> 8 </div>
8{% endif %} 9{% endif %}
10
11{% if not redis and not rabbit and downloadImages %}
12 <div class="card-panel orange darken-1 black-text">
13 {{ 'import.worker.download_images_warning'|trans|raw }}
14 </div>
15{% endif %}
diff --git a/src/Wallabag/ImportBundle/Resources/views/Import/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Import/index.html.twig
index 6ea5e0f4..b1ec40a6 100644
--- a/src/Wallabag/ImportBundle/Resources/views/Import/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/Import/index.html.twig
@@ -6,6 +6,8 @@
6<div class="row"> 6<div class="row">
7 <div class="col s12"> 7 <div class="col s12">
8 <div class="card-panel settings"> 8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_information.html.twig' %}
10
9 {{ 'import.page_description'|trans }} 11 {{ 'import.page_description'|trans }}
10 <ul> 12 <ul>
11 {% for import in imports %} 13 {% for import in imports %}
diff --git a/src/Wallabag/ImportBundle/Resources/views/Instapaper/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Instapaper/index.html.twig
index 5789361f..28165d19 100644
--- a/src/Wallabag/ImportBundle/Resources/views/Instapaper/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/Instapaper/index.html.twig
@@ -6,7 +6,7 @@
6<div class="row"> 6<div class="row">
7 <div class="col s12"> 7 <div class="col s12">
8 <div class="card-panel settings"> 8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_workerEnabled.html.twig' %} 9 {% include 'WallabagImportBundle:Import:_information.html.twig' %}
10 10
11 <div class="row"> 11 <div class="row">
12 <blockquote>{{ import.description|trans }}</blockquote> 12 <blockquote>{{ import.description|trans }}</blockquote>
diff --git a/src/Wallabag/ImportBundle/Resources/views/Pinboard/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Pinboard/index.html.twig
new file mode 100644
index 00000000..43f196ad
--- /dev/null
+++ b/src/Wallabag/ImportBundle/Resources/views/Pinboard/index.html.twig
@@ -0,0 +1,45 @@
1{% extends "WallabagCoreBundle::layout.html.twig" %}
2
3{% block title %}{{ 'import.pinboard.page_title'|trans }}{% endblock %}
4
5{% block content %}
6<div class="row">
7 <div class="col s12">
8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_information.html.twig' %}
10
11 <div class="row">
12 <blockquote>{{ import.description|trans }}</blockquote>
13 <p>{{ 'import.pinboard.how_to'|trans }}</p>
14
15 <div class="col s12">
16 {{ form_start(form, {'method': 'POST'}) }}
17 {{ form_errors(form) }}
18 <div class="row">
19 <div class="file-field input-field col s12">
20 {{ form_errors(form.file) }}
21 <div class="btn">
22 <span>{{ form.file.vars.label|trans }}</span>
23 {{ form_widget(form.file) }}
24 </div>
25 <div class="file-path-wrapper">
26 <input class="file-path validate" type="text">
27 </div>
28 </div>
29 <div class="input-field col s6 with-checkbox">
30 <h6>{{ 'import.form.mark_as_read_title'|trans }}</h6>
31 {{ form_widget(form.mark_as_read) }}
32 {{ form_label(form.mark_as_read) }}
33 </div>
34 </div>
35
36 {{ form_widget(form.save, { 'attr': {'class': 'btn waves-effect waves-light'} }) }}
37
38 {{ form_rest(form) }}
39 </form>
40 </div>
41 </div>
42 </div>
43 </div>
44</div>
45{% endblock %}
diff --git a/src/Wallabag/ImportBundle/Resources/views/Pocket/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Pocket/index.html.twig
index 6195fa07..536e3d1a 100644
--- a/src/Wallabag/ImportBundle/Resources/views/Pocket/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/Pocket/index.html.twig
@@ -6,7 +6,7 @@
6<div class="row"> 6<div class="row">
7 <div class="col s12"> 7 <div class="col s12">
8 <div class="card-panel settings"> 8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_workerEnabled.html.twig' %} 9 {% include 'WallabagImportBundle:Import:_information.html.twig' %}
10 10
11 {% if not has_consumer_key %} 11 {% if not has_consumer_key %}
12 <div class="card-panel red white-text"> 12 <div class="card-panel red white-text">
diff --git a/src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig
index 74653b0f..737b0adf 100644
--- a/src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig
@@ -6,7 +6,7 @@
6<div class="row"> 6<div class="row">
7 <div class="col s12"> 7 <div class="col s12">
8 <div class="card-panel settings"> 8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_workerEnabled.html.twig' %} 9 {% include 'WallabagImportBundle:Import:_information.html.twig' %}
10 10
11 <div class="row"> 11 <div class="row">
12 <blockquote>{{ import.description|trans }}</blockquote> 12 <blockquote>{{ import.description|trans }}</blockquote>
diff --git a/src/Wallabag/ImportBundle/Resources/views/WallabagV1/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/WallabagV1/index.html.twig
index 0b19bc34..974b2c73 100644
--- a/src/Wallabag/ImportBundle/Resources/views/WallabagV1/index.html.twig
+++ b/src/Wallabag/ImportBundle/Resources/views/WallabagV1/index.html.twig
@@ -6,7 +6,7 @@
6<div class="row"> 6<div class="row">
7 <div class="col s12"> 7 <div class="col s12">
8 <div class="card-panel settings"> 8 <div class="card-panel settings">
9 {% include 'WallabagImportBundle:Import:_workerEnabled.html.twig' %} 9 {% include 'WallabagImportBundle:Import:_information.html.twig' %}
10 10
11 <div class="row"> 11 <div class="row">
12 <blockquote>{{ import.description|trans }}</blockquote> 12 <blockquote>{{ import.description|trans }}</blockquote>
diff --git a/src/Wallabag/UserBundle/Entity/User.php b/src/Wallabag/UserBundle/Entity/User.php
index d98ae76a..3a167de7 100644
--- a/src/Wallabag/UserBundle/Entity/User.php
+++ b/src/Wallabag/UserBundle/Entity/User.php
@@ -11,6 +11,7 @@ use JMS\Serializer\Annotation\ExclusionPolicy;
11use JMS\Serializer\Annotation\Expose; 11use JMS\Serializer\Annotation\Expose;
12use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; 12use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
13use Symfony\Component\Security\Core\User\UserInterface; 13use Symfony\Component\Security\Core\User\UserInterface;
14use Wallabag\ApiBundle\Entity\Client;
14use Wallabag\CoreBundle\Entity\Config; 15use Wallabag\CoreBundle\Entity\Config;
15use Wallabag\CoreBundle\Entity\Entry; 16use Wallabag\CoreBundle\Entity\Entry;
16 17
@@ -84,6 +85,11 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf
84 */ 85 */
85 private $trusted; 86 private $trusted;
86 87
88 /**
89 * @ORM\OneToMany(targetEntity="Wallabag\ApiBundle\Entity\Client", mappedBy="user", cascade={"remove"})
90 */
91 protected $clients;
92
87 public function __construct() 93 public function __construct()
88 { 94 {
89 parent::__construct(); 95 parent::__construct();
@@ -240,4 +246,24 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf
240 246
241 return false; 247 return false;
242 } 248 }
249
250 /**
251 * @param Client $client
252 *
253 * @return User
254 */
255 public function addClient(Client $client)
256 {
257 $this->clients[] = $client;
258
259 return $this;
260 }
261
262 /**
263 * @return ArrayCollection<Entry>
264 */
265 public function getClients()
266 {
267 return $this->clients;
268 }
243} 269}
diff --git a/src/Wallabag/UserBundle/Repository/UserRepository.php b/src/Wallabag/UserBundle/Repository/UserRepository.php
index 009c4881..445edb3c 100644
--- a/src/Wallabag/UserBundle/Repository/UserRepository.php
+++ b/src/Wallabag/UserBundle/Repository/UserRepository.php
@@ -38,4 +38,18 @@ class UserRepository extends EntityRepository
38 ->getQuery() 38 ->getQuery()
39 ->getSingleResult(); 39 ->getSingleResult();
40 } 40 }
41
42 /**
43 * Count how many users are enabled.
44 *
45 * @return int
46 */
47 public function getSumEnabledUsers()
48 {
49 return $this->createQueryBuilder('u')
50 ->select('count(u)')
51 ->andWhere('u.expired = false')
52 ->getQuery()
53 ->getSingleScalarResult();
54 }
41} 55}
diff --git a/src/Wallabag/UserBundle/Resources/config/services.yml b/src/Wallabag/UserBundle/Resources/config/services.yml
index a8ee721b..164a25ec 100644
--- a/src/Wallabag/UserBundle/Resources/config/services.yml
+++ b/src/Wallabag/UserBundle/Resources/config/services.yml
@@ -22,7 +22,7 @@ services:
22 arguments: 22 arguments:
23 - WallabagUserBundle:User 23 - WallabagUserBundle:User
24 24
25 wallabag_user.create_config: 25 wallabag_user.listener.create_config:
26 class: Wallabag\UserBundle\EventListener\CreateConfigListener 26 class: Wallabag\UserBundle\EventListener\CreateConfigListener
27 arguments: 27 arguments:
28 - "@doctrine.orm.entity_manager" 28 - "@doctrine.orm.entity_manager"